@minhspark/codex-mcp-bridge 1.25.2 → 1.26.0
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 +58 -0
- package/README.md +81 -32
- package/docs/new-macos-setup.md +47 -21
- package/docs/new-windows-setup.md +34 -9
- package/docs/project-onboarding.md +21 -8
- package/package.json +2 -1
- package/plugin.json +1 -1
- package/scripts/background-relay-hidden.vbs +11 -0
- package/scripts/install-antigravity-bridge.mjs +65 -23
- package/scripts/install-background-relay.mjs +428 -0
- package/scripts/install-claude-code.mjs +2 -2
- package/scripts/install-claude-desktop.mjs +12 -7
- package/scripts/install-native-relay.mjs +2 -2
- package/scripts/setup-background-relay.mjs +58 -0
- package/scripts/setup.mjs +3 -3
- package/scripts/worker-relay.mjs +41 -0
- package/src/antigravity-bridge.mjs +96 -27
- package/src/antigravity-cli.mjs +13 -2
- package/src/antigravity-deliveries.mjs +32 -8
- package/src/antigravity-ipc.mjs +18 -9
- package/src/antigravity-outbound-bridge.mjs +71 -23
- package/src/antigravity-outbound-targets.mjs +100 -10
- package/src/background-antigravity-worker.mjs +192 -0
- package/src/background-claude-worker.mjs +240 -0
- package/src/background-codex-worker.mjs +344 -0
- package/src/background-relay-client.mjs +107 -0
- package/src/background-relay-config.mjs +98 -0
- package/src/background-relay-daemon.mjs +60 -0
- package/src/background-relay-mcp.mjs +45 -0
- package/src/background-relay-protocol.mjs +54 -0
- package/src/background-relay-registration.mjs +198 -0
- package/src/background-relay-service.mjs +311 -0
- package/src/background-relay-storage.mjs +203 -0
- package/src/background-relay-storage.ps1 +64 -0
- package/src/background-relay.mjs +373 -0
- package/src/bridge-readiness.mjs +2 -2
- package/src/claude-bridge.mjs +43 -25
- package/src/claude-code-registration.mjs +2 -1
- package/src/claude-session-router.mjs +8 -15
- package/src/index.mjs +39 -22
- package/src/mcp-supervisor.mjs +26 -16
- package/src/native-relay-companion.mjs +4 -1
- package/src/native-relay.mjs +5 -1
- package/src/peer-protocol.mjs +9 -6
- package/src/recipient-preflight.mjs +81 -0
- package/src/reload-control.mjs +2 -2
- package/src/setup.mjs +62 -25
- package/src/thread-delivery.mjs +10 -5
- package/src/windows-pipe-acl.ps1 +1 -1
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
# Project onboarding and shared authorization
|
|
2
2
|
|
|
3
|
-
The recommended global installation registers the bridges
|
|
3
|
+
The recommended global installation registers the bridges and configures the
|
|
4
|
+
independent worker relay automatically:
|
|
4
5
|
|
|
5
6
|
```sh
|
|
6
|
-
npm install -g @minhspark/codex-mcp-bridge@latest --allow-scripts=@minhspark/codex-mcp-bridge
|
|
7
|
+
npm install -g @minhspark/codex-mcp-bridge@latest --allow-scripts=@minhspark/codex-mcp-bridge --foreground-scripts
|
|
7
8
|
```
|
|
8
9
|
|
|
9
10
|
In Windows PowerShell use `npm.cmd`. The script allowance authorizes only this
|
|
10
11
|
package's setup hook; npm versions that block install scripts by default need
|
|
11
|
-
it. A plain install runs setup only when the hook is allowed.
|
|
12
|
-
`--foreground-scripts`
|
|
12
|
+
it. A plain install runs setup only when the hook is allowed.
|
|
13
|
+
`--foreground-scripts` displays progress. If npm prints
|
|
13
14
|
`1 package had install scripts blocked`, rerun the recommended command or run
|
|
14
15
|
`codex-bridge-setup` explicitly. The setup creates
|
|
15
16
|
`codex-bridge` for Claude Code and the separate `codex-bridge-desktop` shared
|
|
16
17
|
Claude connector, configures Codex and Antigravity, and initializes the native
|
|
17
|
-
relay on Windows/macOS.
|
|
18
|
+
relay on Windows/macOS. It also installs the per-user background service and
|
|
19
|
+
worker communication settings, including Antigravity's four scoped relay tool
|
|
20
|
+
grants. See [installation](../README.md#installation).
|
|
18
21
|
|
|
19
22
|
Setup preserves existing scope and permission choices. Registration does not
|
|
20
23
|
grant workspace trust, authorize cross-project work, or establish a live
|
|
21
24
|
connection. The shared Desktop connector uses local app-server sessions;
|
|
22
25
|
native Codex Desktop handoffs require a verified Claude Code sender in the
|
|
23
|
-
same saved project.
|
|
26
|
+
same saved project. Independent CLI workers require their own provider
|
|
27
|
+
authentication and project authorization; they can communicate while Desktop
|
|
28
|
+
apps are closed or frozen. A frozen UI still cannot accept new user input or
|
|
29
|
+
display results. Use a terminal or another responsive client, subject to the
|
|
30
|
+
[background worker prerequisites](../README.md#independent-background-workers).
|
|
24
31
|
|
|
25
32
|
`codex-bridge-setup --check` inspects all saved registrations. Use
|
|
26
33
|
`codex-bridge-setup --dry-run` to preview or rerun `codex-bridge-setup` to apply
|
|
@@ -31,15 +38,21 @@ If app configuration is incomplete, npm retains the package for repair; its
|
|
|
31
38
|
success exit alone does not verify setup. Explicit setup and `--check` return
|
|
32
39
|
a nonzero status until the saved configuration passes verification.
|
|
33
40
|
|
|
34
|
-
##
|
|
41
|
+
## Shared project policy
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
Native Codex/Claude handoffs can read the same optional policy file via
|
|
37
44
|
`CODEX_BRIDGE_PROJECT_POLICY`. `bridge-projects` defaults to
|
|
38
45
|
`~/.config/GptClaudeBridge/projects.json`. A shared home-directory path avoids
|
|
39
46
|
different AppData views in packaged Windows applications. The policy stores
|
|
40
47
|
canonical directory identities; do not copy it to a new machine as proof of
|
|
41
48
|
authorization there.
|
|
42
49
|
|
|
50
|
+
Independent background dispatch always requires a matching explicit project
|
|
51
|
+
or parent grant from the shared
|
|
52
|
+
policy; setup preserves grants and revocations rather than authorizing a new
|
|
53
|
+
project. Use `bridge-projects allow-project <absolute-project-path>` for a
|
|
54
|
+
selected project (`bridge-projects.cmd` on Windows), or the source commands below.
|
|
55
|
+
|
|
43
56
|
```sh
|
|
44
57
|
node scripts/bridge-projects.mjs allow-project /absolute/projects/example
|
|
45
58
|
node scripts/bridge-projects.mjs allow-parent /absolute/projects
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minhspark/codex-mcp-bridge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
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",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"codex-mcp-bridge-install": "scripts/install-claude-desktop.mjs",
|
|
31
31
|
"codex-bridge-code-install": "scripts/install-claude-code.mjs",
|
|
32
32
|
"codex-bridge-setup": "scripts/setup.mjs",
|
|
33
|
+
"worker-relay": "scripts/worker-relay.mjs",
|
|
33
34
|
"bridge-projects": "scripts/bridge-projects.mjs",
|
|
34
35
|
"claude-mcp-bridge-install": "scripts/install-codex-mcp.mjs",
|
|
35
36
|
"codex-native-relay": "src/native-relay-launcher.mjs",
|
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.
|
|
4
|
+
"version": "1.26.0",
|
|
5
5
|
"description": "Connect ChatGPT to your own Codex Desktop projects and conversations.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Bui Dang Minh",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Option Explicit
|
|
2
|
+
If WScript.Arguments.Count <> 3 Then WScript.Quit 2
|
|
3
|
+
Dim relayShell, relayEnvironment, relayNode, relayService
|
|
4
|
+
Set relayShell = CreateObject("WScript.Shell")
|
|
5
|
+
Set relayEnvironment = relayShell.Environment("PROCESS")
|
|
6
|
+
relayNode = WScript.Arguments(0)
|
|
7
|
+
relayService = WScript.Arguments(1)
|
|
8
|
+
If InStr(relayNode, Chr(34)) > 0 Or InStr(relayNode, "%") > 0 Then WScript.Quit 2
|
|
9
|
+
If InStr(relayService, Chr(34)) > 0 Or InStr(relayService, "%") > 0 Then WScript.Quit 2
|
|
10
|
+
relayEnvironment("AGENT_RELAY_DIRECTORY") = WScript.Arguments(2)
|
|
11
|
+
WScript.Quit relayShell.Run(Chr(34) & relayNode & Chr(34) & " " & Chr(34) & relayService & Chr(34), 0, True)
|
|
@@ -7,6 +7,7 @@ import { applyStdioMcpRegistration, stdioMcpRegistration } from "../src/codex-mc
|
|
|
7
7
|
import { isMainModule, stableInstallPath, stableNodeBin } from "../src/install-paths.mjs";
|
|
8
8
|
import { homeDir, resolveCodexBin, spawnEnv } from "../src/platform.mjs";
|
|
9
9
|
import { exitForVersionRequest } from "../src/cli-version.mjs";
|
|
10
|
+
import { createReleaseSnapshot, snapshotRoot } from "../src/release-snapshot.mjs";
|
|
10
11
|
|
|
11
12
|
const NAME = "antigravity-bridge";
|
|
12
13
|
const SIDECAR = "codex-bridge";
|
|
@@ -17,7 +18,7 @@ function object(value, label) {
|
|
|
17
18
|
return value;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
function snapshot(file) {
|
|
21
|
+
function snapshot(file, jsonLabel) {
|
|
21
22
|
let descriptor;
|
|
22
23
|
try { descriptor = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0)); }
|
|
23
24
|
catch (error) {
|
|
@@ -41,7 +42,13 @@ function snapshot(file) {
|
|
|
41
42
|
}
|
|
42
43
|
if (length !== opened.size || !matches(opened, fs.fstatSync(descriptor)) || !matches(opened, fs.lstatSync(file))) throw new Error(`Configuration changed while reading: ${file}`);
|
|
43
44
|
const text = buffer.subarray(0, length).toString("utf8");
|
|
44
|
-
|
|
45
|
+
let value;
|
|
46
|
+
try { value = object(JSON.parse(text), file); }
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (jsonLabel) throw new Error(`Invalid ${jsonLabel}: expected a JSON object.`);
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
return { file, text, value, ...(jsonLabel ? { jsonLabel } : {}), mode: opened.mode & 0o777, dev: opened.dev, ino: opened.ino };
|
|
45
52
|
} finally { fs.closeSync(descriptor); }
|
|
46
53
|
}
|
|
47
54
|
|
|
@@ -53,17 +60,21 @@ export function validateCodexExecutable(executable, { platform = process.platfor
|
|
|
53
60
|
return resolved;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
|
-
function ownEntry(entry, expected, label, retarget = false) {
|
|
63
|
+
function ownEntry(entry, expected, label, retarget = false, supervised = false) {
|
|
57
64
|
if (entry === undefined || entry === null) return;
|
|
58
65
|
object(entry, label);
|
|
59
66
|
const script = entry.args?.[0];
|
|
60
|
-
|
|
67
|
+
const managed = supervised && Array.isArray(entry.args) && entry.args.length === 2
|
|
68
|
+
&& typeof script === "string" && path.basename(script) === "mcp-supervisor.mjs" && entry.args[1] === path.basename(expected);
|
|
69
|
+
if (!Array.isArray(entry.args) || !managed && entry.args.length !== 1 || typeof script !== "string") {
|
|
61
70
|
throw new Error(`${label} is already configured with a different entrypoint; it was not changed`);
|
|
62
71
|
}
|
|
63
|
-
|
|
72
|
+
const target = managed ? path.join(path.dirname(expected), "mcp-supervisor.mjs") : expected;
|
|
73
|
+
if (path.resolve(script) === path.resolve(target) && !managed) return;
|
|
64
74
|
const source = path.dirname(script);
|
|
65
|
-
if (!
|
|
66
|
-
|| path.basename(
|
|
75
|
+
if (!path.isAbsolute(script) || script.includes("\0") || path.resolve(script) !== path.resolve(target)
|
|
76
|
+
&& (!retarget || path.basename(script) !== path.basename(target)
|
|
77
|
+
|| path.basename(source) !== "src" || path.basename(path.dirname(source)).toLowerCase() !== "codex-mcp-bridge")) {
|
|
67
78
|
throw new Error(`${label} is already configured with a different entrypoint; it was not changed`);
|
|
68
79
|
}
|
|
69
80
|
const command = entry.command;
|
|
@@ -79,36 +90,41 @@ function changed(original, value) {
|
|
|
79
90
|
return { ...original, next: text };
|
|
80
91
|
}
|
|
81
92
|
|
|
82
|
-
export function planAntigravityInstallation({ home = homeDir(), codeConfigDir = process.env.CLAUDE_CONFIG_DIR || home, root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."), node = stableNodeBin(), targets = ["codex", "claude", "desktop"], remove = false, setup = false, codexExisting = null } = {}) {
|
|
93
|
+
export function planAntigravityInstallation({ home = homeDir(), codeConfigDir = process.env.CLAUDE_CONFIG_DIR || home, root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."), node = stableNodeBin(), targets = ["codex", "claude", "desktop"], remove = false, setup = false, codexExisting = null, platform = process.platform, appData = process.env.APPDATA } = {}) {
|
|
83
94
|
if (!path.isAbsolute(home) || !path.isAbsolute(codeConfigDir) || !path.isAbsolute(root) || !path.isAbsolute(node)) throw new Error("Installer paths must be absolute");
|
|
84
95
|
if (!targets.length || targets.some((target) => !["codex", "claude", "desktop"].includes(target))) throw new Error("Unknown installation target");
|
|
85
96
|
const entry = path.join(root, "src", "antigravity-bridge.mjs");
|
|
86
97
|
const sidecarEntry = path.join(root, "src", "antigravity-sidecar.mjs");
|
|
87
98
|
const outboundEntry = path.join(root, "src", "antigravity-outbound-bridge.mjs");
|
|
99
|
+
const supervisorEntry = path.join(root, "src", "mcp-supervisor.mjs");
|
|
88
100
|
const edits = [];
|
|
89
101
|
const notices = [];
|
|
102
|
+
const runtimeCaches = new Set();
|
|
103
|
+
let backgroundPreference = null;
|
|
90
104
|
const retarget = setup && !remove;
|
|
91
105
|
let codex = null;
|
|
92
106
|
if (targets.includes("codex")) {
|
|
93
107
|
if (codexExisting && codexExisting.transport?.type !== "stdio") throw new Error("The existing Antigravity MCP transport is not stdio");
|
|
94
|
-
ownEntry(codexExisting?.transport, entry, "Codex Antigravity bridge", retarget);
|
|
108
|
+
ownEntry(codexExisting?.transport, entry, "Codex Antigravity bridge", retarget, true);
|
|
95
109
|
const existingCaller = codexExisting?.transport?.env?.ANTIGRAVITY_BRIDGE_CALLER;
|
|
96
110
|
if (existingCaller !== undefined && existingCaller !== "codex") throw new Error("The existing Codex bridge has a conflicting caller identity");
|
|
97
|
-
codex = remove ? { remove: Boolean(codexExisting) } : stdioMcpRegistration({ name: NAME, existing: codexExisting, node, entry, envOverrides: { ANTIGRAVITY_BRIDGE_CALLER: "codex" } });
|
|
111
|
+
codex = remove ? { remove: Boolean(codexExisting) } : stdioMcpRegistration({ name: NAME, existing: codexExisting, node, entry: supervisorEntry, entryArgs: [path.basename(entry)], envOverrides: { ANTIGRAVITY_BRIDGE_CALLER: "codex" } });
|
|
112
|
+
if (!remove) runtimeCaches.add(snapshotRoot({ ...process.env, ...codex.environment }));
|
|
98
113
|
}
|
|
99
114
|
if (targets.includes("claude")) {
|
|
100
115
|
const original = snapshot(path.join(codeConfigDir, ".claude.json"));
|
|
101
116
|
const value = structuredClone(original.value);
|
|
102
117
|
if (value.mcpServers !== undefined) object(value.mcpServers, "Claude mcpServers");
|
|
103
118
|
const existing = value.mcpServers?.[NAME];
|
|
104
|
-
ownEntry(existing, entry, "Claude Antigravity bridge", retarget);
|
|
119
|
+
ownEntry(existing, entry, "Claude Antigravity bridge", retarget, true);
|
|
105
120
|
if (existing?.type !== undefined && existing.type !== "stdio") throw new Error("The existing Claude bridge transport is not stdio");
|
|
106
121
|
if (existing?.env !== undefined) object(existing.env, "Claude bridge environment");
|
|
107
122
|
if (existing?.env?.ANTIGRAVITY_BRIDGE_CALLER !== undefined && existing.env.ANTIGRAVITY_BRIDGE_CALLER !== "claude") throw new Error("The existing Claude bridge has a conflicting caller identity");
|
|
108
123
|
if (remove) { if (existing) delete value.mcpServers[NAME]; }
|
|
109
124
|
else {
|
|
110
125
|
value.mcpServers ??= {};
|
|
111
|
-
value.mcpServers[NAME] = { ...existing, type: "stdio", command: node, args: [entry], env: { ...existing?.env, ANTIGRAVITY_BRIDGE_CALLER: "claude" } };
|
|
126
|
+
value.mcpServers[NAME] = { ...existing, type: "stdio", command: node, args: [supervisorEntry, path.basename(entry)], env: { ...existing?.env, ANTIGRAVITY_BRIDGE_CALLER: "claude" } };
|
|
127
|
+
runtimeCaches.add(snapshotRoot({ ...process.env, ...value.mcpServers[NAME].env }));
|
|
112
128
|
}
|
|
113
129
|
const edit = changed(original, value);
|
|
114
130
|
if (edit) edits.push(edit);
|
|
@@ -119,7 +135,7 @@ export function planAntigravityInstallation({ home = homeDir(), codeConfigDir =
|
|
|
119
135
|
const mcp = structuredClone(originalMcp.value);
|
|
120
136
|
if (mcp.mcpServers !== undefined) object(mcp.mcpServers, "Antigravity mcpServers");
|
|
121
137
|
const outbound = mcp.mcpServers?.[OUTBOUND];
|
|
122
|
-
ownEntry(outbound, outboundEntry, "Antigravity outbound bridge", retarget);
|
|
138
|
+
ownEntry(outbound, outboundEntry, "Antigravity outbound bridge", retarget, true);
|
|
123
139
|
if (outbound?.type !== undefined && outbound.type !== "stdio" || outbound?.url !== undefined || outbound?.serverUrl !== undefined || outbound?.httpUrl !== undefined) throw new Error("The existing Antigravity outbound bridge transport is not stdio");
|
|
124
140
|
if (outbound?.env !== undefined) object(outbound.env, "Antigravity outbound bridge environment");
|
|
125
141
|
if (Object.keys(outbound?.env ?? {}).some((name) => /^(?:CLAUDE|CODEX_|ANTIGRAVITY_BRIDGE_CALLER$)/i.test(name))) throw new Error("The existing Antigravity outbound bridge contains a foreign caller environment; review it before installation");
|
|
@@ -129,7 +145,8 @@ export function planAntigravityInstallation({ home = homeDir(), codeConfigDir =
|
|
|
129
145
|
if (remove) { if (outbound) delete mcp.mcpServers[OUTBOUND]; }
|
|
130
146
|
else {
|
|
131
147
|
mcp.mcpServers ??= {};
|
|
132
|
-
mcp.mcpServers[OUTBOUND] = { ...outbound, command: node, args: [outboundEntry] };
|
|
148
|
+
mcp.mcpServers[OUTBOUND] = { ...outbound, command: node, args: [supervisorEntry, path.basename(outboundEntry)] };
|
|
149
|
+
runtimeCaches.add(snapshotRoot({ ...process.env, ...outbound?.env }));
|
|
133
150
|
}
|
|
134
151
|
const mcpEdit = changed(originalMcp, mcp);
|
|
135
152
|
if (mcpEdit) edits.push(mcpEdit);
|
|
@@ -154,12 +171,31 @@ export function planAntigravityInstallation({ home = homeDir(), codeConfigDir =
|
|
|
154
171
|
const configEdit = changed(originalConfig, config);
|
|
155
172
|
if (sidecarEdit) edits.push(sidecarEdit);
|
|
156
173
|
if (configEdit) edits.push(configEdit);
|
|
174
|
+
if (setup && !remove) {
|
|
175
|
+
backgroundPreference = { setting: "runInBackground", status: "unavailable", liveVerified: false };
|
|
176
|
+
if (platform !== "win32") {
|
|
177
|
+
backgroundPreference.detail = "Automatic Antigravity background preferences are not verified on this platform; native settings were preserved.";
|
|
178
|
+
} else if (typeof appData !== "string" || !path.isAbsolute(appData)) {
|
|
179
|
+
backgroundPreference.detail = "Antigravity background preferences were not applied because an absolute APPDATA path is unavailable.";
|
|
180
|
+
} else {
|
|
181
|
+
const storage = snapshot(path.join(appData, "Antigravity", "app_storage.json"), "Antigravity native preferences");
|
|
182
|
+
if (storage.text === null) {
|
|
183
|
+
backgroundPreference.detail = "Antigravity app_storage.json was not found; no native preferences file was created and background operation is unverified.";
|
|
184
|
+
} else {
|
|
185
|
+
const storageEdit = changed(storage, { ...storage.value, runInBackground: "true" });
|
|
186
|
+
if (storageEdit) edits.push(storageEdit);
|
|
187
|
+
backgroundPreference.status = storageEdit ? "planned" : "configured";
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (backgroundPreference.detail) notices.push(backgroundPreference.detail);
|
|
191
|
+
}
|
|
157
192
|
}
|
|
158
|
-
|
|
193
|
+
if (!remove) notices.push("Running direct Antigravity MCP processes retain their current version until the host reconnects normally. Future supervised workers update automatically when quiescent.");
|
|
194
|
+
return { name: NAME, targets: [...new Set(targets)], remove, edits, notices, codex, entry, sidecarEntry, outboundEntry, supervisorEntry, runtimeCaches: [...runtimeCaches], backgroundPreference };
|
|
159
195
|
}
|
|
160
196
|
|
|
161
197
|
function assertCurrent(edit) {
|
|
162
|
-
const current = snapshot(edit.file);
|
|
198
|
+
const current = snapshot(edit.file, edit.jsonLabel);
|
|
163
199
|
if (current.text !== edit.text || current.dev !== edit.dev || current.ino !== edit.ino) throw new Error(`Configuration changed while preparing installation: ${edit.file}`);
|
|
164
200
|
}
|
|
165
201
|
|
|
@@ -180,11 +216,11 @@ export function applyJsonEdits(edits) {
|
|
|
180
216
|
} finally { if (fs.existsSync(temporary)) fs.unlinkSync(temporary); }
|
|
181
217
|
}
|
|
182
218
|
completed.push(edit);
|
|
183
|
-
if (snapshot(edit.file).text !== edit.next) throw new Error(`Configuration readback did not match the saved update: ${edit.file}`);
|
|
219
|
+
if (snapshot(edit.file, edit.jsonLabel).text !== edit.next) throw new Error(`Configuration readback did not match the saved update: ${edit.file}`);
|
|
184
220
|
}
|
|
185
221
|
} catch (error) {
|
|
186
222
|
for (const edit of completed.reverse()) {
|
|
187
|
-
if (snapshot(edit.file).text !== edit.next) continue;
|
|
223
|
+
if (snapshot(edit.file, edit.jsonLabel).text !== edit.next) continue;
|
|
188
224
|
if (edit.text === null) fs.unlinkSync(edit.file);
|
|
189
225
|
else fs.writeFileSync(edit.file, edit.text, { mode: edit.mode ?? 0o600 });
|
|
190
226
|
}
|
|
@@ -199,7 +235,7 @@ export function installerArguments(args) {
|
|
|
199
235
|
return { targets: args.includes("--all") || !selected.length ? ["codex", "claude", "desktop"] : selected, remove: args.includes("--remove"), dryRun: args.includes("--dry-run"), setup: args.includes("--setup"), help: args.includes("--help") };
|
|
200
236
|
}
|
|
201
237
|
|
|
202
|
-
export function runAntigravityInstaller(args = process.argv.slice(2)) {
|
|
238
|
+
export function runAntigravityInstaller(args = process.argv.slice(2), { prepareRuntime = createReleaseSnapshot } = {}) {
|
|
203
239
|
const options = installerArguments(args);
|
|
204
240
|
if (options.help) {
|
|
205
241
|
console.log("Usage: antigravity-mcp-bridge-install [--all|--codex|--claude|--desktop] [--remove] [--dry-run] [--setup]");
|
|
@@ -219,24 +255,30 @@ export function runAntigravityInstaller(args = process.argv.slice(2)) {
|
|
|
219
255
|
}
|
|
220
256
|
const plan = planAntigravityInstallation({ ...options, home, root, node, codexExisting });
|
|
221
257
|
if (!options.remove) {
|
|
222
|
-
for (const entry of [plan.entry, ...(options.targets.includes("desktop") ? [plan.sidecarEntry, plan.outboundEntry] : [])]) {
|
|
258
|
+
for (const entry of [plan.supervisorEntry, plan.entry, ...(options.targets.includes("desktop") ? [plan.sidecarEntry, plan.outboundEntry] : [])]) {
|
|
223
259
|
if (!fs.existsSync(entry)) throw new Error(`Missing bridge entrypoint: ${entry}`);
|
|
224
260
|
}
|
|
225
261
|
}
|
|
226
262
|
if (options.dryRun) {
|
|
227
|
-
console.log(JSON.stringify({ dryRun: true, remove: plan.remove, targets: plan.targets, files: plan.edits.map((edit) => ({ path: edit.file, action: edit.next === null ? "remove" : "write" })), notices: plan.notices, codex: plan.codex ? { name: NAME, action: options.remove ? "remove" : "register" } : null }, null, 2));
|
|
263
|
+
console.log(JSON.stringify({ dryRun: true, remove: plan.remove, targets: plan.targets, files: plan.edits.map((edit) => ({ path: edit.file, action: edit.next === null ? "remove" : "write" })), notices: plan.notices, backgroundPreference: plan.backgroundPreference, codex: plan.codex ? { name: NAME, action: options.remove ? "remove" : "register" } : null }, null, 2));
|
|
228
264
|
return plan;
|
|
229
265
|
}
|
|
266
|
+
for (const cache of plan.runtimeCaches) prepareRuntime(root, { cache });
|
|
230
267
|
for (const edit of plan.edits) assertCurrent(edit);
|
|
231
268
|
if (plan.codex) {
|
|
232
269
|
if (options.remove) { if (plan.codex.remove) run(["mcp", "remove", NAME]); }
|
|
233
270
|
else applyStdioMcpRegistration({ name: NAME, registration: plan.codex, existing: codexExisting, run, configPath: path.join(process.env.CODEX_HOME ?? path.join(home, ".codex"), "config.toml") });
|
|
234
271
|
}
|
|
235
272
|
applyJsonEdits(plan.edits);
|
|
273
|
+
if (plan.backgroundPreference?.status === "planned") plan.backgroundPreference.status = "configured";
|
|
274
|
+
if (options.setup) {
|
|
275
|
+
console.log(JSON.stringify({ action: options.remove ? "remove" : "install", targets: plan.targets, configured: true, liveVerified: false,
|
|
276
|
+
changedFiles: plan.edits.length, notices: plan.notices, backgroundPreference: plan.backgroundPreference }));
|
|
277
|
+
return plan;
|
|
278
|
+
}
|
|
236
279
|
console.log(`${options.remove ? "Removed" : "Installed"} Antigravity bridge: ${plan.targets.join(", ")}.`);
|
|
237
280
|
for (const notice of plan.notices) console.log(notice);
|
|
238
|
-
if (!options.remove
|
|
239
|
-
if (!options.remove) console.log("Reload the selected clients to load the bridge. Antigravity Desktop must launch its sidecar; installation alone does not prove a live connection. Native permissions were preserved.");
|
|
281
|
+
if (!options.remove) console.log("Bridge configuration saved; running clients and native permissions were preserved. Installation does not prove a live connection or load new settings into existing processes. This installer does not start apps or open conversations.");
|
|
240
282
|
return plan;
|
|
241
283
|
}
|
|
242
284
|
|