@minhspark/codex-mcp-bridge 1.26.3 → 1.26.5
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/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +30 -0
- package/README.md +7 -7
- package/docs/new-macos-setup.md +7 -7
- package/docs/new-windows-setup.md +11 -9
- package/docs/project-onboarding.md +5 -5
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/install-native-relay.mjs +1 -1
- package/scripts/update.mjs +38 -20
- package/src/background-relay-registration.mjs +8 -1
- package/src/background-relay-service.mjs +12 -6
- package/src/claude-bridge.mjs +1 -1
- package/src/index.mjs +1 -1
- package/src/native-relay-companion.mjs +1 -1
- package/src/setup.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,36 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [SemVer](ht
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.26.5] - 2026-11-02
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Add `codex-bridge-update --user` to pin installation to the per-user npm prefix before inspecting or replacing packages. The recommended Windows command now avoids `EPERM` when Node resolves its global prefix to `Program Files`, including machines with several Node installations. Existing owned relay runtimes, project grants and disabled-service choices are preserved when moving between prefixes.
|
|
12
|
+
- Install the global package with npm lifecycle scripts disabled, then apply managed setup directly using Node and verify settings and live relay readiness. This removes the lifecycle shell and npm script-approval dependency, and preserves the installed package if setup needs repair.
|
|
13
|
+
- Report installation permission failures, numeric child exit codes, signals and timeouts explicitly while keeping raw child output and credentials out of diagnostics.
|
|
14
|
+
|
|
15
|
+
### Upgrade
|
|
16
|
+
|
|
17
|
+
Windows PowerShell, without administrator rights:
|
|
18
|
+
|
|
19
|
+
```powershell
|
|
20
|
+
npx.cmd --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest -- codex-bridge-update --user
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This single command updates the package, applies all managed client settings and verifies the independent background relay. On macOS/Linux, keep using the installation command in the README; optional `--user` selects `~/.local`.
|
|
24
|
+
|
|
25
|
+
## [1.26.4] - 2026-11-01
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- `codex-bridge-update` no longer stops with `UPDATE_SETUP_INCOMPLETE` / `WORKER_MCP_COLLISION` on machines with more than one Node.js installation. The Antigravity `worker-relay` entry is still recognised as the bridge's own when it launches this package's `background-relay-mcp.mjs` through another existing `node` / `node.exe`, matching how every other bridge MCP entry is checked. Entries that use a different script, a non-Node launcher, a missing executable, a URL, headers or embedded environment are still refused and left unchanged.
|
|
30
|
+
|
|
31
|
+
### Upgrade
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npm exec --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest -- codex-bridge-update
|
|
35
|
+
```
|
|
36
|
+
|
|
7
37
|
## [1.26.3] - 2026-11-01
|
|
8
38
|
|
|
9
39
|
### Fixed
|
package/README.md
CHANGED
|
@@ -36,10 +36,10 @@ npm exec --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest --
|
|
|
36
36
|
**Windows PowerShell**
|
|
37
37
|
|
|
38
38
|
```powershell
|
|
39
|
-
|
|
39
|
+
npx.cmd --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest -- codex-bridge-update --user
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
Requires **Node.js 22.13+**. Use `.cmd` for this package's commands in Windows PowerShell. **Use this same single command for a fresh install and every update**, including upgrades from 1.26.0 or 1.26.1. npm fetches the latest updater into its cache without running setup there. The updater installs that exact resolved version globally,
|
|
42
|
+
Requires **Node.js 22.13+**. Use `.cmd` for this package's commands in Windows PowerShell. **Use this same single command for a fresh install and every update**, including upgrades from 1.26.0 or 1.26.1. On Windows, `--user` selects `%APPDATA%\npm`, avoiding protected `Program Files` prefixes and administrator rights even when switching Node installations. npm fetches the latest updater into its cache without running setup there. The updater installs that exact resolved version globally with lifecycle scripts disabled, applies managed setup directly using Node, and verifies the global version, saved settings and service activation separately. A second installation command is not required.
|
|
43
43
|
|
|
44
44
|
For an enabled legacy Windows relay from 1.26.0 or 1.26.1, the updater moves only this package's owned service to newer code using private relay storage as its working directory before replacing the global package. The temporary service runtime points to the downloaded updater package; changing the working directory releases the old package lock. It waits for active work to drain and the replacement transport to become ready without killing processes or opening apps or chats. If migration is still pending after its bounded wait, it reports `UPDATE_RELAY_PENDING`; keep the existing jobs and receipts and rerun the same command after they finish. Intentionally disabled services stay disabled and are not reported as ready.
|
|
45
45
|
|
|
@@ -65,7 +65,7 @@ Keep the same request UUID for retries and read its durable receipt. Queued work
|
|
|
65
65
|
|
|
66
66
|
Worker delegation is bounded. `WORKER_DELEGATION_CAPACITY` means the destination has no free execution slot and that delegation was not sent or queued behind waiting workers. Inspect or finish existing delegated work before retrying the same unsent request. Queue and delegation limits are reported explicitly.
|
|
67
67
|
|
|
68
|
-
The
|
|
68
|
+
The updater disables npm lifecycle scripts for both the cached download and the global installation, then runs the installed package's managed setup directly using Node. This avoids npm script-policy differences, lifecycle shell failures and client MCP registrations pointing into a temporary npm cache directory. A direct global npm install remains an advanced alternative, but it skips the updater's migration of an old service that can lock the package on Windows.
|
|
69
69
|
|
|
70
70
|
| Client | Installed entry | Purpose |
|
|
71
71
|
| --- | --- | --- |
|
|
@@ -79,11 +79,11 @@ The recommended command's `--ignore-scripts` applies only to downloading the upd
|
|
|
79
79
|
|
|
80
80
|
For native Claude-to-Codex handoffs, use an existing native Claude **Code** session and the same local project saved in Codex Desktop. The shared `codex-bridge-desktop` connector has its own legacy route; it does not establish a native Code sender identity. Setup applies saved configuration without opening or restarting apps. Saved configuration is not proof of a live connection; available supervisors reload compatible workers when idle. Complete [verification](#verify-the-installation) with the tools already available in the actual clients. If a native host is unloaded, report the diagnostic instead of opening chats to load a relay.
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
Managed setup runs **after the updater's global npm installation succeeds**, outside npm's lifecycle hook. Local `npm install` and `npm ci` do not change app settings. For a deliberate package-only installation, use a direct global npm install with `CODEX_BRIDGE_SKIP_SETUP=1` or `--ignore-scripts`; the complete updater always applies and verifies setup, and refuses an explicit `CODEX_BRIDGE_SKIP_SETUP=1` opt-out. The optional [workflow skill](#workflow-skill--npx-skills), VS Code preview, and hosted ChatGPT pairing retain their separate setup steps.
|
|
83
83
|
|
|
84
84
|
### Setup recovery and checks
|
|
85
85
|
|
|
86
|
-
If service migration, installation or managed setup fails, the updater returns a nonzero exit and reports the failed step. Inspect that component and the installed version, correct the reported cause, and rerun the same update command above.
|
|
86
|
+
If service migration, installation or managed setup fails, the updater returns a nonzero exit and reports the failed step. Inspect that component and the installed version, correct the reported cause, and rerun the same update command above. A completed package installation is preserved if managed setup fails. For configuration-only repair of an already installed package, the following is an optional alternative:
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
89
|
codex-bridge-setup
|
|
@@ -549,10 +549,10 @@ Node.js 22.13+ is required. If a command is missing, open a new terminal and che
|
|
|
549
549
|
If the package or registrations are missing, use the complete installation command:
|
|
550
550
|
|
|
551
551
|
```powershell
|
|
552
|
-
|
|
552
|
+
npx.cmd --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest -- codex-bridge-update --user
|
|
553
553
|
```
|
|
554
554
|
|
|
555
|
-
This runs the latest updater outside the global package directory, handles an owned legacy service that can lock that directory,
|
|
555
|
+
This runs the latest updater outside the global package directory, handles an owned legacy service that can lock that directory, installs the exact resolved package into the per-user npm prefix, then directly applies and verifies managed setup. Lifecycle scripts remain disabled during npm installation. If a previous update reported `UPDATE_NPM_FAILED` or `UPDATE_INSTALL_PERMISSION` because npm selected a protected directory, `--user` selects the user-writable destination. If a previous direct install printed `1 package had install scripts blocked`, this same updater command installs and applies the managed settings together. Intentional setup opt-outs and disabled-service choices are not silently removed. On macOS/Linux, use the installation command above; optional `--user` selects `~/.local`, whose `bin` directory must be on `PATH` for terminal commands.
|
|
556
556
|
|
|
557
557
|
For an already-installed package, these optional commands preview or repair configuration without updating package files:
|
|
558
558
|
|
package/docs/new-macos-setup.md
CHANGED
|
@@ -19,9 +19,9 @@ npm exec --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest --
|
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
npm downloads the latest updater into its cache. `--ignore-scripts` suppresses
|
|
22
|
-
setup
|
|
23
|
-
version globally
|
|
24
|
-
and verifies the global version and configuration. No second installation or
|
|
22
|
+
setup in that temporary location. The updater installs its exact resolved
|
|
23
|
+
version globally with lifecycle scripts disabled, runs managed setup directly
|
|
24
|
+
using Node and verifies the global version and configuration. No second installation or
|
|
25
25
|
setup command is required. Existing disabled choices, authentication and custom
|
|
26
26
|
project grants are preserved; the updater does not open apps or chats or kill
|
|
27
27
|
processes. A pending service migration is reported instead of interrupting work.
|
|
@@ -32,8 +32,8 @@ official package under `~/.config/GptClaudeBridge/tools` (or
|
|
|
32
32
|
`XDG_CONFIG_HOME/GptClaudeBridge/tools`). No separate Codex CLI install command
|
|
33
33
|
is required. Run the install under the account that uses the apps, without `sudo`.
|
|
34
34
|
If setup or update exits nonzero, inspect the reported component and installed
|
|
35
|
-
version, correct the cause, and rerun the same updater command.
|
|
36
|
-
package
|
|
35
|
+
version, correct the cause, and rerun the same updater command. A completed
|
|
36
|
+
package installation is preserved when managed setup fails.
|
|
37
37
|
|
|
38
38
|
The command also installs an independent per-user LaunchAgent for separate
|
|
39
39
|
Claude, Codex and Antigravity CLI workers and updates its managed settings on
|
|
@@ -101,8 +101,8 @@ a nonzero exit status while configuration is incomplete.
|
|
|
101
101
|
|
|
102
102
|
Local `npm install` and `npm ci` do not change app settings. For a deliberate
|
|
103
103
|
package-only installation, a direct global npm install can use
|
|
104
|
-
`CODEX_BRIDGE_SKIP_SETUP=1` or `--ignore-scripts`;
|
|
105
|
-
|
|
104
|
+
`CODEX_BRIDGE_SKIP_SETUP=1` or `--ignore-scripts`; the complete updater runs
|
|
105
|
+
managed setup directly after package installation. For Homebrew, run
|
|
106
106
|
`codex-bridge-setup` after installing the formula. A pinned source checkout can
|
|
107
107
|
use `node scripts/setup.mjs`.
|
|
108
108
|
|
|
@@ -7,13 +7,15 @@ does not require the Desktop apps to be open. Use this one PowerShell command
|
|
|
7
7
|
for a fresh installation or an update, including from 1.26.0 or 1.26.1:
|
|
8
8
|
|
|
9
9
|
```powershell
|
|
10
|
-
|
|
10
|
+
npx.cmd --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest -- codex-bridge-update --user
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
npm downloads the latest updater into its cache. `--
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
npm downloads the latest updater into its cache. `--user` selects `%APPDATA%\npm`
|
|
14
|
+
instead of a protected Node installation directory such as `Program Files`;
|
|
15
|
+
administrator rights are not required. The updater installs its exact resolved
|
|
16
|
+
version globally with lifecycle scripts disabled, then runs managed setup
|
|
17
|
+
directly using Node. It displays progress and verifies the global version,
|
|
18
|
+
saved settings and relay activation.
|
|
17
19
|
No second installation or setup command is required.
|
|
18
20
|
|
|
19
21
|
An older Windows background service can keep the global npm package directory
|
|
@@ -33,8 +35,8 @@ official package in the current user's `.config\GptClaudeBridge\tools`
|
|
|
33
35
|
directory. It also configures native pipe forwarding and bootstraps the relay
|
|
34
36
|
executor. A failed component is reported as incomplete rather than ready. If
|
|
35
37
|
setup or update exits nonzero, inspect the reported component and installed
|
|
36
|
-
version, correct the cause, and rerun the same updater command.
|
|
37
|
-
package
|
|
38
|
+
version, correct the cause, and rerun the same updater command. A completed
|
|
39
|
+
package installation is preserved when managed setup fails.
|
|
38
40
|
|
|
39
41
|
The same command installs the independent worker relay as a hidden per-user
|
|
40
42
|
scheduled task and applies its managed settings on every update. It registers
|
|
@@ -104,8 +106,8 @@ attempting delivery.
|
|
|
104
106
|
Local `npm install` and `npm ci` do not configure apps. For a deliberate
|
|
105
107
|
package-only installation, a direct global npm install can use
|
|
106
108
|
`CODEX_BRIDGE_SKIP_SETUP=1` or `--ignore-scripts`. This differs from the
|
|
107
|
-
recommended updater command, which
|
|
108
|
-
|
|
109
|
+
recommended updater command, which disables lifecycle scripts and runs managed
|
|
110
|
+
setup directly after the final global installation. A direct install skips the
|
|
109
111
|
updater's migration of legacy Windows services that can lock the package.
|
|
110
112
|
The optional card extension, hosted ChatGPT pairing, and VS Code preview have
|
|
111
113
|
separate installation steps.
|
|
@@ -7,12 +7,12 @@ configures the independent worker relay automatically:
|
|
|
7
7
|
npm exec --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest -- codex-bridge-update
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
In Windows PowerShell use `
|
|
10
|
+
In Windows PowerShell use `npx.cmd --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest -- codex-bridge-update --user` to select the per-user npm prefix without administrator rights. npm fetches the latest updater into its
|
|
11
11
|
cache with setup disabled there. The updater moves an owned legacy service to
|
|
12
12
|
newer code using private relay storage as its working directory when needed,
|
|
13
13
|
temporarily using the downloaded updater package. It waits for active work to
|
|
14
|
-
drain, then installs its exact resolved version globally with
|
|
15
|
-
|
|
14
|
+
drain, then installs its exact resolved version globally with lifecycle scripts
|
|
15
|
+
disabled and runs managed setup directly using Node. It verifies the global installation and managed settings. No second
|
|
16
16
|
installation command is required. It does not open apps or chats or kill
|
|
17
17
|
processes. The setup creates
|
|
18
18
|
`codex-bridge` for Claude Code and the separate `codex-bridge-desktop` shared
|
|
@@ -37,10 +37,10 @@ setup after correcting an incomplete component. Add `.cmd` on Windows and
|
|
|
37
37
|
`--json` for structured output. Local package installs do not run setup.
|
|
38
38
|
For a deliberate package-only direct global installation,
|
|
39
39
|
`CODEX_BRIDGE_SKIP_SETUP=1` or npm `--ignore-scripts` opts out of setup; this is
|
|
40
|
-
separate from the
|
|
40
|
+
separate from the complete updater, which runs managed setup directly after installation.
|
|
41
41
|
Failed setup or update returns a nonzero exit. Inspect the reported component
|
|
42
42
|
and installed version, correct the cause, then rerun the same updater command.
|
|
43
|
-
|
|
43
|
+
A completed package installation is preserved when managed setup fails.
|
|
44
44
|
Explicit setup and `--check` also return a nonzero status until the saved
|
|
45
45
|
configuration passes verification.
|
|
46
46
|
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "codex-mcp-bridge",
|
|
4
|
-
"version": "1.26.
|
|
4
|
+
"version": "1.26.5",
|
|
5
5
|
"description": "Connect ChatGPT to your own Codex Desktop projects and conversations.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Bui Dang Minh",
|
|
@@ -25,7 +25,7 @@ import { removeRelayFile } from "../src/native-relay-files.mjs";
|
|
|
25
25
|
|
|
26
26
|
exitForVersionRequest(import.meta.url);
|
|
27
27
|
|
|
28
|
-
const VERSION = "1.26.
|
|
28
|
+
const VERSION = "1.26.5";
|
|
29
29
|
const root = stableInstallPath(path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."));
|
|
30
30
|
const entry = path.join(root, "src", "mcp-supervisor.mjs");
|
|
31
31
|
const entryArgs = ["native-relay-companion.mjs"];
|
package/scripts/update.mjs
CHANGED
|
@@ -87,11 +87,19 @@ function neutralDirectory(roots, env) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function childFailure(result, phase) {
|
|
90
|
-
const match = /npm\s+(?:ERR!|error)\s+code\s+([A-Z][A-Z0-9_]{0,79})\b/.exec(String(result?.stderr ?? ""));
|
|
91
|
-
const
|
|
90
|
+
const match = /npm\s+(?:ERR!|error)\s+code\s+([A-Z][A-Z0-9_]{0,79}|\d{1,10})\b/.exec(String(result?.stderr ?? ""));
|
|
91
|
+
const signal = result?.error?.signal ?? result?.signal;
|
|
92
|
+
const value = match?.[1] ?? result?.error?.code ?? (signal ? null : result?.status ?? result?.exitCode);
|
|
93
|
+
const timedOut = value === "ETIMEDOUT" || result?.error?.killed === true && signal === "SIGTERM";
|
|
94
|
+
const code = timedOut ? "ETIMEDOUT" : /^\d{1,10}$/.test(String(value)) ? `EXIT_${value}`
|
|
95
|
+
: /^[A-Z][A-Z0-9_]{0,79}$/.test(value ?? "") ? value
|
|
96
|
+
: /^SIG[A-Z0-9]{1,20}$/.test(signal ?? "") ? `SIGNAL_${signal}` : null;
|
|
92
97
|
const cause = code ? Object.assign(new Error("The child command reported a failure."), { code }) : undefined;
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
const permissionDenied = phase === "installation" && ["EPERM", "EACCES"].includes(code);
|
|
99
|
+
return failure(permissionDenied ? "UPDATE_INSTALL_PERMISSION" : code === "EBUSY" ? "UPDATE_PACKAGE_BUSY" : timedOut ? "UPDATE_COMMAND_TIMEOUT" : phase === "verification" ? "UPDATE_SETUP_INCOMPLETE" : "UPDATE_NPM_FAILED",
|
|
100
|
+
permissionDenied ? "npm could not write to the installation directory. Run codex-bridge-update --user to install and configure the bridge in your user account without administrator rights. Existing work and receipts were preserved."
|
|
101
|
+
: timedOut ? `The update command exceeded its deadline. ${RETRY}`
|
|
102
|
+
: code === "EBUSY" ? `The installed package is still held open by another process. No process was stopped. ${RETRY}`
|
|
95
103
|
: `The ${phase === "verification" ? "installed setup verification" : "npm command"} did not complete. ${RETRY}`, phase, cause);
|
|
96
104
|
}
|
|
97
105
|
|
|
@@ -114,28 +122,35 @@ function serviceQuiescent(config) {
|
|
|
114
122
|
export async function runUpdate({ args = [], env = process.env, platform = process.platform, root = ROOT, run = execute,
|
|
115
123
|
installService = installBackgroundRelay, readConfig = readRelayConfiguration, readRuntime = readRelayServiceRuntime,
|
|
116
124
|
quiescent = serviceQuiescent, now = Date.now, sleep = pause, waitMs = 120000, onProgress = () => {} } = {}) {
|
|
117
|
-
if (args.some(arg => !["--help", "--json"].includes(arg))) throw failure("UPDATE_ARGUMENT_INVALID", "Usage: codex-bridge-update [--json] [--help]", "preflight");
|
|
118
|
-
if (args.includes("--help")) return { help: "Usage: codex-bridge-update [--json]\nSafely update the global bridge and all managed settings. Legacy Windows relays migrate when existing work finishes; pending migrations can be retried with the same command." };
|
|
125
|
+
if (args.some(arg => !["--help", "--json", "--user"].includes(arg))) throw failure("UPDATE_ARGUMENT_INVALID", "Usage: codex-bridge-update [--user] [--json] [--help]", "preflight");
|
|
126
|
+
if (args.includes("--help")) return { help: "Usage: codex-bridge-update [--user] [--json]\nSafely update the global bridge and all managed settings. --user selects the per-user npm prefix (Windows: AppData/Roaming/npm; macOS/Linux: ~/.local) without administrator rights. Legacy Windows relays migrate when existing work finishes; pending migrations can be retried with the same command." };
|
|
119
127
|
if (env.CODEX_BRIDGE_SKIP_SETUP === "1") throw failure("UPDATE_SETUP_DISABLED", "CODEX_BRIDGE_SKIP_SETUP=1 disables managed setup. Remove that explicit opt-out before requesting this complete update.", "preflight");
|
|
120
128
|
if (!Number.isSafeInteger(waitMs) || waitMs < 1 || waitMs > 600000) throw failure("UPDATE_ARGUMENT_INVALID", "The update readiness deadline is invalid.", "preflight");
|
|
121
129
|
root = fs.realpathSync.native(root);
|
|
122
130
|
const version = packageVersion(root);
|
|
123
131
|
const npm = resolveUpdateNpm(env);
|
|
132
|
+
const userPrefix = args.includes("--user") ? platform === "win32"
|
|
133
|
+
? path.join(env.APPDATA || path.join(env.USERPROFILE || os.homedir(), "AppData", "Roaming"), "npm")
|
|
134
|
+
: path.join(env.HOME || os.homedir(), ".local") : null;
|
|
135
|
+
if (userPrefix && (!path.isAbsolute(userPrefix) || /[\u0000-\u001f\u007f]/.test(userPrefix))) {
|
|
136
|
+
throw failure("UPDATE_GLOBAL_ROOT_INVALID", "The per-user installation directory is invalid.", "preflight");
|
|
137
|
+
}
|
|
138
|
+
const prefixArgs = userPrefix ? ["--prefix", userPrefix] : [];
|
|
124
139
|
let cwd = neutralDirectory([root], env);
|
|
125
140
|
let childEnv = Object.fromEntries(Object.entries(env).filter(([name]) => !/^AGENT_RELAY_(?!DIRECTORY$)/i.test(name)));
|
|
126
|
-
const command = async (arguments_, phase, timeout = 600000) => {
|
|
141
|
+
const command = async (arguments_, phase, timeout = 600000, environment = childEnv) => {
|
|
127
142
|
let result;
|
|
128
143
|
try {
|
|
129
|
-
result = await run(process.execPath, arguments_, { env:
|
|
144
|
+
result = await run(process.execPath, arguments_, { env: environment, cwd, encoding: "utf8", windowsHide: true,
|
|
130
145
|
shell: false, timeout, maxBuffer: 8 * 1024 * 1024, stdio: ["ignore", "pipe", "pipe"] });
|
|
131
146
|
} catch (error) { throw childFailure({ error, stderr: error.stderr }, phase); }
|
|
132
147
|
if (result?.error || (result?.status ?? result?.exitCode ?? 0) !== 0) throw childFailure(result, phase);
|
|
133
148
|
return String(result?.stdout ?? "");
|
|
134
149
|
};
|
|
135
|
-
const modules = (await command([npm, "root", "--global"], "preflight", 30000)).trim();
|
|
150
|
+
const modules = (await command([npm, "root", "--global", ...prefixArgs], "preflight", 30000)).trim();
|
|
136
151
|
if (!path.isAbsolute(modules) || /[\u0000-\u001f\u007f]/.test(modules)) throw failure("UPDATE_GLOBAL_ROOT_INVALID", "npm returned an invalid global installation directory.", "preflight");
|
|
137
|
-
const prefix = (await command([npm, "prefix", "--global"], "preflight", 30000)).trim();
|
|
138
|
-
if (!path.isAbsolute(prefix) || /[\u0000-\u001f\u007f]/.test(prefix)
|
|
152
|
+
const prefix = (await command([npm, "prefix", "--global", ...prefixArgs], "preflight", 30000)).trim();
|
|
153
|
+
if (!path.isAbsolute(prefix) || /[\u0000-\u001f\u007f]/.test(prefix) || userPrefix && path.relative(userPrefix, prefix) !== ""
|
|
139
154
|
|| path.relative(modules, path.join(prefix, ...(platform === "win32" ? [] : ["lib"]), "node_modules")) !== "") {
|
|
140
155
|
throw failure("UPDATE_GLOBAL_ROOT_INVALID", "npm returned inconsistent global prefix and installation directories.", "preflight");
|
|
141
156
|
}
|
|
@@ -146,7 +161,7 @@ export async function runUpdate({ args = [], env = process.env, platform = proce
|
|
|
146
161
|
const config = readConfig({ env: childEnv, platform, missing: true });
|
|
147
162
|
let runtime;
|
|
148
163
|
if (config) {
|
|
149
|
-
runtime = readRuntime(config.paths.root, { root: globalRoot, nodeExecutable: process.execPath });
|
|
164
|
+
runtime = readRuntime(config.paths.root, { root: globalRoot, nodeExecutable: process.execPath, allowMissingFiles: true });
|
|
150
165
|
if (runtime.packageVersion && newerVersion(runtime.packageVersion, version)) throw failure("UPDATE_DOWNGRADE_REFUSED", "The owned relay runtime is newer than this updater. Run the update command with the latest published package.", "preflight");
|
|
151
166
|
if (runtime.environment !== undefined) childEnv = { ...Object.fromEntries(Object.entries(childEnv).filter(([name]) => !MANAGED_ENV.has(name.toUpperCase()))), ...runtime.environment };
|
|
152
167
|
childEnv.AGENT_RELAY_DIRECTORY = config.paths.root;
|
|
@@ -176,19 +191,22 @@ export async function runUpdate({ args = [], env = process.env, platform = proce
|
|
|
176
191
|
if (!quiescent(config)) throw Object.assign(failure("UPDATE_RELAY_PENDING", `The disabled relay still has an owned process whose release could not be verified. ${RETRY}`, "migration"), { pending: true });
|
|
177
192
|
} else {
|
|
178
193
|
onProgress("Waiting for the legacy relay to finish existing work and activate the update runtime.");
|
|
179
|
-
const staged = runtime.packageVersion && !legacy(runtime.packageVersion) && !newerVersion("1.26.2", runtime.packageVersion);
|
|
194
|
+
const staged = runtime.filesAvailable !== false && runtime.packageVersion && !legacy(runtime.packageVersion) && !newerVersion("1.26.2", runtime.packageVersion);
|
|
180
195
|
const service = await awaitService(staged ? runtime.packageRoot : root, !staged, "migration", staged ? runtime.packageVersion : version);
|
|
181
196
|
migrated = !staged && service.enabled;
|
|
182
197
|
}
|
|
183
198
|
}
|
|
184
|
-
onProgress(`Installing bridge ${version}
|
|
185
|
-
await command([npm, "install", "--global", "--prefix", prefix, `${PACKAGE}@${version}`, "--ignore-scripts
|
|
199
|
+
onProgress(`Installing bridge ${version}.`);
|
|
200
|
+
await command([npm, "install", "--global", "--prefix", prefix, `${PACKAGE}@${version}`, "--ignore-scripts"],
|
|
201
|
+
"installation", 600000, { ...childEnv, CODEX_BRIDGE_SKIP_SETUP: "1" });
|
|
186
202
|
if (packageVersion(globalRoot) !== version) throw failure("UPDATE_VERSION_MISMATCH", "The global package version does not match the updater version. Rerun the same update command.", "verification");
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
203
|
+
for (const check of [false, true]) {
|
|
204
|
+
onProgress(check ? "Verifying the global bridge settings and owned relay runtime." : "Applying managed settings from the installed bridge.");
|
|
205
|
+
let setup;
|
|
206
|
+
try { setup = JSON.parse(await command([path.join(globalRoot, "scripts", "setup.mjs"), ...(check ? ["--check"] : []), "--json"], "verification")); }
|
|
207
|
+
catch (error) { if (error?.code) throw error; throw failure("UPDATE_SETUP_INCOMPLETE", `Global setup did not return a verifiable result. ${RETRY}`, "verification"); }
|
|
208
|
+
if (setup?.configured !== true) throw failure("UPDATE_SETUP_INCOMPLETE", `The global bridge settings are incomplete. ${RETRY}`, "verification");
|
|
209
|
+
}
|
|
192
210
|
const service = await awaitService(globalRoot, false, "verification");
|
|
193
211
|
return { updated: true, configured: true, version, previousVersion, migrated, service, opensApps: false, opensChats: false };
|
|
194
212
|
}
|
|
@@ -120,9 +120,16 @@ function samePath(left, right, platform) {
|
|
|
120
120
|
catch { return false; }
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
function nodeLauncher(command, expected, platform) {
|
|
124
|
+
if (samePath(command, expected, platform)) return true;
|
|
125
|
+
if (typeof command !== "string" || !path.isAbsolute(command) || command.includes("\0")) return false;
|
|
126
|
+
if (!(platform === "win32" ? /^node\.exe$/i : /^node$/).test(path.basename(command))) return false;
|
|
127
|
+
try { return fs.statSync(command).isFile(); } catch { return false; }
|
|
128
|
+
}
|
|
129
|
+
|
|
123
130
|
function ownedEntry(entry, expected, platform) {
|
|
124
131
|
if (entry === undefined) return;
|
|
125
|
-
if (!object(entry) || !
|
|
132
|
+
if (!object(entry) || !nodeLauncher(entry.command, expected.command, platform)
|
|
126
133
|
|| !Array.isArray(entry.args) || entry.args.length !== 1 || !samePath(entry.args[0], expected.args[0], platform)
|
|
127
134
|
|| entry.type !== undefined && entry.type !== "stdio"
|
|
128
135
|
|| ["url", "serverUrl", "serverURL", "httpUrl", "headers", "oauth", "authProviderType"].some((key) => Object.hasOwn(entry, key))
|
|
@@ -66,7 +66,7 @@ export function relayServiceRuntimeId(runtime) {
|
|
|
66
66
|
return createHash("sha256").update(JSON.stringify([runtime.packageRoot, runtime.nodeExecutable, runtime.packageVersion ?? null, runtime.environment ?? {}])).digest("hex");
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
export function readRelayServiceRuntime(directory, { root = ROOT, nodeExecutable = stableNodeBin() } = {}) {
|
|
69
|
+
export function readRelayServiceRuntime(directory, { root = ROOT, nodeExecutable = stableNodeBin(), allowMissingFiles = false } = {}) {
|
|
70
70
|
let text;
|
|
71
71
|
try { text = readRegularConfigFile(path.join(directory, "service-runtime.json"), { missing: true, maxBytes: 64 * 1024 }); }
|
|
72
72
|
catch { throw serviceFailure("SERVICE_RUNTIME_INVALID"); }
|
|
@@ -78,11 +78,17 @@ export function readRelayServiceRuntime(directory, { root = ROOT, nodeExecutable
|
|
|
78
78
|
|| ![value.packageRoot, value.nodeExecutable].every((entry) => typeof entry === "string" && path.isAbsolute(entry) && !/[\u0000-\u001f\u007f]/.test(entry))
|
|
79
79
|
|| value.environment !== undefined && (!value.environment || typeof value.environment !== "object" || Array.isArray(value.environment)
|
|
80
80
|
|| Object.entries(value.environment).some(([name, entry]) => !["HOME", "PATH", "CODEX_HOME", "CLAUDE_CONFIG_DIR", "XDG_CONFIG_HOME"].includes(name)
|
|
81
|
-
|| typeof entry !== "string" || !entry || /[\u0000-\u001f\u007f]/.test(entry)))
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
|| typeof entry !== "string" || !entry || /[\u0000-\u001f\u007f]/.test(entry)))) throw serviceFailure("SERVICE_RUNTIME_INVALID");
|
|
82
|
+
let filesAvailable = true;
|
|
83
|
+
for (const entry of [path.join(value.packageRoot, "src", "background-relay-service.mjs"), path.join(value.packageRoot, "src", "background-relay-daemon.mjs"), value.nodeExecutable]) {
|
|
84
|
+
try {
|
|
85
|
+
if (!fs.statSync(entry).isFile()) throw serviceFailure("SERVICE_RUNTIME_INVALID");
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (!allowMissingFiles || error.code !== "ENOENT") throw serviceFailure("SERVICE_RUNTIME_INVALID");
|
|
88
|
+
filesAvailable = false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return allowMissingFiles ? { ...value, filesAvailable } : value;
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
export function acquireRelayServiceLock(root, { pid = process.pid, isAlive = alive, identity = processIdentity } = {}) {
|
package/src/claude-bridge.mjs
CHANGED
|
@@ -31,7 +31,7 @@ import { BACKGROUND_WORKER_GUIDANCE, registerBackgroundTools } from "./backgroun
|
|
|
31
31
|
|
|
32
32
|
exitForVersionRequest(import.meta.url);
|
|
33
33
|
|
|
34
|
-
const VERSION = "1.26.
|
|
34
|
+
const VERSION = "1.26.5";
|
|
35
35
|
void import("./telemetry.mjs").then(({ startUsageReporting }) => startUsageReporting({ version: VERSION })).catch(() => {});
|
|
36
36
|
const FORWARD_MIN_INTERVAL_MS = 5000;
|
|
37
37
|
const FORWARD_MAX_PER_SESSION = 50;
|
package/src/index.mjs
CHANGED
|
@@ -44,7 +44,7 @@ import { BACKGROUND_WORKER_GUIDANCE, registerBackgroundTools } from "./backgroun
|
|
|
44
44
|
|
|
45
45
|
exitForVersionRequest(import.meta.url);
|
|
46
46
|
|
|
47
|
-
const VERSION = "1.26.
|
|
47
|
+
const VERSION = "1.26.5";
|
|
48
48
|
void import("./telemetry.mjs").then(({ startUsageReporting }) => startUsageReporting({ version: VERSION })).catch(() => {});
|
|
49
49
|
const log = (msg) => process.stderr.write(`[codex-mcp-bridge] ${msg}\n`);
|
|
50
50
|
|
|
@@ -43,7 +43,7 @@ import { remoteCodexSocketPath, remoteNativeAdapter, remoteRequestResponse } fro
|
|
|
43
43
|
|
|
44
44
|
exitForVersionRequest(import.meta.url);
|
|
45
45
|
|
|
46
|
-
const VERSION = "1.26.
|
|
46
|
+
const VERSION = "1.26.5";
|
|
47
47
|
const log = (msg) => process.stderr.write(`[native-relay] ${msg}\n`);
|
|
48
48
|
|
|
49
49
|
function errorResponse(code, message, sent, details) {
|
package/src/setup.mjs
CHANGED
|
@@ -179,7 +179,7 @@ export function checkSetup({ env = process.env, platform = process.platform, roo
|
|
|
179
179
|
|
|
180
180
|
export function runSetup({ args = [], env = process.env, platform = process.platform, root = ROOT, run = execute, resolve = resolveCodexBin } = {}) {
|
|
181
181
|
const options = setupArguments(args);
|
|
182
|
-
if (options.help) return { help:
|
|
182
|
+
if (options.help) return { help: `Usage: codex-bridge-setup [--check|--dry-run] [--json] [--no-download]\nUpdate the package and apply managed settings with: ${platform === "win32" ? "npx.cmd" : "npx"} --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest -- codex-bridge-update${platform === "win32" ? " --user" : ""}\nThe updater installs the package with lifecycle scripts disabled, then directly configures Claude Desktop, Claude Code, Codex, Antigravity and the independent worker relay. CODEX_BRIDGE_SKIP_SETUP=1 opts out. Saved configuration, background relay health and loaded native app connections are verified separately.` };
|
|
183
183
|
if (options.postinstall && !automaticSetupEnabled(env)) return { skipped: true, reason: "Automatic configuration runs only for a global npm install; use codex-bridge-setup for manual setup." };
|
|
184
184
|
if (options.check) return checkSetup({ env, platform, root, run, resolve });
|
|
185
185
|
const native = platform === "win32" || platform === "darwin";
|