Files
Aether/aether-vscodex/vscode-extension/README.md
T

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.