@minhspark/codex-mcp-bridge 1.26.4 → 1.26.6

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.
@@ -15,25 +15,30 @@ separately authenticated provider CLIs and do not require open Desktop apps.
15
15
  Use the same command for a fresh installation and every update:
16
16
 
17
17
  ```bash
18
- npm exec --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest -- codex-bridge-update
18
+ npx -y @minhspark/codex-mcp-bridge@latest
19
19
  ```
20
20
 
21
- npm downloads the latest updater into its cache. `--ignore-scripts` suppresses
22
- setup only in that temporary location. The updater installs its exact resolved
23
- version globally, enables this package's managed setup hook, displays progress
24
- and verifies the global version and configuration. No second installation or
21
+ Run this directly in an interactive terminal. The bare npm package launch
22
+ selects the complete updater. 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.
28
28
 
29
+ For scripts or redirected input/output, use
30
+ `npx -y --ignore-scripts -p @minhspark/codex-mcp-bridge@latest codex-bridge-update`.
31
+ MCP clients keep using the normal stdio launch; piped launches do not select
32
+ the terminal updater.
33
+
29
34
  Setup registers the local bridges, adds native pipe forwarding, and bootstraps
30
35
  the relay executor. It reuses an available Codex CLI or installs a pinned
31
36
  official package under `~/.config/GptClaudeBridge/tools` (or
32
37
  `XDG_CONFIG_HOME/GptClaudeBridge/tools`). No separate Codex CLI install command
33
38
  is required. Run the install under the account that uses the apps, without `sudo`.
34
39
  If setup or update exits nonzero, inspect the reported component and installed
35
- version, correct the cause, and rerun the same updater command. npm can roll back
36
- package files without restoring settings already written or command shims.
40
+ version, correct the cause, and rerun the same updater command. A completed
41
+ package installation is preserved when managed setup fails.
37
42
 
38
43
  The command also installs an independent per-user LaunchAgent for separate
39
44
  Claude, Codex and Antigravity CLI workers and updates its managed settings on
@@ -101,14 +106,13 @@ a nonzero exit status while configuration is incomplete.
101
106
 
102
107
  Local `npm install` and `npm ci` do not change app settings. For a deliberate
103
108
  package-only installation, a direct global npm install can use
104
- `CODEX_BRIDGE_SKIP_SETUP=1` or `--ignore-scripts`; this differs from the cache-only
105
- script suppression in the recommended updater command. For Homebrew, run
109
+ `CODEX_BRIDGE_SKIP_SETUP=1` or `--ignore-scripts`; the complete updater runs
110
+ managed setup directly after package installation. For Homebrew, run
106
111
  `codex-bridge-setup` after installing the formula. A pinned source checkout can
107
112
  use `node scripts/setup.mjs`.
108
113
 
