@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.
Files changed (50) hide show
  1. package/.codex-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +58 -0
  3. package/README.md +81 -32
  4. package/docs/new-macos-setup.md +47 -21
  5. package/docs/new-windows-setup.md +34 -9
  6. package/docs/project-onboarding.md +21 -8
  7. package/package.json +2 -1
  8. package/plugin.json +1 -1
  9. package/scripts/background-relay-hidden.vbs +11 -0
  10. package/scripts/install-antigravity-bridge.mjs +65 -23
  11. package/scripts/install-background-relay.mjs +428 -0
  12. package/scripts/install-claude-code.mjs +2 -2
  13. package/scripts/install-claude-desktop.mjs +12 -7
  14. package/scripts/install-native-relay.mjs +2 -2
  15. package/scripts/setup-background-relay.mjs +58 -0
  16. package/scripts/setup.mjs +3 -3
  17. package/scripts/worker-relay.mjs +41 -0
  18. package/src/antigravity-bridge.mjs +96 -27
  19. package/src/antigravity-cli.mjs +13 -2
  20. package/src/antigravity-deliveries.mjs +32 -8
  21. package/src/antigravity-ipc.mjs +18 -9
  22. package/src/antigravity-outbound-bridge.mjs +71 -23
  23. package/src/antigravity-outbound-targets.mjs +100 -10
  24. package/src/background-antigravity-worker.mjs +192 -0
  25. package/src/background-claude-worker.mjs +240 -0
  26. package/src/background-codex-worker.mjs +344 -0
  27. package/src/background-relay-client.mjs +107 -0
  28. package/src/background-relay-config.mjs +98 -0
  29. package/src/background-relay-daemon.mjs +60 -0
  30. package/src/background-relay-mcp.mjs +45 -0
  31. package/src/background-relay-protocol.mjs +54 -0
  32. package/src/background-relay-registration.mjs +198 -0
  33. package/src/background-relay-service.mjs +311 -0
  34. package/src/background-relay-storage.mjs +203 -0
  35. package/src/background-relay-storage.ps1 +64 -0
  36. package/src/background-relay.mjs +373 -0
  37. package/src/bridge-readiness.mjs +2 -2
  38. package/src/claude-bridge.mjs +43 -25
  39. package/src/claude-code-registration.mjs +2 -1
  40. package/src/claude-session-router.mjs +8 -15
  41. package/src/index.mjs +39 -22
  42. package/src/mcp-supervisor.mjs +26 -16
  43. package/src/native-relay-companion.mjs +4 -1
  44. package/src/native-relay.mjs +5 -1
  45. package/src/peer-protocol.mjs +9 -6
  46. package/src/recipient-preflight.mjs +81 -0
  47. package/src/reload-control.mjs +2 -2
  48. package/src/setup.mjs +62 -25
  49. package/src/thread-delivery.mjs +10 -5
  50. 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 automatically:
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. Append
12
- `--foreground-scripts` to see progress. If npm prints
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. See [installation](../README.md#installation).
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
- ## Optional shared project policy
41
+ ## Shared project policy
35
42
 
36
- Both bridge directions can read the same optional policy file via
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.25.2",
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.25.2",
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
- return { file, text, value: object(JSON.parse(text), file), mode: opened.mode & 0o777, dev: opened.dev, ino: opened.ino };
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
- if (!Array.isArray(entry.args) || entry.args.length !== 1 || typeof script !== "string") {
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
- if (path.resolve(script) === path.resolve(expected)) return;
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 (!retarget || !path.isAbsolute(script) || script.includes("\0") || path.basename(script) !== path.basename(expected)
66
- || path.basename(source) !== "src" || path.basename(path.dirname(source)).toLowerCase() !== "codex-mcp-bridge") {
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
- return { name: NAME, targets: [...new Set(targets)], remove, edits, notices, codex, entry, sidecarEntry, outboundEntry };
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 && options.targets.includes("desktop")) console.log("In Antigravity Customizations > Installed, find agent-bridge and choose More actions > Restart. If it is absent, reopen the App to load its new registration. In the CLI, use /mcp to reload server configurations.");
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