@minhspark/codex-mcp-bridge 1.13.9 → 1.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +53 -24
- package/package.json +6 -6
- package/scripts/check.mjs +6 -1
- package/scripts/install-claude-desktop.mjs +22 -7
- package/scripts/install-codex-mcp.mjs +8 -5
- package/scripts/install-native-relay.mjs +15 -9
- package/src/bridge-account-context.mjs +77 -0
- package/src/claude-bridge-launcher.mjs +3 -0
- package/src/claude-bridge.mjs +91 -28
- package/src/claude-desktop-context.mjs +11 -6
- package/src/claude-sender-context.mjs +206 -0
- package/src/claude-session-router.mjs +92 -0
- package/src/codex-account-context.mjs +116 -0
- package/src/codex-bridge-launcher.mjs +3 -0
- package/src/codex-mcp-registration.mjs +14 -9
- package/src/desktop-account-context.mjs +140 -0
- package/src/desktop-task-receipts.mjs +3 -1
- package/src/index.mjs +91 -9
- package/src/mcp-supervisor.mjs +326 -0
- package/src/native-relay-companion.mjs +173 -34
- package/src/native-relay-launcher.mjs +3 -0
- package/src/native-relay.mjs +234 -50
- package/src/peer-protocol.mjs +120 -17
- package/src/platform.mjs +4 -1
- package/src/release-snapshot.mjs +198 -0
- package/src/reload-control.mjs +102 -0
- package/src/reply-forwarder.mjs +43 -0
- package/src/thread-delivery.mjs +27 -11
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,53 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [SemVer](ht
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.15.1] - 2026-09-14
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Allow bounded cold startup time for the Windows snapshot identity test on slower CI hosts while preserving the production five-second inspection deadline and the real MCP caller check.
|
|
12
|
+
- Discover the native tools pipe on current Windows Codex Desktop builds that load app tools through a plugin instead of an inline app-server override. Validate the Desktop process ancestry and pipe owner before connecting, so the relay can start without a pinned pipe address.
|
|
13
|
+
- Keep native relay reloads usable when another companion takes over a shared listener during the handoff, while still rejecting a missing endpoint.
|
|
14
|
+
- Migrate legacy Desktop installations explicitly with `install-claude-desktop.mjs --desktop-tasks`, disable external app-server autostart, and leave automatic routing unpinned so a later relay installation can take effect.
|
|
15
|
+
- Reload effective shared routing changes on the existing MCP connection when safely idle. Allow legacy-to-Desktop upgrades while preserving confirmed receipts without replay; reject automatic downgrades to the external app-server. Clarify that open Desktop tasks are valid native destinations.
|
|
16
|
+
- Reuse validated dependency hashes for unchanged files when preparing cached runtimes, avoiding repeated content reads that can delay MCP initialization during Windows startup. Changed source dependencies and modified cached copies still require verification; missing or invalid hash metadata falls back to reading file contents.
|
|
17
|
+
- Log runtime preparation start and elapsed time before the MCP worker launches, so startup delays can be distinguished from worker initialization failures.
|
|
18
|
+
|
|
19
|
+
## [1.15.0] - 2026-09-07
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Keep Claude and Codex MCP stdio connections open through a stable supervisor, with immutable worker snapshots and automatic activation of compatible installed-source changes when safely idle. Packaged bridge commands and all three MCP installers use the supervisor.
|
|
24
|
+
- Expose automatic reload state and pending-update blockers in status diagnostics. Failed candidates retain the working generation; active calls, pending deliveries, late replies, and native relay work defer replacement without replaying prompts.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Preserve existing Claude MCP entry properties and native companion environment settings during installer upgrades. Codex registrations refuse to overwrite custom access, timeout, or transport settings and print the exact launcher fields to change in place. Accept or preserve an explicit Claude data directory without binding an account.
|
|
29
|
+
|
|
30
|
+
### Upgrade notes
|
|
31
|
+
|
|
32
|
+
- Re-run the relevant installers and reconnect each MCP server once in its existing task to load the supervisor. A previously running direct-entry process cannot adopt this mechanism from a disk update alone. Subsequent compatible worker updates do not require an application restart.
|
|
33
|
+
- Automatic reload observes installed files; npm/Git updates remain the responsibility of the existing update mechanism. Client environment changes and incompatible supervisor changes still require MCP reconnect. Pending or uncertain deliveries can defer an update until safely resolved; never resend them merely to unblock a reload.
|
|
34
|
+
|
|
35
|
+
## [1.14.0] - 2026-09-07
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- Rediscover Claude Desktop and Codex account identity for each Desktop call, including repeated account switches and Windows Store Claude installations. Add `target: "auto"` for the unique current-account Claude Code task in an exact project directory, with native task identity retained across CLI restarts.
|
|
40
|
+
- Bind pending replies and Codex creation receipts to their original account fingerprints. Verify the calling Claude Code task through process ancestry, reject lingering old-account callers, and hide delayed results after an account change without repeating an uncertain delivery.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- Clear automatically discovered native relay endpoints after disconnect, error, or close so the next request can discover a replacement while preserving explicit socket overrides and uncertain-delivery semantics.
|
|
45
|
+
- Recheck account identity after asynchronous transport connection and before native dispatch. Account-bound requests use protocol v2 and a separate companion endpoint so an older shared companion cannot accept them without enforcement.
|
|
46
|
+
- Isolate Windows MCP contract fixtures from the machine's live native relay pipe. Skip file-symlink tests only when Windows denies creating the test link.
|
|
47
|
+
- Read Windows caller ancestry through a bounded native process snapshot instead of WMI service queries. Clean up integration fixtures even when initialization fails, and fail creation gates promptly when preflight rejects a request.
|
|
48
|
+
|
|
49
|
+
### Upgrade notes
|
|
50
|
+
|
|
51
|
+
- Reconnect both Desktop MCP clients once after upgrading. Codex can defer its reload until the next active turn. Verify the loaded version and current runtime from that task; subsequent account changes are inspected on each call.
|
|
52
|
+
- Account detection uses Claude's local sign-in snapshot and Codex file-backed ChatGPT identity. Unsupported or transitional identity states stop dispatch until the next call can verify them. Legacy creation receipts without account fingerprints remain protected from implicit reuse.
|
|
53
|
+
|
|
7
54
|
## [1.13.9] - 2026-09-07
|
|
8
55
|
|
|
9
56
|
### Fixed
|
package/README.md
CHANGED
|
@@ -32,10 +32,22 @@ Reusing a receipt also verifies that the saved project still has the same identi
|
|
|
32
32
|
|
|
33
33
|
The updated companion exposes a separate `-desktop-tasks` endpoint so a previous companion holding the legacy reply socket need not be killed during an upgrade. `codex_bridge_status` verifies the native connection through Desktop's local project list; `native_relay_status` reports both endpoints. Reconnect the companion after upgrading so it accepts the current native operations.
|
|
34
34
|
|
|
35
|
+
On Windows, the companion supports both legacy inline app-server configuration and current Codex Desktop plugin startup. When no native pipe is inherited, discovery checks the companion's Desktop process ancestry and the named pipe's server process, then uses a read-only project-list probe to distinguish app tools from browser-only pipes. A running companion alone does not establish readiness: verify that `native_relay_status` reports its account relay listening and that `codex_bridge_status` can read Desktop projects.
|
|
36
|
+
|
|
35
37
|
Desktop mode never contacts, starts, or falls back to an external app-server, including during status checks and thread discovery. The Claude Desktop installer records `CODEX_BRIDGE_AUTOSTART=0` in this mode. If Desktop or its companion is unavailable, the bridge reports the failure and leaves existing tasks in Desktop. `stop_codex_app_server` is a no-op in Desktop mode; stop an obsolete external service through its own launcher after confirming it has no active work.
|
|
36
38
|
|
|
37
39
|
`list_codex_threads` filters Desktop's recent/pinned task snapshot by authorization, workspace/title, and result limit. The snapshot can omit agent-created tasks visible in the sidebar and does not cover the complete archive. Absence from this list does not mean creation failed: read the returned task ID directly. Desktop does not expose the external server's `loadedOnly` state; requesting it returns an explanation without contacting that server.
|
|
38
40
|
|
|
41
|
+
### Switching Desktop accounts
|
|
42
|
+
|
|
43
|
+
Desktop calls reread both applications' stored account identity on every invocation and immediately before dispatch. Switching A → B → A does not require a manual peer binding. For Codex → Claude, use `send_to_claude_session` with `target: "auto"` and the exact `expectedCwd`; it selects the current account's only live Code task in that directory. Supply `expectedTaskId` when several tasks match. An explicit task ID stays pinned to that task; if its CLI process restarts, the bridge can rediscover the same native task without choosing a different one.
|
|
44
|
+
|
|
45
|
+
Claude → Codex verifies the calling Code task through the MCP process's parent ancestry and current-account task metadata. An old-account process that remains alive cannot dispatch under the new login. Replies and creation receipts retain both original account fingerprints. A login change during a pending operation hides the old reply and blocks forwarding to the new account; it never repeats an uncertain send or creates a replacement task. Legacy creation receipts without account identity require inspection of their existing task before reuse.
|
|
46
|
+
|
|
47
|
+
Claude detection reads its Desktop sign-in snapshot, including Windows Store installations. Codex detection currently supports file-backed ChatGPT login identity in `auth.json`; keyring, automatic/ephemeral credential storage, API keys, and external tokens return an unavailable state. These local records establish routing identity, not a fresh server authentication check. During logout or an incomplete sign-in, dispatch stops; the next call inspects the records again. The bridge returns fingerprints and status, never tokens or email addresses.
|
|
48
|
+
|
|
49
|
+
After installing this update, reconnect both MCP clients once. Codex may defer its MCP reload until the next active turn; verify the loaded version and `runtime state: current` from that task. Subsequent account changes are detected without a source reload. Account-bound requests use protocol v2 through a separate `-accounts-v2` companion endpoint, so a previous companion holding the legacy socket does not need to be killed and cannot silently ignore the identity checks. Automatically discovered native relay endpoints are discarded after disconnect so the next request discovers the new endpoint; explicit endpoint overrides remain fixed.
|
|
50
|
+
|
|
39
51
|
## Architecture
|
|
40
52
|
|
|
41
53
|
Two MCP servers, one living inside each agent:
|
|
@@ -112,7 +124,7 @@ Two ways in. Pick by what you intend to do with it.
|
|
|
112
124
|
npm install -g @minhspark/codex-mcp-bridge
|
|
113
125
|
```
|
|
114
126
|
|
|
115
|
-
Already installed? The same command with `@latest` upgrades it in place
|
|
127
|
+
Already installed? The same command with `@latest` upgrades it in place. Once the supervisor is registered, compatible updates load automatically without restarting the client. Details in [Upgrading an install you already have](#upgrading-an-install-you-already-have):
|
|
116
128
|
|
|
117
129
|
```bash
|
|
118
130
|
npm install -g @minhspark/codex-mcp-bridge@latest
|
|
@@ -128,9 +140,21 @@ Either route puts the bridge servers, configuration installers, native relay, an
|
|
|
128
140
|
|
|
129
141
|
#### Upgrading an install you already have
|
|
130
142
|
|
|
131
|
-
`npm install -g @minhspark/codex-mcp-bridge@latest
|
|
143
|
+
Install the new files with `npm install -g @minhspark/codex-mcp-bridge@latest`. To migrate an existing direct-entry installation, run `codex-mcp-bridge-install` for Claude Desktop, `claude-mcp-bridge-install` for Codex, and `codex-native-relay-install --no-bootstrap` if the native companion is installed. Update Claude Code's separate registration as shown below. Reconnect each MCP server once in its existing task so it loads the supervisor. No replacement task or full app exit is required when the client exposes MCP reconnect/reload. A process launched before this upgrade cannot acquire the supervisor merely because files changed on disk.
|
|
144
|
+
|
|
145
|
+
After this one-time migration, the stable supervisor keeps the client's MCP stdio connection open while it starts each worker from an immutable source snapshot. The installers prepare this snapshot before changing the registration, avoiding a slow first copy during MCP initialization. Compatible installed-source changes become candidates after the tree stabilizes. A candidate must initialize successfully before it can replace an idle worker. Active calls, pending deliveries, late replies, and relay work defer the switch; an update never silently resends a prompt. A failed candidate leaves the existing worker and its delivery state available.
|
|
132
146
|
|
|
133
|
-
|
|
147
|
+
Snapshot verification reuses previously calculated dependency hashes when file identity, size, modification time, and change time still match. Changed files are read and hashed again; directory inventories still detect additions and deletions. Missing or invalid hash metadata falls back to reading file contents. This reduces repeated disk reads during simultaneous MCP startup, especially just after Windows boots. Supervisor stderr logs show when runtime preparation starts and how long it takes.
|
|
148
|
+
|
|
149
|
+
Call `codex_bridge_status`, `claude_bridge_status`, or `native_relay_status` from the actual app task to inspect `autoReload`, the loaded version, and any pending update or blocker. Reading an old message receipt still refers to the worker that accepted that message. Pending or uncertain messages can keep an update deferred until they are resolved; a new process or an unknown message ID is never evidence that a send failed. If the supervisor protocol itself changes incompatibly, reconnect that MCP server once again.
|
|
150
|
+
|
|
151
|
+
Auto-reload watches the **installed source at the configured path**. It does not poll npm, download a release, run `git pull`, or upgrade Claude/Codex themselves. Keep the normal package update mechanism to install new versions. Changes to environment variables or access settings supplied by the MCP client require a client MCP reload to pass the new configuration to the supervisor. Subsequent account switches remain detectable per call without this configuration reload.
|
|
152
|
+
|
|
153
|
+
The installers preserve existing environment and access settings; explicit supported environment variables override their stored values. Claude's `--reset` explicitly returns to defaults. Codex entries with custom access, timeout, or transport settings are left intact with the exact command and arguments to update in place. `CLAUDE_DESKTOP_USER_DATA` pins only the data directory, never an account identity. Re-running an installer is unnecessary for later compatible source updates. The global install path remains stable across package versions.
|
|
154
|
+
|
|
155
|
+
To migrate a legacy Claude registration to native Desktop task delivery, run `codex-mcp-bridge-install --desktop-tasks` (from a checkout: `node scripts/install-claude-desktop.mjs --desktop-tasks`). This explicit flag overrides a stored or inherited `CODEX_BRIDGE_DESKTOP_TASKS=0` and disables external app-server autostart while preserving access settings. Without the flag or an explicit routing setting, the installer leaves routing automatic so a later native relay installation can enable Desktop tasks. Existing explicit `0` settings remain unchanged until the flag or an environment override is supplied.
|
|
156
|
+
|
|
157
|
+
Reconnect `codex-bridge` in the existing Claude task after changing its registration; source auto-reload cannot replace environment variables inherited by a running MCP process. If the task has a separate Claude Code MCP registration, update that registration too. Verify `codex_bridge_status` from the actual sending task reports Desktop tasks enabled, the native relay available, and a current runtime. Keep the destination open in Codex Desktop. An earlier unconfirmed send must be inspected before sending again.
|
|
134
158
|
|
|
135
159
|
#### Show the final npm installation result
|
|
136
160
|
|
|
@@ -157,7 +181,7 @@ npm install -g @minhspark/codex-mcp-bridge@latest
|
|
|
157
181
|
| Dry run | `Dry run completed: ... (no changes applied).` |
|
|
158
182
|
| Mode or installed metadata cannot be verified | A warning instead of a success claim |
|
|
159
183
|
|
|
160
|
-
The footer runs after npm finishes and keeps npm's output and exit code. It supports `install`/`i`, explicit global installation, one bridge package, dry-run flags, common install booleans, and a single `--prefix`. Other commands, multiple packages, unknown options, and machine-readable or silent output modes pass through without a footer. Installing the package itself does not alter shell profiles. A successful install confirms files on disk;
|
|
184
|
+
The footer runs after npm finishes and keeps npm's output and exit code. It supports `install`/`i`, explicit global installation, one bridge package, dry-run flags, common install booleans, and a single `--prefix`. Other commands, multiple packages, unknown options, and machine-readable or silent output modes pass through without a footer. Installing the package itself does not alter shell profiles. A successful install confirms files on disk; the app's bridge status separately confirms whether its supervisor has activated that version.
|
|
161
185
|
|
|
162
186
|
**Clone it** — right if you intend to read, test or change the code:
|
|
163
187
|
|
|
@@ -167,7 +191,7 @@ cd codex-mcp-bridge
|
|
|
167
191
|
npm install
|
|
168
192
|
```
|
|
169
193
|
|
|
170
|
-
A clone upgrades with `git pull && npm ci
|
|
194
|
+
A clone upgrades with `git pull && npm ci`; a registered supervisor activates a compatible stable update when its worker is safely idle. Direct `node src/index.mjs` and `node src/claude-bridge.mjs` remain available for diagnostics but bypass automatic reload. Use the registered supervisor or packaged bridge commands for long-lived clients.
|
|
171
195
|
|
|
172
196
|
Confirm the tree is healthy before wiring it into anything:
|
|
173
197
|
|
|
@@ -217,7 +241,7 @@ The result on macOS:
|
|
|
217
241
|
"mcpServers": {
|
|
218
242
|
"codex-bridge": {
|
|
219
243
|
"command": "/Users/<user>/.local/node/v24.18.0/bin/node",
|
|
220
|
-
"args": ["/Users/<user>/code/codex-mcp-bridge/src/index.mjs"],
|
|
244
|
+
"args": ["/Users/<user>/code/codex-mcp-bridge/src/mcp-supervisor.mjs", "index.mjs"],
|
|
221
245
|
"env": {
|
|
222
246
|
"CODEX_BIN": "/Users/<user>/.local/bin/codex",
|
|
223
247
|
"CODEX_APP_SERVER_URL": "ws://127.0.0.1:8791"
|
|
@@ -227,19 +251,19 @@ The result on macOS:
|
|
|
227
251
|
}
|
|
228
252
|
```
|
|
229
253
|
|
|
230
|
-
|
|
254
|
+
Reconnect `codex-bridge` once in the existing Claude task, then verify `codex_bridge_status` reports auto-reload enabled. To write the same entry by hand, use absolute paths for `command` and the supervisor script; its second argument is the worker filename.
|
|
231
255
|
|
|
232
256
|
### 3. Claude → Codex, into Claude Code (CLI)
|
|
233
257
|
|
|
234
258
|
Claude Code keeps its own MCP registry, so it needs its own registration. Run this from the repo root:
|
|
235
259
|
|
|
236
260
|
```bash
|
|
237
|
-
claude mcp add codex-bridge --scope user -e CODEX_BIN="$(command -v codex)" -- "$(command -v node)" "$PWD/src/index.mjs
|
|
261
|
+
claude mcp add codex-bridge --scope user -e CODEX_BIN="$(command -v codex)" -- "$(command -v node)" "$PWD/src/mcp-supervisor.mjs" index.mjs
|
|
238
262
|
```
|
|
239
263
|
|
|
240
264
|
```powershell
|
|
241
265
|
# Windows (PowerShell 7+)
|
|
242
|
-
claude mcp add codex-bridge --scope user -e CODEX_BIN="$((Get-Command codex).Source)" -- "$((Get-Command node).Source)" "$PWD\src\index.mjs
|
|
266
|
+
claude mcp add codex-bridge --scope user -e CODEX_BIN="$((Get-Command codex).Source)" -- "$((Get-Command node).Source)" "$PWD\src\mcp-supervisor.mjs" index.mjs
|
|
243
267
|
```
|
|
244
268
|
|
|
245
269
|
Check and remove it with:
|
|
@@ -250,7 +274,7 @@ claude mcp get codex-bridge
|
|
|
250
274
|
claude mcp remove codex-bridge --scope user
|
|
251
275
|
```
|
|
252
276
|
|
|
253
|
-
`--scope user` makes the bridge available in every project. Use `--scope project` instead to commit the entry into a repo's `.mcp.json` and share it with the team.
|
|
277
|
+
`--scope user` makes the bridge available in every project. Use `--scope project` instead to commit the entry into a repo's `.mcp.json` and share it with the team. For an existing registration, preserve its environment and access settings while replacing only the launcher command/arguments. Use the current session's `/mcp` reconnect action once, then verify `codex_bridge_status` from that session.
|
|
254
278
|
|
|
255
279
|
### 4. Codex → Claude, into Codex
|
|
256
280
|
|
|
@@ -261,7 +285,7 @@ node scripts/install-codex-mcp.mjs
|
|
|
261
285
|
The installer updates the named entry through `codex mcp add`, preserves existing environment settings, pins the configured Desktop policy, and disables external autostart in Desktop mode. It refuses to reset custom access, timeout, or transport settings. It never infers the sender permission class from the recipient. The basic registration by hand is:
|
|
262
286
|
|
|
263
287
|
```bash
|
|
264
|
-
codex mcp add claude-bridge --env CLAUDE_BRIDGE_PEER_NAME=codex-desktop -- "$(command -v node)" "$PWD/src/claude-bridge.mjs
|
|
288
|
+
codex mcp add claude-bridge --env CLAUDE_BRIDGE_PEER_NAME=codex-desktop -- "$(command -v node)" "$PWD/src/mcp-supervisor.mjs" claude-bridge.mjs
|
|
265
289
|
```
|
|
266
290
|
|
|
267
291
|
Verify and remove:
|
|
@@ -272,7 +296,7 @@ codex mcp get claude-bridge
|
|
|
272
296
|
node scripts/install-codex-mcp.mjs --remove
|
|
273
297
|
```
|
|
274
298
|
|
|
275
|
-
Reconnect the MCP server in the existing Codex Desktop task
|
|
299
|
+
Reconnect the MCP server once in the existing Codex Desktop task. Verify `claude_bridge_status` **from that task**: `autoReload` must be enabled, `runtime state` must be `current`, and `session policy` must match the intended mode. Codex may apply a requested reload on its next active turn. Do not create a replacement task. `npm run check:claude` starts a separate diagnostic process and cannot prove that the app's existing MCP connection reloaded.
|
|
276
300
|
|
|
277
301
|
`CLAUDE_BRIDGE_PEER_NAME` sets the name Claude shows for this bridge in its agent list.
|
|
278
302
|
|
|
@@ -284,7 +308,7 @@ Optional, and only worth installing if you keep the bound thread **open in Codex
|
|
|
284
308
|
node scripts/install-native-relay.mjs
|
|
285
309
|
```
|
|
286
310
|
|
|
287
|
-
This registers the companion with Codex (`codex mcp add codex-native-relay -- <node> src/native-relay-companion.mjs`) and bootstraps the executor thread the native dispatch needs, writing its id to `~/.codex/native-relay.json`. The `<node>` is resolved rather than inherited: Codex Desktop authenticates the code-signing identity of any process that connects to its native tools pipe, so on macOS the installer registers the runtime the app ships and prints the `relay runtime:` line naming it and where it came from. `CODEX_NATIVE_RELAY_NODE` overrides that on any platform; Windows and Linux keep the Node running the installer, as before. The bootstrap creates one thread, unsubscribes it, and closes its connection. It leaves other clients and threads running; an idle server may retain the executor until its configured unload delay expires.
|
|
311
|
+
This registers the companion with Codex (`codex mcp add codex-native-relay -- <node> src/mcp-supervisor.mjs native-relay-companion.mjs`) and bootstraps the executor thread the native dispatch needs, writing its id to `~/.codex/native-relay.json`. The installer preserves the existing environment and refuses to reset custom access, timeout, or transport settings. The `<node>` is resolved rather than inherited: Codex Desktop authenticates the code-signing identity of any process that connects to its native tools pipe, so on macOS the installer registers the runtime the app ships and prints the `relay runtime:` line naming it and where it came from. `CODEX_NATIVE_RELAY_NODE` overrides that on any platform; Windows and Linux keep the Node running the installer, as before. The supervisor uses that same runtime for its worker. The bootstrap creates one thread, unsubscribes it, and closes its connection. It leaves other clients and threads running; an idle server may retain the executor until its configured unload delay expires.
|
|
288
312
|
|
|
289
313
|
```bash
|
|
290
314
|
codex mcp get codex-native-relay
|
|
@@ -292,7 +316,7 @@ node scripts/install-native-relay.mjs --no-bootstrap # register only; supply C
|
|
|
292
316
|
node scripts/install-native-relay.mjs --remove # unregister, and remove the relay config and thread id
|
|
293
317
|
```
|
|
294
318
|
|
|
295
|
-
|
|
319
|
+
Reconnect `codex-native-relay` once in the existing Codex Desktop task so it launches the supervisor, then call its `native_relay_status` tool and verify auto-reload is enabled. `claude_bridge_status` also names the active delivery backend. Until both the companion and the executor thread are in place, `claude-bridge` keeps using the app-server path exactly as before.
|
|
296
320
|
|
|
297
321
|
### 6. macOS only: keep the app-server alive with launchd
|
|
298
322
|
|
|
@@ -392,13 +416,13 @@ Desktop task mode (`CODEX_BRIDGE_DESKTOP_TASKS=1` or the shared setting written
|
|
|
392
416
|
|
|
393
417
|
`claude_bridge_status` reports the destination policy, eligible session count, and excluded non-Desktop count. Send receipts include the destination's `entrypoint`, `cwd`, and `sessionId`. A correlated reply confirms receipt in that session; it does not independently prove that its conversation is visible in the Desktop UI. Desktop sends require `expectedCwd`, an absolute independently verified project directory. The bridge resolves both paths and refuses a missing, nonexistent, parent, or different directory before sending; it rechecks the destination after queueing. Reconnect the bridge's MCP connection after upgrading to load the enforcement code.
|
|
394
418
|
|
|
395
|
-
Desktop session discovery includes the native task title and task ID, matched through Claude Desktop's local session metadata. Use `list_claude_sessions({expectedCwd})` for the intended project; an empty result is not permission to substitute a different project.
|
|
419
|
+
Desktop session discovery includes the native task title and task ID, matched through the current Claude Desktop account's local session metadata. Use `list_claude_sessions({expectedCwd})` for the intended project; an empty result is not permission to substitute a different project. Explicit targets require `expectedTaskId` from the intended task, alongside `target` and `expectedCwd`. With `target: "auto"`, `expectedTaskId` is optional only when exactly one task matches the current account and project. The bridge verifies the exact CLI session ID, unique native task ID, canonical cwd, archive state, optional bridge ID, and live process start identity. It repeats these checks at dispatch. Missing, stale, ambiguous, or unreadable metadata blocks the send rather than guessing from a generated name such as `pcc4sh-19`. Each matched Desktop row also reports the task's permission mode from that metadata and its Claude parity class (`bypass` for `bypassPermissions`; `prompting` for `default`, `acceptEdits`, `auto`, and `dontAsk`; no class for `plan` or an unknown value).
|
|
396
420
|
|
|
397
421
|
A `held` receipt confirms only that the recipient has withheld a message. It does not prove an approval button is available in Claude Desktop. Inspect that exact task before directing the user to approve; when the UI offers no approval route, preserve the receipt and report the boundary. A preflight failure is distinct: it returns `sent: false` and no message ID, so nothing was submitted to the recipient.
|
|
398
422
|
|
|
399
423
|
Sends to the same Claude session run in order. An outstanding message blocks all further sends to that session, including `waitSec: 0`; changing wait time must not bypass a held or uncertain delivery. Use `read_claude_delivery` with the original message ID to observe `held`, `expired`, `refused`, or a late reply. A transport error after writing began also retains pending ownership. Different destination sessions remain independent. A process restart loses in-memory receipts: retain the original ID and inspect the existing recipient conversation before any resend; never treat an unknown ID as permission to retry.
|
|
400
424
|
|
|
401
|
-
Both bridge status tools report the loaded source fingerprint and process identity.
|
|
425
|
+
Both bridge status tools report the loaded source fingerprint and process identity. Supervised workers read immutable snapshots while the supervisor observes updates in the configured installation. An update waits for safe idle before activation; inspect `autoReload` for pending work or a failed candidate. Direct-entry workers retain the stale-source guard and require reconnect after source changes. Desktop-routing configuration changes still block new sends until the correct configuration is loaded, while Claude inbox/receipt reads remain available.
|
|
402
426
|
|
|
403
427
|
Claude's [inbound permission controls](https://code.claude.com/docs/en/cross-session-messaging#control-inbound-messages) remain authoritative, and their default rule is symmetric: when no `crossSessionInbound` value applies, a message is delivered only when the sender's attested class matches the recipient's. A recipient that bypasses permission prompts holds every sender that does not attest `bypass`; a recipient that prompts (default, `acceptEdits`, `auto`, `dontAsk`) holds every sender that attests `bypass`. The bridge attests the sender's true class from the verified Codex turn and never adjusts it to suit a recipient. Claude Desktop does not declare the `peer_inbound_approval` dialog kind to Claude Code, so a message held by a Desktop-hosted session has no approval button there; Claude Code keeps it until `dialogExpiry` (five minutes by default) and then reports it as expired. A `held` receipt in Desktop mode therefore means the two tasks run in different permission classes, not that the sender was misread. Report that to the user: only they can run the recipient task in the matching class or set `crossSessionInbound` to `accept` in that session's own settings. Because that outcome is predictable, `send_to_claude_session` first reads the recipient session's effective `crossSessionInbound` from the same files Claude Desktop loads (managed settings, then the user's `~/.claude/settings.json`, with the recipient project's `.claude/settings.json` and `settings.local.json` able only to tighten it) and refuses at preflight with `CLAUDE_RECIPIENT_INBOUND_POLICY` when that value is `refuse` or `hold`, since nothing could reach Claude or be approved. An explicit `accept` sends without a class check, because Claude delivers regardless of class. Otherwise the parity default applies: the recipient task's permission mode from Claude Desktop metadata is compared with the verified sender class and a mismatch is refused with `CLAUDE_RECIPIENT_CLASS_MISMATCH`, naming both classes and the user-only remedies on either side; both checks repeat immediately before writing. A task whose mode is unknown is sent as before and Claude decides. Flags passed at launch such as `--settings` are invisible to the bridge, so put the value in a settings file. `list_claude_sessions` shows an explicit inbound value with its source, and receipts carry `senderApprovalPolicy`, `recipientPermissionMode`, `recipientPermissionClass`, and `recipientInboundPolicy`; a held receipt repeats both classes. Do not change `CLAUDE_BRIDGE_PERMISSION_MODE`, fabricate a sender class, or edit recipient settings on the user's behalf. Legacy CLI configuration must truthfully represent every session using that MCP entry; Desktop sends derive their class from the verified calling turn. If the Desktop UI does not expose the pending approval, keep the original receipt rather than creating a CLI session.
|
|
404
428
|
|
|
@@ -442,7 +466,7 @@ See [Claude Code cross-session messaging](https://code.claude.com/docs/en/cross-
|
|
|
442
466
|
|
|
443
467
|
The relay has two hard limits in `src/claude-bridge.mjs`: at most **one forwarding attempt every 5s** and **50 per bridge run**. Replies arriving together are queued in order rather than discarded. Rebinding cannot reset the limit. Every queued reply keeps its original destination task and an inspectable forwarding status: `queued`, `sending`, `forwarded`, `failed`, `unknown`, or `blocked`. Read `read_claude_delivery` for a specific request or `read_claude_inbox` for late replies; status includes queue totals. A confirmed relay acknowledgement marks `forwarded`, while visibility in the Desktop conversation requires a separate UI check.
|
|
444
468
|
|
|
445
|
-
Uncertain delivery is never retried automatically or routed through another backend. Replies blocked by the session limit remain readable with an explicit reason. Source updates
|
|
469
|
+
Uncertain delivery is never retried automatically or routed through another backend. Replies blocked by the session limit remain readable with an explicit reason. Source updates defer worker replacement while deliveries or replies remain pending. The supervisor preserves the worker responsible for accepted prompts until it is safe to switch. A direct-entry worker instead blocks new prompts until reconnect after source changes. A manual restart still loses in-memory state: inspect pending messages before restarting; a new process does not prove that an old request failed. Two agents left talking to each other unattended still come to a stop.
|
|
446
470
|
|
|
447
471
|
## Tools — `codex-native-relay` (launched by Codex Desktop, macOS)
|
|
448
472
|
|
|
@@ -494,7 +518,9 @@ Claude
|
|
|
494
518
|
|
|
495
519
|
Resolution order is `CODEX_RELAY_ID` → that file → an error naming both. Never a guess: an invented executor fails inside Codex with a message that says nothing about the configuration that actually caused it. The recorded thread works as an executor even if it has never been opened in the app.
|
|
496
520
|
|
|
497
|
-
**
|
|
521
|
+
**Desktop-only delivery.** With Desktop tasks enabled, both `send_to_codex_thread` and correlated Claude replies use the native relay. Desktop keeps its writer lock throughout delivery. If the relay is unavailable, the bridge reports that failure and does not start or use an external app-server.
|
|
522
|
+
|
|
523
|
+
**Legacy reply delivery.** When Desktop tasks are explicitly disabled, `claude-bridge` can choose between native and external app-server delivery. The native path requires:
|
|
498
524
|
|
|
499
525
|
| Condition | Otherwise |
|
|
500
526
|
|---|---|
|
|
@@ -502,7 +528,7 @@ Resolution order is `CODEX_RELAY_ID` → that file → an error naming both. Nev
|
|
|
502
528
|
| `CODEX_BRIDGE_NATIVE_RELAY` is not `0` | switched off by hand |
|
|
503
529
|
| the Windows named pipe or macOS unix socket exists | the companion is not installed, or Codex Desktop is not running |
|
|
504
530
|
|
|
505
|
-
|
|
531
|
+
Only legacy reply delivery falls back to the app-server path when a companion cannot be reached before sending. Once a request has been written, a refusal, timeout, or lost acknowledgement does not trigger another delivery: the first attempt may already have succeeded. Invalid or oversized messages are also refused rather than passed to another backend.
|
|
506
532
|
|
|
507
533
|
The companion uses `CODEX_APP_TOOLS_PIPE_PATH` when inherited. Desktop builds that supply it only to their bundled `codex_app` MCP are also supported: the companion reads the exact `mcp_servers.codex_app.env` override from its launching app-server. It never picks a pipe from another session or saves a restart-specific address. The native connection remains separate from MCP stdio. If neither source provides a valid pipe, the relay stays unavailable; if a configured pipe is late during startup, it retries in the background. When several MCP instances start, status identifies a reachable shared companion instead of reporting that the relay is down.
|
|
508
534
|
|
|
@@ -521,11 +547,11 @@ The native pipe uses a 4-byte UInt32LE payload length followed by UTF-8 JSON-RPC
|
|
|
521
547
|
|
|
522
548
|
### Caveats
|
|
523
549
|
|
|
524
|
-
-
|
|
525
|
-
- **
|
|
526
|
-
- **
|
|
550
|
+
- Codex Desktop runs its own app-server. Its internal IPC endpoint is not an external app-server connection. The [native relay](#codex-desktop-native-relay), launched in Desktop's context, invokes Desktop's own tools to deliver into an existing task without resuming it through another server.
|
|
551
|
+
- **The writer-lock limitation applies to the legacy external app-server path.** A second writer receives `thread <id> already has an active writer`; an `idle` task can still be owned by Desktop. Native Desktop delivery supports tasks open in the app. Check `codex_bridge_status` in the actual sending task and migrate a legacy registration with `--desktop-tasks`; do not delete lock files, stop Desktop, or ask for manual copying merely because Desktop owns the task.
|
|
552
|
+
- **New Desktop tasks receive their saved project and title through Desktop's native creation tool.** Legacy bridge-created threads instead use the app-server's `thread/name/set` and an exact `codex://threads/<id>` link. Both paths preserve the requested workspace.
|
|
527
553
|
- A repo living on the NTFS partition of a dual-boot machine (`/Volumes/<label>/...`) is **read-only** under macOS. Keep a separate checkout on an APFS volume to run and edit it.
|
|
528
|
-
- `codex app-server daemon start` uses the `unix://` transport with a control socket at `~/.codex/app-server-control/app-server-control.sock`. The
|
|
554
|
+
- `codex app-server daemon start` uses the `unix://` transport with a control socket at `~/.codex/app-server-control/app-server-control.sock`. The legacy external bridge uses `ws://` instead; Desktop-only delivery uses the native relay.
|
|
529
555
|
|
|
530
556
|
## Troubleshooting
|
|
531
557
|
|
|
@@ -633,6 +659,9 @@ Runs the whole suite with `node --test`. It needs no Codex install, no login and
|
|
|
633
659
|
|
|
634
660
|
| File | Covers |
|
|
635
661
|
|---|---|
|
|
662
|
+
| `test/mcp-supervisor.test.mjs` | live reload of all three real MCP workers over unchanged client connections, immutable dependencies, rollback, pending deliveries, and no replay after crashes |
|
|
663
|
+
| `test/reload-control.test.mjs` | safe quiescence, retained receipts and account bindings, original task ownership, and forwarding limits |
|
|
664
|
+
| `test/native-relay-companion.test.mjs` | active socket handlers, startup races, listener handoff, and failed activation rollback |
|
|
636
665
|
| `test/tool-contract.test.mjs` | all three servers boot over stdio and every tool declares a title, a description, per-parameter descriptions and complete annotation hints |
|
|
637
666
|
| `test/server-requests.test.mjs` | all 10 app-server requests get a reply in the shape their schema declares — the regression test for "the turn pauses itself" |
|
|
638
667
|
| `test/reconnect.test.mjs` | reconnect after a dropped socket, no leaked pending requests or listeners, an interrupted turn ending promptly, a refused first handshake being retried |
|
|
@@ -644,7 +673,7 @@ Runs the whole suite with `node --test`. It needs no Codex install, no login and
|
|
|
644
673
|
| `test/native-relay.test.mjs` | the Codex Desktop relay: executor thread resolution, feature detection, Windows named-pipe and POSIX socket round trips, backend selection and fallback, and the companion answering a real MCP client that plays Codex Desktop |
|
|
645
674
|
| `test/repo-hygiene.test.mjs` | no environment file or build output is ever tracked, versions do not drift, documentation stays in English |
|
|
646
675
|
|
|
647
|
-
GitHub Actions runs the
|
|
676
|
+
GitHub Actions runs the suite on every push and pull request, across Node 22 and 24 on Linux, macOS and Windows (`.github/workflows/ci.yml`). Windows runners limit concurrent test files to two so immutable snapshot copies do not starve the bounded PowerShell process-identity checks. The Codex → Claude direction uses unix sockets on macOS/Linux and named pipes on Windows; the native relay has matching platform-specific coverage.
|
|
648
677
|
|
|
649
678
|
Two checks need a real Codex and are not part of `npm test`:
|
|
650
679
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minhspark/codex-mcp-bridge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"description": "Two-way MCP bridge between Claude and Codex: prompts into a live Codex thread, messages into a running Claude Code session.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"type": "module",
|
|
24
24
|
"bin": {
|
|
25
|
-
"codex-mcp-bridge": "src/
|
|
26
|
-
"claude-mcp-bridge": "src/claude-bridge.mjs",
|
|
25
|
+
"codex-mcp-bridge": "src/codex-bridge-launcher.mjs",
|
|
26
|
+
"claude-mcp-bridge": "src/claude-bridge-launcher.mjs",
|
|
27
27
|
"codex-mcp-bridge-install": "scripts/install-claude-desktop.mjs",
|
|
28
28
|
"claude-mcp-bridge-install": "scripts/install-codex-mcp.mjs",
|
|
29
|
-
"codex-native-relay": "src/native-relay-
|
|
29
|
+
"codex-native-relay": "src/native-relay-launcher.mjs",
|
|
30
30
|
"codex-native-relay-install": "scripts/install-native-relay.mjs",
|
|
31
31
|
"codex-npm-footer-install": "scripts/install-npm-footer.mjs"
|
|
32
32
|
},
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
|
-
"start": "node src/
|
|
45
|
-
"claude-bridge": "node src/claude-bridge.mjs",
|
|
44
|
+
"start": "node src/codex-bridge-launcher.mjs",
|
|
45
|
+
"claude-bridge": "node src/claude-bridge-launcher.mjs",
|
|
46
46
|
"app-server": "codex app-server --listen ws://127.0.0.1:8791",
|
|
47
47
|
"test": "node --test",
|
|
48
48
|
"check": "node scripts/check.mjs",
|
package/scripts/check.mjs
CHANGED
|
@@ -2,9 +2,12 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
2
2
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { desktopTasksConfigured } from "../src/native-relay.mjs";
|
|
5
6
|
|
|
6
7
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
7
8
|
const bridgeEnvNames = [
|
|
9
|
+
"CODEX_HOME",
|
|
10
|
+
"CLAUDE_DESKTOP_USER_DATA",
|
|
8
11
|
"CODEX_APP_SERVER_URL",
|
|
9
12
|
"CODEX_BIN",
|
|
10
13
|
"CODEX_BRIDGE_ALLOWED_ROOTS",
|
|
@@ -40,7 +43,9 @@ try {
|
|
|
40
43
|
await client.connect(transport);
|
|
41
44
|
const tools = await client.listTools();
|
|
42
45
|
console.log("tools:", tools.tools.map((t) => t.name).join(", "));
|
|
43
|
-
const listed = await client.callTool(
|
|
46
|
+
const listed = await client.callTool(desktopTasksConfigured()
|
|
47
|
+
? { name: "codex_bridge_status", arguments: {} }
|
|
48
|
+
: { name: "list_codex_threads", arguments: { limit: 3 } });
|
|
44
49
|
console.log(listed.content[0].text.slice(0, 600));
|
|
45
50
|
if (listed.isError) process.exitCode = 1;
|
|
46
51
|
} catch (err) {
|
|
@@ -6,12 +6,14 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { IS_WINDOWS, PLATFORM_LABEL, claudeDesktopConfigPath, resolveCodexBin } from "../src/platform.mjs";
|
|
7
7
|
import { desktopTasksConfigured } from "../src/native-relay.mjs";
|
|
8
8
|
import { exitForVersionRequest } from "../src/cli-version.mjs";
|
|
9
|
+
import { createReleaseSnapshot, snapshotRoot } from "../src/release-snapshot.mjs";
|
|
9
10
|
|
|
10
11
|
exitForVersionRequest(import.meta.url);
|
|
11
12
|
|
|
12
13
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
14
|
const cfgPath = process.env.CLAUDE_DESKTOP_CONFIG ?? claudeDesktopConfigPath();
|
|
14
15
|
const reset = process.argv.includes("--reset");
|
|
16
|
+
const forceDesktopTasks = process.argv.includes("--desktop-tasks");
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* This bridge is intentionally the local hand-off point between Claude and
|
|
@@ -51,17 +53,22 @@ cfg.mcpServers = cfg.mcpServers ?? {};
|
|
|
51
53
|
* already says, then the default. `--reset` drops the middle one for the rare
|
|
52
54
|
* case of wanting the defaults back.
|
|
53
55
|
*/
|
|
54
|
-
const
|
|
56
|
+
const previousEntry = (reset ? {} : cfg.mcpServers["codex-bridge"]) ?? {};
|
|
57
|
+
const previousEnv = previousEntry.env ?? {};
|
|
55
58
|
const settled = (name, fallback) => process.env[name] ?? previousEnv[name] ?? fallback;
|
|
56
|
-
const desktopMode =
|
|
59
|
+
const desktopMode = forceDesktopTasks ? "1" : settled("CODEX_BRIDGE_DESKTOP_TASKS");
|
|
60
|
+
const desktopEnabled = desktopMode === undefined ? desktopTasksConfigured() : desktopMode === "1";
|
|
57
61
|
|
|
58
62
|
const kept = Object.keys(previousEnv).filter(
|
|
59
|
-
(name) => process.env[name] === undefined && name !== "CODEX_BIN"
|
|
63
|
+
(name) => process.env[name] === undefined && name !== "CODEX_BIN" &&
|
|
64
|
+
!(forceDesktopTasks && name === "CODEX_BRIDGE_DESKTOP_TASKS") &&
|
|
65
|
+
!(desktopEnabled && name === "CODEX_BRIDGE_AUTOSTART"),
|
|
60
66
|
);
|
|
61
67
|
|
|
62
68
|
cfg.mcpServers["codex-bridge"] = {
|
|
69
|
+
...previousEntry,
|
|
63
70
|
command: nodeBin,
|
|
64
|
-
args: [path.join(root, "src", "index.mjs"
|
|
71
|
+
args: [path.join(root, "src", "mcp-supervisor.mjs"), "index.mjs"],
|
|
65
72
|
env: {
|
|
66
73
|
/**
|
|
67
74
|
* Keys this installer knows nothing about survive too - a setting added by
|
|
@@ -90,8 +97,8 @@ cfg.mcpServers["codex-bridge"] = {
|
|
|
90
97
|
CODEX_BRIDGE_SANDBOX: settled("CODEX_BRIDGE_SANDBOX", "workspace-write"),
|
|
91
98
|
CODEX_BRIDGE_OPEN_IN_APP: settled("CODEX_BRIDGE_OPEN_IN_APP", IS_WINDOWS ? "1" : "0"),
|
|
92
99
|
CODEX_BRIDGE_RELEASE_AFTER_TURN: settled("CODEX_BRIDGE_RELEASE_AFTER_TURN", IS_WINDOWS ? "1" : "0"),
|
|
93
|
-
CODEX_BRIDGE_DESKTOP_TASKS: desktopMode,
|
|
94
|
-
CODEX_BRIDGE_AUTOSTART:
|
|
100
|
+
...(desktopMode !== undefined ? { CODEX_BRIDGE_DESKTOP_TASKS: desktopMode } : {}),
|
|
101
|
+
CODEX_BRIDGE_AUTOSTART: desktopEnabled ? "0" : settled("CODEX_BRIDGE_AUTOSTART", "1"),
|
|
95
102
|
...(process.env.CODEX_BRIDGE_ALLOWED_THREADS !== undefined
|
|
96
103
|
? { CODEX_BRIDGE_ALLOWED_THREADS: process.env.CODEX_BRIDGE_ALLOWED_THREADS }
|
|
97
104
|
: {}),
|
|
@@ -100,9 +107,14 @@ cfg.mcpServers["codex-bridge"] = {
|
|
|
100
107
|
: {}),
|
|
101
108
|
...(process.env.CODEX_BRIDGE_MODEL ? { CODEX_BRIDGE_MODEL: process.env.CODEX_BRIDGE_MODEL } : {}),
|
|
102
109
|
...(process.env.CODEX_BRIDGE_EFFORT ? { CODEX_BRIDGE_EFFORT: process.env.CODEX_BRIDGE_EFFORT } : {}),
|
|
110
|
+
...(process.env.CLAUDE_DESKTOP_USER_DATA !== undefined
|
|
111
|
+
? { CLAUDE_DESKTOP_USER_DATA: process.env.CLAUDE_DESKTOP_USER_DATA }
|
|
112
|
+
: {}),
|
|
103
113
|
},
|
|
104
114
|
};
|
|
105
115
|
|
|
116
|
+
createReleaseSnapshot(root, { cache: snapshotRoot({ ...process.env, ...cfg.mcpServers["codex-bridge"].env }) });
|
|
117
|
+
|
|
106
118
|
if (fs.existsSync(cfgPath)) {
|
|
107
119
|
const backup = `${cfgPath}.bak-${new Date().toISOString().slice(0, 10)}-codexbridge`;
|
|
108
120
|
if (!fs.existsSync(backup)) fs.copyFileSync(cfgPath, backup);
|
|
@@ -112,6 +124,7 @@ fs.writeFileSync(cfgPath, `${JSON.stringify(cfg, null, 2)}\n`, "utf8");
|
|
|
112
124
|
console.log(`platform: ${PLATFORM_LABEL}`);
|
|
113
125
|
console.log(`updated ${cfgPath}`);
|
|
114
126
|
console.log(JSON.stringify(cfg.mcpServers["codex-bridge"], null, 2));
|
|
127
|
+
console.log(`Desktop tasks: ${desktopEnabled ? "enabled" : "disabled"} (${desktopMode === undefined ? "automatic relay configuration" : forceDesktopTasks ? "--desktop-tasks" : "explicit environment setting"}).`);
|
|
115
128
|
|
|
116
129
|
if (reset) {
|
|
117
130
|
console.log("\n--reset: existing values were discarded in favour of the defaults.");
|
|
@@ -139,4 +152,6 @@ if (cfg.mcpServers["codex-bridge"].env.CODEX_BRIDGE_THREAD_POLICY === "owned") {
|
|
|
139
152
|
);
|
|
140
153
|
}
|
|
141
154
|
|
|
142
|
-
console.log("\
|
|
155
|
+
console.log("\nReconnect codex-bridge once in the existing Claude task to load the supervisor. Subsequent compatible installed-source updates reload automatically when the worker is safely idle.");
|
|
156
|
+
console.log("Verify codex_bridge_status from that task: autoReload must be enabled and runtime state must be current. A separate diagnostic process does not verify the app's loaded MCP process.");
|
|
157
|
+
if (desktopEnabled) console.log("Verify Desktop tasks are enabled and the native relay is available. Configuration changes require MCP reconnect even when source auto-reload is enabled; keep the destination task open in Codex Desktop.");
|
|
@@ -8,13 +8,15 @@ import { PLATFORM_LABEL, resolveCodexBin, spawnEnv } from "../src/platform.mjs";
|
|
|
8
8
|
import { exitForVersionRequest } from "../src/cli-version.mjs";
|
|
9
9
|
import { desktopTasksConfigured } from "../src/native-relay.mjs";
|
|
10
10
|
import { codexMcpRegistration } from "../src/codex-mcp-registration.mjs";
|
|
11
|
+
import { createReleaseSnapshot, snapshotRoot } from "../src/release-snapshot.mjs";
|
|
11
12
|
|
|
12
13
|
exitForVersionRequest(import.meta.url);
|
|
13
14
|
|
|
14
15
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
15
16
|
const codexBin = resolveCodexBin(process.env.CODEX_EXE);
|
|
16
17
|
const serverName = process.env.CLAUDE_BRIDGE_NAME ?? "claude-bridge";
|
|
17
|
-
const entry = path.join(root, "src", "
|
|
18
|
+
const entry = path.join(root, "src", "mcp-supervisor.mjs");
|
|
19
|
+
const entryArgs = ["claude-bridge.mjs"];
|
|
18
20
|
const remove = process.argv.includes("--remove");
|
|
19
21
|
const permissionMode = process.env.CLAUDE_BRIDGE_PERMISSION_MODE;
|
|
20
22
|
if (!remove && permissionMode && !["bypass", "prompting"].includes(permissionMode)) throw new Error("CLAUDE_BRIDGE_PERMISSION_MODE must be bypass or prompting");
|
|
@@ -35,13 +37,14 @@ const servers = JSON.parse(run(["mcp", "list", "--json"]));
|
|
|
35
37
|
if (!Array.isArray(servers)) throw new Error("Codex returned an invalid MCP inventory; existing configuration was not changed");
|
|
36
38
|
const existing = servers.some((server) => server.name === serverName)
|
|
37
39
|
? JSON.parse(run(["mcp", "get", serverName, "--json"])) : null;
|
|
38
|
-
const registration = codexMcpRegistration({ name: serverName, existing, node: process.execPath, entry, env: process.env, desktopOnly: desktopTasksConfigured() });
|
|
40
|
+
const registration = codexMcpRegistration({ name: serverName, existing, node: process.execPath, entry, entryArgs, env: process.env, desktopOnly: desktopTasksConfigured() });
|
|
41
|
+
createReleaseSnapshot(root, { cache: snapshotRoot({ ...process.env, ...registration.environment }) });
|
|
39
42
|
run(registration.args);
|
|
40
43
|
|
|
41
44
|
console.log(`platform: ${PLATFORM_LABEL}`);
|
|
42
45
|
console.log(`registered MCP server "${serverName}" with Codex:`);
|
|
43
|
-
console.log(`command: ${process.execPath} ${entry}`);
|
|
46
|
+
console.log(`command: ${process.execPath} ${entry} ${entryArgs.join(" ")}`);
|
|
44
47
|
console.log(`environment keys: ${registration.environmentKeys.join(", ")}`);
|
|
45
|
-
console.log("\nReconnect
|
|
46
|
-
console.log("Verify claude_bridge_status from that task: runtime state must be current and the session policy must match this installation. A separate diagnostic process does not verify the app's loaded MCP process.");
|
|
48
|
+
console.log("\nReconnect claude-bridge once in the existing Codex Desktop task to load the supervisor. Subsequent compatible installed-source updates reload automatically when the worker is safely idle. Do not create a replacement task.");
|
|
49
|
+
console.log("Verify claude_bridge_status from that task: autoReload must be enabled, runtime state must be current, and the session policy must match this installation. A separate diagnostic process does not verify the app's loaded MCP process.");
|
|
47
50
|
console.log(`remove: node scripts/install-codex-mcp.mjs --remove`);
|
|
@@ -17,12 +17,15 @@ import {
|
|
|
17
17
|
spawnEnv,
|
|
18
18
|
} from "../src/platform.mjs";
|
|
19
19
|
import { exitForVersionRequest } from "../src/cli-version.mjs";
|
|
20
|
+
import { stdioMcpRegistration } from "../src/codex-mcp-registration.mjs";
|
|
21
|
+
import { createReleaseSnapshot, snapshotRoot } from "../src/release-snapshot.mjs";
|
|
20
22
|
|
|
21
23
|
exitForVersionRequest(import.meta.url);
|
|
22
24
|
|
|
23
|
-
const VERSION = "1.
|
|
25
|
+
const VERSION = "1.15.1";
|
|
24
26
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
25
|
-
const entry = path.join(root, "src", "
|
|
27
|
+
const entry = path.join(root, "src", "mcp-supervisor.mjs");
|
|
28
|
+
const entryArgs = ["native-relay-companion.mjs"];
|
|
26
29
|
const serverName = process.env.CODEX_NATIVE_RELAY_NAME ?? "codex-native-relay";
|
|
27
30
|
const remove = process.argv.includes("--remove");
|
|
28
31
|
const skipBootstrap = process.argv.includes("--no-bootstrap");
|
|
@@ -60,11 +63,10 @@ if (!IS_MACOS && !IS_WINDOWS) {
|
|
|
60
63
|
console.log("claude-bridge will keep using the app-server path here.");
|
|
61
64
|
}
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
66
|
+
const servers = JSON.parse(run(["mcp", "list", "--json"]));
|
|
67
|
+
if (!Array.isArray(servers)) throw new Error("Codex returned an invalid MCP inventory; existing configuration was not changed");
|
|
68
|
+
const existingServer = servers.some((server) => server.name === serverName)
|
|
69
|
+
? JSON.parse(run(["mcp", "get", serverName, "--json"])) : null;
|
|
68
70
|
/**
|
|
69
71
|
* Registering under whichever Node happens to run this installer is what made
|
|
70
72
|
* the companion unreachable on macOS: Codex Desktop rejects a peer whose
|
|
@@ -75,7 +77,11 @@ try {
|
|
|
75
77
|
*/
|
|
76
78
|
const runtime = resolveCodexDesktopNodeBin();
|
|
77
79
|
|
|
78
|
-
|
|
80
|
+
const registration = stdioMcpRegistration({ name: serverName, existing: existingServer, node: runtime.path, entry, entryArgs, envOverrides: {
|
|
81
|
+
...(process.env.CLAUDE_DESKTOP_USER_DATA !== undefined ? { CLAUDE_DESKTOP_USER_DATA: process.env.CLAUDE_DESKTOP_USER_DATA } : {}),
|
|
82
|
+
} });
|
|
83
|
+
createReleaseSnapshot(root, { cache: snapshotRoot({ ...process.env, ...registration.environment }) });
|
|
84
|
+
run(registration.args);
|
|
79
85
|
|
|
80
86
|
console.log(`platform: ${PLATFORM_LABEL}`);
|
|
81
87
|
console.log(`relay runtime: ${runtime.path} (${runtime.source})`);
|
|
@@ -120,5 +126,5 @@ if (process.argv.includes("--desktop-tasks")) {
|
|
|
120
126
|
writeRelayConfig({ ...readRelayConfig(), desktopTasks: true });
|
|
121
127
|
console.log("Desktop task creation enabled: exact saved local projects, immediate visibility, Codex Desktop permissions.");
|
|
122
128
|
}
|
|
123
|
-
console.log("
|
|
129
|
+
console.log("Reconnect codex-native-relay once in the existing Codex Desktop task to load the supervisor, then verify native_relay_status reports autoReload enabled. Subsequent compatible installed-source updates reload automatically when safely idle.");
|
|
124
130
|
console.log("remove: node scripts/install-native-relay.mjs --remove");
|