109
- The [manual macOS commands](../README.md#macos-terminal) and
110
- [Claude Code registration commands](../README.md#register-claude-code) remain
111
- available for selective configuration. `codex-bridge-code-install --install`
114
+ See [project onboarding](project-onboarding.md) for shared authorization and
115
+ selective configuration. `codex-bridge-code-install --install`
112
116
  can create a missing user-level Code entry; its existing policy migration mode
113
117
  is described in [project onboarding](project-onboarding.md). Use intended
114
118
  project paths when setting explicit `CODEX_BRIDGE_ALLOWED_ROOTS`; macOS
@@ -7,15 +7,28 @@ 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
- npm.cmd exec --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest -- codex-bridge-update
10
+ npx.cmd -y @minhspark/codex-mcp-bridge@latest
11
11
  ```
12
12
 
13
- npm downloads the latest updater into its cache. `--ignore-scripts` prevents
14
- setup from running in that temporary location; the updater then installs its
15
- exact resolved version globally with this package's managed setup hook enabled.
16
- It displays progress and verifies the global version and saved settings.
13
+ Run this directly in an interactive terminal. The bare npm package launch
14
+ selects the complete updater and defaults to `%APPDATA%\npm` on Windows instead
15
+ of a protected Node installation directory such as `Program Files`;
16
+ administrator rights are not required. The updater installs its exact resolved
17
+ version globally with lifecycle scripts disabled, then runs managed setup
18
+ directly using Node. It displays progress and verifies the global version,
19
+ saved settings and relay activation.
17
20
  No second installation or setup command is required.
18
21
 
22
+ For scripts or redirected input/output, select the updater explicitly:
23
+
24
+ ```powershell
25
+ npx.cmd -y --ignore-scripts -p @minhspark/codex-mcp-bridge@latest codex-bridge-update --user
26
+ ```
27
+
28
+ MCP clients keep using the package's normal stdio launch. Piped launches do not
29
+ select the terminal updater. An installed package also accepts
30
+ `codex-mcp-bridge.cmd update` for an explicit update from that package version.
31
+
19
32
  An older Windows background service can keep the global npm package directory
20
33
  locked as its working directory. Before replacing the package, the updater
21
34
  moves only this package's owned legacy service to newer code using private relay
@@ -33,8 +46,8 @@ official package in the current user's `.config\GptClaudeBridge\tools`
33
46
  directory. It also configures native pipe forwarding and bootstraps the relay
34
47
  executor. A failed component is reported as incomplete rather than ready. If
35
48
  setup or update exits nonzero, inspect the reported component and installed
36
- version, correct the cause, and rerun the same updater command. npm can roll back
37
- package files without restoring settings already written or command shims.
49
+ version, correct the cause, and rerun the same updater command. A completed
50
+ package installation is preserved when managed setup fails.
38
51
 
39
52
  The same command installs the independent worker relay as a hidden per-user
40
53
  scheduled task and applies its managed settings on every update. It registers
@@ -104,8 +117,8 @@ attempting delivery.
104
117
  Local `npm install` and `npm ci` do not configure apps. For a deliberate
105
118
  package-only installation, a direct global npm install can use
106
119
  `CODEX_BRIDGE_SKIP_SETUP=1` or `--ignore-scripts`. This differs from the
107
- recommended updater command, which suppresses setup only in its temporary cache
108
- and enables it for the final global installation. A direct install skips the
120
+ recommended updater command, which disables lifecycle scripts and runs managed
121
+ setup directly after the final global installation. A direct install skips the
109
122
  updater's migration of legacy Windows services that can lock the package.
110
123
  The optional card extension, hosted ChatGPT pairing, and VS Code preview have
111
124
  separate installation steps.
@@ -4,15 +4,19 @@ One command installs or updates the global package, registers the bridges and
4
4
  configures the independent worker relay automatically:
5
5
 
6
6
  ```sh
7
- npm exec --yes --ignore-scripts --package=@minhspark/codex-mcp-bridge@latest -- codex-bridge-update
7
+ npx -y @minhspark/codex-mcp-bridge@latest
8
8
  ```
9
9
 
10
- In Windows PowerShell use `npm.cmd`. npm fetches the latest updater into its
11
- cache with setup disabled there. The updater moves an owned legacy service to
10
+ Run it directly in an interactive terminal. In Windows PowerShell use
11
+ `npx.cmd -y @minhspark/codex-mcp-bridge@latest`; it selects the per-user npm
12
+ prefix without administrator rights. For scripts or redirected terminals, use
13
+ the explicit updater in the [Windows](new-windows-setup.md) or
14
+ [macOS](new-macos-setup.md) guide. Normal MCP client launches keep serving stdio.
15
+ The updater moves an owned legacy service to
12
16
  newer code using private relay storage as its working directory when needed,
13
17
  temporarily using the downloaded updater package. It waits for active work to
14
- drain, then installs its exact resolved version globally with the setup hook
15
- enabled. It verifies the global installation and managed settings. No second
18
+ drain, then installs its exact resolved version globally with lifecycle scripts
19
+ disabled and runs managed setup directly using Node. It verifies the global installation and managed settings. No second
16
20
  installation command is required. It does not open apps or chats or kill
17
21
  processes. The setup creates
18
22
  `codex-bridge` for Claude Code and the separate `codex-bridge-desktop` shared
@@ -37,10 +41,10 @@ setup after correcting an incomplete component. Add `.cmd` on Windows and
37
41
  `--json` for structured output. Local package installs do not run setup.
38
42
  For a deliberate package-only direct global installation,
39
43
  `CODEX_BRIDGE_SKIP_SETUP=1` or npm `--ignore-scripts` opts out of setup; this is
40
- separate from the cache-only script suppression in the recommended updater.
44
+ separate from the complete updater, which runs managed setup directly after installation.
41
45
  Failed setup or update returns a nonzero exit. Inspect the reported component
42
46
  and installed version, correct the cause, then rerun the same updater command.
43
- npm can roll back package files without restoring settings or command shims.
47
+ A completed package installation is preserved when managed setup fails.
44
48
  Explicit setup and `--check` also return a nonzero status until the saved
45
49
  configuration passes verification.
46
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhspark/codex-mcp-bridge",
3
- "version": "1.26.4",
3
+ "version": "1.26.6",
4
4
  "description": "Connect Claude, Codex, and Antigravity through MCP, with native Desktop handoffs and bridge-owned Antigravity CLI sessions.",
5
5
  "keywords": [
6
6
  "mcp",
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",
4
+ "version": "1.26.6",
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.4";
28
+ const VERSION = "1.26.6";
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"];
@@ -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 code = match?.[1] ?? (/^[A-Z][A-Z0-9_]{0,79}$/.test(result?.error?.code ?? "") ? result.error.code : null);
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
- return failure(code === "EBUSY" ? "UPDATE_PACKAGE_BUSY" : phase === "verification" ? "UPDATE_SETUP_INCOMPLETE" : "UPDATE_NPM_FAILED",
94
- code === "EBUSY" ? `The installed package is still held open by another process. No process was stopped. ${RETRY}`
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: childEnv, cwd, encoding: "utf8", windowsHide: true,
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,26 +191,27 @@ 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} and applying managed settings.`);
185
- await command([npm, "install", "--global", "--prefix", prefix, `${PACKAGE}@${version}`, "--ignore-scripts=false", `--allow-scripts=${PACKAGE}`, "--foreground-scripts"], "installation");
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
- onProgress("Verifying the global bridge settings and owned relay runtime.");
188
- let setup;
189
- try { setup = JSON.parse(await command([path.join(globalRoot, "scripts", "setup.mjs"), "--check", "--json"], "verification")); }
190
- catch (error) { if (error?.code) throw error; throw failure("UPDATE_SETUP_INCOMPLETE", `Global setup did not return a verifiable result. ${RETRY}`, "verification"); }
191
- if (setup?.configured !== true) throw failure("UPDATE_SETUP_INCOMPLETE", `The global bridge settings are incomplete. ${RETRY}`, "verification");
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
  }
