mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-09 12:40:20 +08:00
238 lines
10 KiB
Markdown
238 lines
10 KiB
Markdown
# Codex Remote Collaboration VS Code Bridge
|
|
|
|
This extension connects local and Aether relay channels to one switchable Codex
|
|
control host. **Synchronous mode** follows the conversation currently shown by
|
|
the official Codex VS Code extension through its private IPC protocol and does
|
|
not spawn a `codex` process. **Asynchronous mode** starts an independent
|
|
app-server and lets the Web UI list, resume, create, and select conversations.
|
|
|
|
The attached conversation remains visible and usable in the official Codex
|
|
panel. Remote operators can observe its output, submit a new turn or steer the
|
|
active turn, interrupt it, and answer supported approval/input requests.
|
|
|
|
The mode can be changed from the Web UI without reconnecting either relay.
|
|
Synchronous mode makes the official panel the only conversation-navigation
|
|
owner; asynchronous mode restores the browser history and new-conversation
|
|
actions. A running turn or pending request blocks mode changes.
|
|
|
|
## Requirements
|
|
|
|
- The official `openai.chatgpt` VS Code extension is installed and signed in.
|
|
- The target Codex conversation is open and owned by that extension.
|
|
- The bridge and official extension run as the same OS user. The default Unix
|
|
socket is `$CODEX_HOME/ipc/ipc.sock`, normally `~/.codex/ipc/ipc.sock`.
|
|
- For a loopback `ws://` URL, the extension starts and owns its bundled relay
|
|
automatically. Remote and `wss://` relay URLs remain externally hosted.
|
|
|
|
The IPC follower protocol is private and versioned, not a public OpenAI API.
|
|
An official extension update can require a compatible bridge update. Strict
|
|
stream-version checks are enabled by default so an unknown protocol fails
|
|
closed instead of being interpreted optimistically.
|
|
|
|
## Build and install
|
|
|
|
```sh
|
|
npm install
|
|
npm run check
|
|
npm run build
|
|
npx --yes @vscode/vsce package
|
|
code --install-extension codex-remote-collab-0.4.0.vsix --force
|
|
```
|
|
|
|
Run **Developer: Reload Window** after installing or replacing the VSIX.
|
|
|
|
## Configure control modes
|
|
|
|
For the local default, no separate relay command is required. The extension
|
|
starts the bundled relay on the host and port from `codexRemoteCollab.localRelayUrl`.
|
|
To run the development relay manually, disable
|
|
`codexRemoteCollab.autoStartLocalRelay` and use:
|
|
|
|
```sh
|
|
HOST=127.0.0.1 PORT=8787 CODEX_REMOTE_MODE=host npm start
|
|
```
|
|
|
|
To opt into authentication later, set `CODEX_REMOTE_AUTH=required` and the three
|
|
token variables before starting the relay.
|
|
|
|
Set the extension configuration:
|
|
|
|
```json
|
|
{
|
|
"codexRemoteCollab.localRelayUrl": "ws://127.0.0.1:8787/v1/connect",
|
|
"codexRemoteCollab.controlMode": "sync",
|
|
"codexRemoteCollab.autoDiscoverThread": true,
|
|
"codexRemoteCollab.autoStart": true
|
|
}
|
|
```
|
|
|
|
Then:
|
|
|
|
1. Open the target conversation in the official Codex panel.
|
|
2. Reload VS Code once after installing the companion extension. The local relay and bridge start automatically; no token is needed for loopback. The status item opens the Web console and is not a connect/disconnect toggle.
|
|
3. Open the relay web console; it connects automatically on localhost. The web UI uses a
|
|
Codex-style conversation stream with a bottom composer; Enter sends and Shift+Enter
|
|
inserts a newline. There is no separate connect/disconnect step for the local relay.
|
|
|
|
If the browser says that it is waiting for the VS Code host or the recent-session list is
|
|
empty, verify that `codexRemoteCollab.localRelayUrl` uses the same port as the relay and run
|
|
**Developer: Reload Window**. Keep `codexRemoteCollab.threadId` empty unless a specific
|
|
conversation must be pinned; an old closed ID can prevent startup until it is cleared.
|
|
|
|
When authentication is enabled, run **Codex Remote: Set Relay Token** with the
|
|
host token. It is stored in `vscode.SecretStorage`, not in settings; the browser
|
|
uses the operator or viewer token separately.
|
|
|
|
With no configured thread ID, the bridge ranks recent VS Code rollout metadata
|
|
and shows only candidates verified by live IPC owner discovery and a matching
|
|
follower snapshot. Explicit Codex Desktop tasks, closed, stale, and other
|
|
non-attachable history entries are omitted.
|
|
In synchronous mode, switching the conversation in the official Codex panel
|
|
also switches the Web projection after the new owner snapshot is ready. The
|
|
Web UI cannot list, select, or create conversations in this mode. Switch to
|
|
asynchronous mode when the browser should own conversation navigation.
|
|
To avoid ambiguity when several Codex windows are open, run **Codex Remote: Set Existing Thread ID**.
|
|
An empty value restores automatic discovery.
|
|
|
|
Useful commands:
|
|
|
|
- **Codex Remote: Start Bridge** / **Stop Bridge**
|
|
- **Codex Remote: Set Existing Thread ID**
|
|
- **Codex Remote: Set Relay Token**
|
|
- **Codex Remote: Pair with Aether**
|
|
- **Codex Remote: Configure Aether Cloud Relay**
|
|
- **Codex Remote: Send Input**
|
|
- **Codex Remote: Show Snapshot**
|
|
|
|
## Settings
|
|
|
|
| Setting | Default | Meaning |
|
|
| --- | --- | --- |
|
|
| `codexRemoteCollab.controlMode` | `sync` | `sync` follows VS Code; `async` owns an independent app-server. |
|
|
| `codexRemoteCollab.localRelayUrl` | `ws://127.0.0.1:8787/v1/connect` | Bundled loopback relay used by the local Web control. |
|
|
| `codexRemoteCollab.aetherUrl` | empty | Aether origin remembered by the pairing command. |
|
|
| `codexRemoteCollab.cloudRelayUrl` | empty | Aether WebSocket relay URL populated by pairing. |
|
|
| `codexRemoteCollab.threadId` | empty | Exact existing conversation ID; empty enables discovery. |
|
|
| `codexRemoteCollab.autoDiscoverThread` | `true` | Discover and owner-check a local VS Code session. |
|
|
| `codexRemoteCollab.followVscodeSession` | `true` | Legacy compatibility setting; synchronous mode always follows VS Code. |
|
|
| `codexRemoteCollab.ipcSocketPath` | empty | Override the local IPC socket path. |
|
|
| `codexRemoteCollab.hostId` | `local` | Owner-discovery host identifier. |
|
|
| `codexRemoteCollab.ipcStrictVersions` | `true` | Reject unsupported stream protocol versions. |
|
|
| `codexRemoteCollab.approvalTimeoutMs` | `300000` | Deny an unanswered request locally after this delay. |
|
|
| `codexRemoteCollab.allowHighRiskApprovals` | `false` | Permit remote high-risk approvals when explicitly enabled. |
|
|
|
|
`codexRemoteCollab.codexCommand`, `codexArgs`, and `defaultCwd` apply only to
|
|
asynchronous mode. The deprecated `mode=attach/spawn` values map to
|
|
`controlMode=sync/async` when no explicit control mode exists.
|
|
|
|
## Pair with Aether
|
|
|
|
The local relay stays enabled after cloud pairing. In Aether, open **Codex remote
|
|
control** and generate a one-time code. Then run **Codex Remote: Pair with Aether**
|
|
from the VS Code Command Palette, enter the Aether server URL and the code, and
|
|
the bridge will connect to both relays. The long-lived device credential is stored
|
|
only in VS Code SecretStorage. Revoke a lost or retired device from the Aether page.
|
|
|
|
## Relay behavior
|
|
|
|
The bridge sends a `hello` and, when a relay token is configured, a separate
|
|
bearer-auth frame over an outbound WebSocket. It publishes normalized events including:
|
|
|
|
- `connection.opened` / `connection.closed`
|
|
- `session.snapshot`
|
|
- `output.snapshot` / `output.chunk`
|
|
- `task.started` / `task.finished` / `task.cancelled`
|
|
- `approval.requested` / `approval.resolved` / `approval.expired`
|
|
- `input.requested` / `input.resolved` / `input.expired`
|
|
|
|
Remote commands are mapped to the existing conversation owner:
|
|
|
|
- `control/mode/set` atomically switches between `sync` and `async`.
|
|
- `session/list`, `session/select`, and `session/new` are available only in
|
|
asynchronous mode and map to `thread/list`, `thread/resume`, and `thread/start`.
|
|
- `turn/start` starts a turn in the attached thread.
|
|
- `turn/steer` adds input to the active turn.
|
|
- `turn/interrupt` interrupts the expected active turn.
|
|
- `approval.respond`, `input.respond`, and `server.request.respond` preserve the
|
|
original request ID and use method-specific follower responses.
|
|
- `thread/start` is deliberately rejected in synchronous mode because VS Code
|
|
owns conversation navigation there.
|
|
|
|
The browser never connects directly to the IPC socket. Relay and host both
|
|
enforce role/capability checks; high-risk command approval remains disabled
|
|
unless the local VS Code setting opts in.
|
|
|
|
## Supported follower requests
|
|
|
|
- `item/commandExecution/requestApproval`
|
|
- `item/fileChange/requestApproval`
|
|
- `item/permissions/requestApproval`
|
|
- `item/tool/requestUserInput`
|
|
- `mcpServer/elicitation/request`
|
|
- legacy `applyPatchApproval` and `execCommandApproval`
|
|
|
|
Unanswered requests expire with a local deny. JSON-RPC numeric and string IDs
|
|
remain distinct, and a response can be submitted only once.
|
|
|
|
## Legacy mode migration
|
|
|
|
The old setting remains accepted:
|
|
|
|
```json
|
|
{
|
|
"codexRemoteCollab.mode": "spawn",
|
|
"codexRemoteCollab.codexCommand": "/absolute/path/to/codex",
|
|
"codexRemoteCollab.codexArgs": ["app-server", "--stdio"]
|
|
}
|
|
```
|
|
|
|
It maps to `controlMode=async`. Prefer the new setting directly. A
|
|
`spawn codex ENOENT` error belongs only to asynchronous mode; it is not a
|
|
synchronous-mode prerequisite or a PATH problem that needs fixing for
|
|
existing-session control.
|
|
|
|
The standalone `npm run start:stdio` entry point and `createBridge()` helper
|
|
also retain the legacy app-server adapter for compatibility.
|
|
|
|
## Embedding the attach adapter
|
|
|
|
The reusable exports are in `src/index.ts`:
|
|
|
|
```ts
|
|
import {
|
|
CodexIpcAgentAdapter,
|
|
RelayClient,
|
|
RelayHost,
|
|
} from "codex-remote-collab";
|
|
|
|
const adapter = new CodexIpcAgentAdapter({
|
|
threadId: process.env.CODEX_THREAD_ID,
|
|
autoDiscoverThread: true,
|
|
});
|
|
const relay = new RelayClient({
|
|
url: "wss://relay.example.test/v1/connect",
|
|
accessToken: process.env.CODEX_REMOTE_HOST_TOKEN,
|
|
});
|
|
const host = new RelayHost({ adapter, relay });
|
|
await host.start();
|
|
```
|
|
|
|
`CodexIpcClient` is exported separately for protocol fixtures and diagnostics.
|
|
Use `followConversation()` before follower mutations, and always target the
|
|
owner returned by `findThreadOwner()`.
|
|
|
|
## Troubleshooting
|
|
|
|
- **No existing session found:** open the target official Codex conversation,
|
|
keep that VS Code window running, then retry or set its exact thread ID.
|
|
- **Owner not found:** the rollout exists on disk but no live official client
|
|
currently owns it. Reopen the conversation in the Codex panel.
|
|
- **IPC version mismatch:** update this bridge for the installed official
|
|
extension. Disabling strict versions is diagnostic only.
|
|
- **Relay stays at waiting for host:** confirm host mode, relay URL, and that no
|
|
second host is already connected. If authentication is enabled, also check the
|
|
host token.
|
|
- **Old `spawn codex ENOENT` message:** install version `0.4.0`, reload VS Code,
|
|
and verify `codexRemoteCollab.controlMode` is `sync` unless independent
|
|
conversations are intended.
|