195
213
 
196
- if (isMainModule(process.argv[1], import.meta.url)) {
197
- exitForVersionRequest(import.meta.url);
198
- const args = process.argv.slice(2);
214
+ export async function runUpdateCli(args = process.argv.slice(2)) {
199
215
  try {
200
216
  process.chdir(neutralDirectory([ROOT], process.env));
201
217
  const result = await runUpdate({ args, onProgress: message => process.stderr.write(`${message}\n`) });
@@ -208,3 +224,8 @@ if (isMainModule(process.argv[1], import.meta.url)) {
208
224
  process.exitCode = 1;
209
225
  }
210
226
  }
227
+
228
+ if (isMainModule(process.argv[1], import.meta.url)) {
229
+ exitForVersionRequest(import.meta.url);
230
+ await runUpdateCli();
231
+ }
@@ -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
- || ![path.join(value.packageRoot, "src", "background-relay-service.mjs"), path.join(value.packageRoot, "src", "background-relay-daemon.mjs"), value.nodeExecutable].every((entry) => {
83
- try { return fs.statSync(entry).isFile(); } catch { return false; }
84
- })) throw serviceFailure("SERVICE_RUNTIME_INVALID");
85
- return value;
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 } = {}) {
@@ -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.4";
34
+ const VERSION = "1.26.6";
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;
@@ -1,11 +1,39 @@
1
1
  #!/usr/bin/env node
2
2
  import { runSupervisor } from "./mcp-supervisor.mjs";
3
3
  import { handleTelemetryCommand } from "./telemetry.mjs";
4
- if (process.argv[2] === "setup") {
4
+ import { exitForVersionRequest } from "./cli-version.mjs";
5
+ import { isMainModule } from "./install-paths.mjs";
6
+
7
+ async function applySetup(args) {
5
8
  const { runSetup, printSetup } = await import("./setup.mjs");
6
- try {
7
- const result = runSetup({ args: process.argv.slice(3) });
8
- printSetup(result, { json: process.argv.includes("--json") });
9
- if (result.configured === false && !result.dryRun) process.exitCode = 1;
10
- } catch (error) { process.stderr.write(`Bridge setup: ${error.message}\n`); process.exitCode = 1; }
11
- } else if (!await handleTelemetryCommand(process.argv.slice(2))) await runSupervisor("index.mjs");
9
+ const result = runSetup({ args });
10
+ printSetup(result, { json: args.includes("--json") });
11
+ return result;
12
+ }
13
+
14
+ export async function runLauncher({ args = process.argv.slice(2), env = process.env, platform = process.platform,
15
+ input = process.stdin, output = process.stdout, errorOutput = process.stderr, supervise = runSupervisor,
16
+ telemetry = handleTelemetryCommand, setup = applySetup,
17
+ updateCli = async args => (await import("../scripts/update.mjs")).runUpdateCli(args) } = {}) {
18
+ const terminalUpdate = args.length === 0 && env.npm_command === "exec" && input.isTTY === true && output.isTTY === true;
19
+ if (terminalUpdate || args[0] === "update") {
20
+ const updateArgs = terminalUpdate ? [] : args.slice(1);
21
+ return updateCli(platform === "win32" && !updateArgs.includes("--user") ? ["--user", ...updateArgs] : updateArgs);
22
+ }
23
+ if (args[0] === "setup") {
24
+ try {
25
+ const result = await setup(args.slice(1));
26
+ return result.configured === false && !result.dryRun ? 1 : 0;
27
+ } catch (error) {
28
+ errorOutput.write(`Bridge setup: ${error.message}\n`);
29
+ return 1;
30
+ }
31
+ }
32
+ if (!await telemetry(args)) return supervise("index.mjs");
33
+ }
34
+
35
+ if (isMainModule(process.argv[1], import.meta.url)) {
36
+ exitForVersionRequest(import.meta.url);
37
+ const exitCode = await runLauncher();
38
+ if (Number.isInteger(exitCode)) process.exitCode = exitCode;
39
+ }
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.4";
47
+ const VERSION = "1.26.6";
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.4";
46
+ const VERSION = "1.26.6";
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: "Usage: codex-bridge-setup [--check|--dry-run] [--json] [--no-download]\nUpdate the package and apply managed settings with: npm install -g @minhspark/codex-mcp-bridge@latest --allow-scripts=@minhspark/codex-mcp-bridge --foreground-scripts\nGlobal npm installation configures Claude Desktop, Claude Code, Codex, Antigravity and the independent worker relay automatically. CODEX_BRIDGE_SKIP_SETUP=1 opts out. Saved configuration, background relay health and loaded native app connections are verified separately." };
182
+ if (options.help) return { help: `Usage: codex-bridge-setup [--check|--dry-run] [--json] [--no-download]\nUpdate the package and apply managed settings in an interactive terminal with: ${platform === "win32" ? "npx.cmd" : "npx"} -y @minhspark/codex-mcp-bridge@latest\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";