@codexhost/cli-darwin-x64 0.16.1 → 0.17.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/README.md CHANGED
@@ -7,7 +7,7 @@ Run Pi and Claude Code as first-class external harnesses inside Codex Desktop.
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- npm install -g @codexhost/cli@0.16.1
10
+ npm install -g @codexhost/cli@0.17.0
11
11
  ```
12
12
 
13
13
  Do not install this package directly. npm selects it through the optional dependencies of `@codexhost/cli`.
@@ -36,7 +36,7 @@ The `codexhost` command launches the packaged Rust launcher with:
36
36
 
37
37
  ## Requirements
38
38
 
39
- - Node.js 22 or 24 (Node 20 and older are not supported)
39
+ - Node.js 22.19.0 or later, except 23.x (other releases lack parts of the built-in `node:sqlite` module)
40
40
  - Official ChatGPT/Codex Desktop for macOS, Windows, or Linux
41
41
  - Pi on `PATH` when using the Pi agent
42
42
  - Claude Code installed when using the Claude Code adapter
@@ -32,14 +32,18 @@ License text: licenses/OpenCode-v2-Client-LICENSE.txt
32
32
  License: MIT
33
33
  License text: licenses/OpenCode-SDK-LICENSE.txt
34
34
 
35
- @qoder-ai/qoder-agent-sdk 1.0.39
35
+ @qoder-ai/qoder-agent-sdk 1.0.58
36
36
  License: SEE LICENSE IN LICENSE
37
37
  License text: licenses/Qoder-Agent-SDK-LICENSE.txt
38
38
 
39
- @qodercn-ai/qodercn-agent-sdk 1.0.39
39
+ @qodercn-ai/qodercn-agent-sdk 1.0.58
40
40
  License: SEE LICENSE IN LICENSE
41
41
  License text: licenses/QoderCN-Agent-SDK-LICENSE.txt
42
42
 
43
+ @qwen-code/sdk 0.1.26
44
+ License: Apache-2.0
45
+ License text: licenses/Qwen-Code-SDK-LICENSE.txt
46
+
43
47
  diff 8.0.2
44
48
  License: BSD-3-Clause
45
49
  License text: licenses/diff-LICENSE.txt
@@ -1 +1 @@
1
- {"schemaVersion":1,"version":"0.16.1","distribution":"npm","target":"macos-x64"}
1
+ {"schemaVersion":1,"version":"0.17.0","distribution":"npm","target":"macos-x64"}
@@ -17031,33 +17031,58 @@ var HarnessLaunchSettingsStore = class {
17031
17031
  return path8.join(this.#directory, `${harnessPluginIdSchema.parse(id)}.json`);
17032
17032
  }
17033
17033
  async #read(id) {
17034
+ const file2 = this.#file(id);
17035
+ let text;
17034
17036
  try {
17035
- const value = harnessLaunchPathSchema.parse(JSON.parse(await readFile4(this.#file(id), "utf8")));
17036
- if (!path8.isAbsolute(value))
17037
- throw new Error("Expected an absolute entrypoint path");
17038
- return value;
17037
+ text = await readFile4(file2, "utf8");
17039
17038
  } catch (error51) {
17040
17039
  if (error51 && typeof error51 === "object" && "code" in error51 && error51.code === "ENOENT")
17041
17040
  return null;
17042
- throw new Error("Could not read Harness launch settings");
17041
+ throw new Error(`Could not read Harness launch settings at ${file2}`, { cause: error51 });
17043
17042
  }
17043
+ let parsed;
17044
+ try {
17045
+ parsed = harnessLaunchPathSchema.parse(JSON.parse(text));
17046
+ } catch {
17047
+ }
17048
+ if (parsed === void 0 || !path8.isAbsolute(parsed)) {
17049
+ throw new Error(`Could not read Harness launch settings at ${file2}: expected an absolute installation path; save a new path or reset it`);
17050
+ }
17051
+ return parsed;
17044
17052
  }
17045
- /** Capture the value used to construct this Host's Adapter; changes require a restart. */
17046
- async initialCommand(id) {
17053
+ #snapshot(id) {
17047
17054
  let initial = this.#initial.get(id);
17048
17055
  if (!initial) {
17049
- initial = this.#read(id);
17056
+ initial = this.#read(id).then((value) => ({ path: value }), (error51) => ({ error: error51 instanceof Error ? error51 : new Error(String(error51)) }));
17050
17057
  this.#initial.set(id, initial);
17051
17058
  }
17052
- return await initial ?? void 0;
17059
+ return initial;
17060
+ }
17061
+ /**
17062
+ * Capture the value used to construct this Host's Adapter; changes require a restart. A failed
17063
+ * read is remembered for restart reporting but not reused: the next call reads the file again.
17064
+ */
17065
+ async initialCommand(id) {
17066
+ const cached2 = this.#initial.get(id);
17067
+ if (cached2 && "error" in await cached2)
17068
+ this.#initial.delete(id);
17069
+ const snapshot = await this.#snapshot(id);
17070
+ if ("error" in snapshot)
17071
+ throw snapshot.error;
17072
+ return snapshot.path ?? void 0;
17073
+ }
17074
+ /** The Adapter was built without a readable setting, so any saved value needs a restart. */
17075
+ #restartRequired(snapshot, value) {
17076
+ return "error" in snapshot || value !== snapshot.path;
17053
17077
  }
17054
17078
  async get(id) {
17055
- const initial = await this.initialCommand(id) ?? null;
17079
+ const snapshot = await this.#snapshot(id);
17056
17080
  const value = await this.#read(id);
17057
- return { path: value, restartRequired: value !== initial };
17081
+ return { path: value, restartRequired: this.#restartRequired(snapshot, value) };
17058
17082
  }
17083
+ /** Replaces the file without reading it, so an unreadable file can always be repaired. */
17059
17084
  async set(id, value) {
17060
- const initial = await this.initialCommand(id) ?? null;
17085
+ const snapshot = await this.#snapshot(id);
17061
17086
  const file2 = this.#file(id);
17062
17087
  if (value === null) {
17063
17088
  await rm3(file2, { force: true });
@@ -17078,7 +17103,7 @@ var HarnessLaunchSettingsStore = class {
17078
17103
  await rm3(temporary, { force: true });
17079
17104
  }
17080
17105
  }
17081
- return { path: value, restartRequired: value !== initial };
17106
+ return { path: value, restartRequired: this.#restartRequired(snapshot, value) };
17082
17107
  }
17083
17108
  };
17084
17109
 
@@ -17139,10 +17164,15 @@ function createConsoleHarnesses(roots, environment = process.env) {
17139
17164
  if (seen.has(manifest.id)) continue;
17140
17165
  seen.add(manifest.id);
17141
17166
  const icon = manifest.icon ? await readPluginIcon(plugin.root, manifest.icon).catch(() => null) : null;
17142
- const launchPath = manifest.launchCommand ? await store.get(manifest.id).then(
17143
- (settings) => settings.path,
17144
- () => null
17145
- ) : null;
17167
+ let launchPath = null;
17168
+ let launchPathError = null;
17169
+ if (manifest.launchCommand) {
17170
+ try {
17171
+ launchPath = (await store.get(manifest.id)).path;
17172
+ } catch (error51) {
17173
+ launchPathError = error51 instanceof Error ? error51.message : String(error51);
17174
+ }
17175
+ }
17146
17176
  harnesses.push({
17147
17177
  id: manifest.id,
17148
17178
  name: manifest.name,
@@ -17151,7 +17181,8 @@ function createConsoleHarnesses(roots, environment = process.env) {
17151
17181
  launchCommand: manifest.launchCommand === true,
17152
17182
  links: manifest.links ?? null,
17153
17183
  icon,
17154
- launchPath
17184
+ launchPath,
17185
+ launchPathError
17155
17186
  });
17156
17187
  }
17157
17188
  return harnesses.sort(
@@ -17168,7 +17199,7 @@ function createConsoleHarnesses(roots, environment = process.env) {
17168
17199
  }
17169
17200
  try {
17170
17201
  const settings = await store.set(id, value);
17171
- return { ...harness2, launchPath: settings.path };
17202
+ return { ...harness2, launchPath: settings.path, launchPathError: null };
17172
17203
  } catch (error51) {
17173
17204
  throw new ConsoleHarnessError(400, error51 instanceof Error ? error51.message : String(error51));
17174
17205
  }
@@ -18107,7 +18138,11 @@ function startConsoleServer(options) {
18107
18138
  return;
18108
18139
  }
18109
18140
  scheduleIdleExit();
18110
- const url2 = new URL(request.url ?? "/", `http://127.0.0.1:${port}`);
18141
+ const url2 = URL.parse(request.url ?? "/", `http://127.0.0.1:${port}`);
18142
+ if (!url2) {
18143
+ sendJson(response, 400, { error: "Bad Request" });
18144
+ return;
18145
+ }
18111
18146
  const method = request.method ?? "GET";
18112
18147
  const route = `${method} ${url2.pathname}`;
18113
18148
  if (route === "GET /api/health") {
@@ -18782,13 +18782,12 @@ th[aria-sort] .console-usage-sort__mark {
18782
18782
  { className: "console-harness__path" },
18783
18783
  input,
18784
18784
  button(document2, messages.save, () => submit(input.value.trim() || null)),
18785
- harness2.launchPath ? button(document2, messages.useAutomatic, () => submit(null)) : null
18785
+ harness2.launchPath || harness2.launchPathError ? button(document2, messages.useAutomatic, () => submit(null)) : null
18786
18786
  )
18787
18787
  );
18788
- if (status.has(harness2.id)) {
18789
- row.append(
18790
- h(document2, "div", { className: "console-muted" }, status.get(harness2.id) ?? "")
18791
- );
18788
+ const message2 = status.get(harness2.id) ?? harness2.launchPathError;
18789
+ if (message2) {
18790
+ row.append(h(document2, "div", { className: "console-muted" }, message2));
18792
18791
  }
18793
18792
  }
18794
18793
  if (guide) {
@@ -20965,7 +20964,7 @@ th[aria-sort] .console-usage-sort__mark {
20965
20964
  launchPathAutomatic: "No saved override. Uses environment configuration, then automatic discovery.",
20966
20965
  launchPathSaving: "Saving\u2026",
20967
20966
  launchPathLoading: "Loading launch settings\u2026",
20968
- launchPathLoadError: "Could not load launch settings. Reopen this detail panel to retry.",
20967
+ launchPathLoadError: "Could not load launch settings. Save an installation directory or reset to automatic discovery to replace them.",
20969
20968
  launchPathSaveError: "Could not save. Enter an existing absolute installation folder on this Host and check configuration permissions.",
20970
20969
  connectionOfficialWebsite: "Visit official website",
20971
20970
  connectionOpenHarnessWeb: "Open DeepSeek Harness Web",
@@ -21244,7 +21243,7 @@ th[aria-sort] .console-usage-sort__mark {
21244
21243
  launchPathAutomatic: "\u672A\u8BBE\u7F6E\u81EA\u5B9A\u4E49\u8DEF\u5F84\uFF0C\u4F7F\u7528\u73AF\u5883\u53D8\u91CF\u914D\u7F6E\u6216\u81EA\u52A8\u53D1\u73B0\u3002",
21245
21244
  launchPathSaving: "\u6B63\u5728\u4FDD\u5B58\u2026",
21246
21245
  launchPathLoading: "\u6B63\u5728\u8BFB\u53D6\u542F\u52A8\u8BBE\u7F6E\u2026",
21247
- launchPathLoadError: "\u65E0\u6CD5\u8BFB\u53D6\u542F\u52A8\u8BBE\u7F6E\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6B64\u8BE6\u60C5\u9762\u677F\u91CD\u8BD5\u3002",
21246
+ launchPathLoadError: "\u65E0\u6CD5\u8BFB\u53D6\u542F\u52A8\u8BBE\u7F6E\u3002\u4FDD\u5B58\u5B89\u88C5\u76EE\u5F55\u6216\u6062\u590D\u81EA\u52A8\u53D1\u73B0\u5373\u53EF\u66FF\u6362\u3002",
21248
21247
  launchPathSaveError: "\u4FDD\u5B58\u5931\u8D25\u3002\u8BF7\u586B\u5199\u6B64 Host \u4E0A\u5B9E\u9645\u5B58\u5728\u7684\u5B89\u88C5\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u5E76\u786E\u8BA4\u914D\u7F6E\u76EE\u5F55\u53EF\u5199\u3002",
21249
21248
  connectionOfficialWebsite: "\u8BBF\u95EE\u5B98\u7F51",
21250
21249
  connectionOpenHarnessWeb: "\u6253\u5F00 DeepSeek Harness Web",
@@ -23887,7 +23886,7 @@ ${detail}` : ""}`
23887
23886
  "codexhost was not detected over SSH. Install it on the remote computer or add it to the login shell PATH, then retry": "\u901A\u8FC7 SSH \u672A\u68C0\u6D4B\u5230 codexhost\u3002\u8BF7\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u5B89\u88C5\uFF0C\u6216\u5C06\u5DF2\u5B89\u88C5\u7684 codexhost \u52A0\u5165\u767B\u5F55 shell \u7684 PATH \u540E\u91CD\u8BD5",
23888
23887
  "The active codexhost does not match the global npm installation. Uninstall only the remote service, or remove the package manually on the remote computer": "\u5F53\u524D codexhost \u4E0E npm \u5168\u5C40\u5B89\u88C5\u4F4D\u7F6E\u4E0D\u5339\u914D\u3002\u8BF7\u9009\u62E9\u4EC5\u5378\u8F7D\u8FDC\u7A0B\u670D\u52A1\uFF0C\u6216\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u624B\u52A8\u5378\u8F7D\u8F6F\u4EF6\u5305",
23889
23888
  "The remote service was uninstalled, but removing the codexhost package failed. Check npm permissions on the remote computer and retry": "\u8FDC\u7A0B\u670D\u52A1\u5DF2\u5378\u8F7D\uFF0C\u4F46 codexhost \u8F6F\u4EF6\u5305\u5378\u8F7D\u5931\u8D25\u3002\u8BF7\u68C0\u67E5\u8FDC\u7A0B\u7535\u8111\u4E0A\u7684 npm \u6743\u9650\u540E\u91CD\u8BD5",
23890
- "Remote Node.js is unsupported. Select Node.js 22.19 or later in the 22.x series, or Node.js 24.x": "\u8FDC\u7A0B Node.js \u7248\u672C\u4E0D\u53D7\u652F\u6301\uFF0C\u8BF7\u9009\u62E9 Node.js 22.19 \u53CA\u4EE5\u4E0A\u7684 22.x \u7248\u672C\uFF0C\u6216 Node.js 24.x",
23889
+ "Remote Node.js is unsupported. Select Node.js 22.19 or later, except 23.x": "\u8FDC\u7A0B Node.js \u7248\u672C\u4E0D\u53D7\u652F\u6301\uFF0C\u8BF7\u9009\u62E9 Node.js 22.19 \u53CA\u4EE5\u4E0A\u7248\u672C\uFF0C\u4F46\u4E0D\u5305\u62EC 23.x",
23891
23890
  "npm installation failed. Check network access and global installation permissions": "npm \u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u548C\u5168\u5C40\u5B89\u88C5\u6743\u9650",
23892
23891
  "Remote service configuration failed. Check Codex CLI and the remote desktop login": "\u8FDC\u7A0B\u670D\u52A1\u914D\u7F6E\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 Codex CLI \u4EE5\u53CA\u8FDC\u7A0B\u7535\u8111\u7684\u684C\u9762\u767B\u5F55\u72B6\u6001",
23893
23892
  "Remote service could not stop. Check the remote service before retrying": "\u8FDC\u7A0B\u670D\u52A1\u65E0\u6CD5\u505C\u6B62\uFF0C\u8BF7\u68C0\u67E5\u8FDC\u7A0B\u670D\u52A1\u540E\u91CD\u8BD5",
@@ -25160,6 +25159,7 @@ ${detail}` : ""}`
25160
25159
  loaded = true;
25161
25160
  show(value);
25162
25161
  }).catch(() => {
25162
+ loaded = true;
25163
25163
  status.textContent = messages.launchPathLoadError;
25164
25164
  }).finally(() => {
25165
25165
  busy = false;
@@ -26647,6 +26647,30 @@ ${messages.harnessVersionLatest}: ${harnessVersionLabel(version2.state, messages
26647
26647
  };
26648
26648
  }
26649
26649
 
26650
+ // src/settings/session-import-sources.ts
26651
+ async function loadSessionImportSources(client, diagnostics, signal, refresh) {
26652
+ const { harnesses } = await client.listSessionImportSources();
26653
+ signal.throwIfAborted();
26654
+ if (!diagnostics || harnesses.length === 0) return harnesses;
26655
+ const hostId2 = client.hostId ?? "local";
26656
+ const readHost = () => diagnostics.snapshot().hosts.find((host2) => host2.hostId === hostId2);
26657
+ let host = readHost();
26658
+ const incomplete = () => !host || Boolean(host.directoryError) || harnesses.some(({ harnessId }) => {
26659
+ const status = host?.agents.find(({ agent }) => agent === harnessId)?.availability;
26660
+ return status === void 0 || status === "checking";
26661
+ });
26662
+ if (refresh || incomplete()) {
26663
+ await diagnostics.refresh(hostId2);
26664
+ signal.throwIfAborted();
26665
+ host = readHost();
26666
+ }
26667
+ if (incomplete()) throw new Error("Harness installation diagnostics are incomplete");
26668
+ const missing = new Set(
26669
+ host?.agents.filter(({ availability }) => availability === "notInstalled").map(({ agent }) => agent)
26670
+ );
26671
+ return harnesses.filter(({ harnessId }) => !missing.has(harnessId));
26672
+ }
26673
+
26650
26674
  // src/settings/session-import-page.ts
26651
26675
  function shortSessionId(value) {
26652
26676
  return value.length <= 18 ? value : `${value.slice(0, 10)}\u2026${value.slice(-6)}`;
@@ -26716,7 +26740,7 @@ ${messages.harnessVersionLatest}: ${harnessVersionLabel(version2.state, messages
26716
26740
  ...getDiagnostics()?.snapshot().hosts.map(({ hostId: hostId2 }) => hostId2) ?? [],
26717
26741
  ...current ? [current] : []
26718
26742
  ])
26719
- ];
26743
+ ].filter((hostId2) => hostId2 !== "durable");
26720
26744
  displayedHostId = current;
26721
26745
  const scrollLeft = hostOptions.scrollLeft;
26722
26746
  hostOptions.replaceChildren();
@@ -27060,10 +27084,14 @@ ${messages.harnessVersionLatest}: ${harnessVersionLabel(version2.state, messages
27060
27084
  content.append(list);
27061
27085
  updateImportActions();
27062
27086
  };
27063
- const load = () => {
27087
+ const load = (refreshDiagnostics = false) => {
27064
27088
  if (importingId !== null || context.signal.aborted) return;
27065
27089
  loadedClient = null;
27066
- const client = getClient(selectedHostId);
27090
+ let client = getClient(selectedHostId);
27091
+ if (client?.hostId === "durable") {
27092
+ selectedHostId = "local";
27093
+ client = getClient(selectedHostId);
27094
+ }
27067
27095
  renderHosts(client);
27068
27096
  listControls.setBusy(client !== null);
27069
27097
  refresh.disabled = client !== null;
@@ -27076,11 +27104,11 @@ ${messages.harnessVersionLatest}: ${harnessVersionLabel(version2.state, messages
27076
27104
  void context.runLatest(
27077
27105
  async (signal) => {
27078
27106
  if (!client) throw new RendererSessionImportUnavailableError();
27079
- const missing = new Set(
27080
- getDiagnostics()?.snapshot().hosts.find(({ hostId: hostId2 }) => hostId2 === (client.hostId ?? "local"))?.agents.filter(({ availability }) => availability === "notInstalled").map(({ agent }) => agent)
27081
- );
27082
- const harnesses = (await client.listSessionImportSources()).harnesses.filter(
27083
- ({ harnessId }) => !missing.has(harnessId)
27107
+ const harnesses = await loadSessionImportSources(
27108
+ client,
27109
+ getDiagnostics(),
27110
+ signal,
27111
+ refreshDiagnostics
27084
27112
  );
27085
27113
  const selected = harnesses.find(({ harnessId }) => harnessId === requestedHarness)?.harnessId ?? harnesses[0]?.harnessId ?? null;
27086
27114
  if (signal.aborted) throw new Error("Session import selection changed");
@@ -27131,7 +27159,7 @@ ${messages.harnessVersionLatest}: ${harnessVersionLabel(version2.state, messages
27131
27159
  load();
27132
27160
  hostButtons.find((button2) => button2.dataset.sessionImportHost === hostId2)?.focus();
27133
27161
  };
27134
- refresh.addEventListener("click", load);
27162
+ refresh.addEventListener("click", () => load(true));
27135
27163
  load();
27136
27164
  return void 0;
27137
27165
  }
@@ -227,13 +227,32 @@ var CdpClient = class _CdpClient {
227
227
  returnByValue: true
228
228
  });
229
229
  if (!isRecord(response)) throw new Error("Runtime.evaluate returned an invalid result");
230
- if (isRecord(response.exceptionDetails)) {
231
- throw new Error(cdpExceptionMessage(response.exceptionDetails, "Renderer evaluation failed"));
232
- }
233
- if (!isRecord(response.result) || !("value" in response.result)) {
234
- throw new Error("Runtime.evaluate did not return a value");
230
+ try {
231
+ if (isRecord(response.exceptionDetails)) {
232
+ throw new Error(
233
+ cdpExceptionMessage(response.exceptionDetails, "Renderer evaluation failed")
234
+ );
235
+ }
236
+ if (!isRecord(response.result) || !("value" in response.result)) {
237
+ throw new Error("Runtime.evaluate did not return a value");
238
+ }
239
+ return response.result.value;
240
+ } finally {
241
+ const objects = [
242
+ response.result,
243
+ isRecord(response.exceptionDetails) ? response.exceptionDetails.exception : void 0
244
+ ];
245
+ const ids = new Set(
246
+ objects.flatMap(
247
+ (object2) => isRecord(object2) && typeof object2.objectId === "string" ? [object2.objectId] : []
248
+ )
249
+ );
250
+ await Promise.all(
251
+ [...ids].map(
252
+ (objectId) => this.command("Runtime.releaseObject", { objectId }).catch(() => void 0)
253
+ )
254
+ );
235
255
  }
236
- return response.result.value;
237
256
  }
238
257
  close() {
239
258
  if (this.#closed) return;
@@ -17724,16 +17743,20 @@ function installRendererHostRpcRouting(root, target, dependencies) {
17724
17743
  if (!isRecord3(input) || !isRecord3(input.collaborationMode)) return message;
17725
17744
  const settings = input.collaborationMode.settings;
17726
17745
  if (!isRecord3(settings)) return message;
17746
+ const nativeModel = typeof settings.model === "string" ? settings.model : null;
17727
17747
  let model2 = null;
17728
17748
  if (path3[0] === "startConversation") {
17729
17749
  const draftId = isRecord3(options) ? options.clientThreadId : null;
17730
17750
  model2 = typeof draftId === "string" ? externalDrafts.get(draftId)?.model ?? null : null;
17731
17751
  } else if (path3[0] === "prewarmConversation") {
17732
17752
  if (input.ephemeral !== true) publishDraftWorkspace("local", input.cwd);
17733
- const selected = selectedModels.get("local");
17734
- model2 = selected ? prewarmCarrier(selected) : null;
17753
+ const selected = selectedModels.get("local") ?? null;
17754
+ const carrier = nativeModel?.startsWith("codexhost/") ? nativeModel : selected;
17755
+ model2 = carrier ? prewarmCarrier(carrier) : null;
17735
17756
  }
17736
17757
  if (!model2) return message;
17758
+ if (path3[0] === "startConversation" && nativeModel?.startsWith("codexhost/") && nativeModel !== model2)
17759
+ return message;
17737
17760
  return [
17738
17761
  "push",
17739
17762
  [
@@ -17945,23 +17968,29 @@ function directRendererInstaller() {
17945
17968
  },
17946
17969
  portPrototype: MessagePort.prototype,
17947
17970
  });
17948
- // Desktop 26.1027 moved AppServerManagers to the main process. Older builds
17949
- // expose native managers in Renderer hook state; the main-process mode only
17950
- // applies when none is visible and its per-Host RPC store is identified.
17971
+ const connected = (routing) => routing.forComposer() !== null ||
17972
+ ['local', ...(routing.knownHostIds?.() ?? [])].some((hostId) => routing.forHost(hostId) !== null);
17973
+ let nativeReady = false;
17974
+ // A legacy registry can remain mounted without a usable native manager.
17975
+ // Prefer a ready native route, then try the identified main-process RPC store.
17951
17976
  const routing = (() => {
17952
17977
  if (window.__codexhostHostRoutingV1) return window.__codexhostHostRoutingV1;
17953
17978
  const native = discover(document);
17954
- if (native.managers.length || native.registries.length) return installNative();
17979
+ if (native.managers.length || native.registries.length) {
17980
+ const routing = installNative();
17981
+ if (connected(routing)) {
17982
+ nativeReady = true;
17983
+ return routing;
17984
+ }
17985
+ routing.dispose();
17986
+ }
17955
17987
  const rpc = installRpc();
17956
17988
  if (rpc) return rpc;
17957
17989
  throw new Error('Renderer Host request manager is unavailable');
17958
17990
  })();
17959
17991
  // Publish a default draft policy only when its Host is unambiguous. Host
17960
17992
  // connections can be ready even when multiple Composers coexist.
17961
- const composerRoute = routing.forComposer();
17962
- const connected = composerRoute !== null ||
17963
- ['local', ...(routing.knownHostIds?.() ?? [])].some((hostId) => routing.forHost(hostId) !== null);
17964
- if (!connected) throw new Error('Renderer Host request manager is unavailable');
17993
+ if (!nativeReady && !connected(routing)) throw new Error('Renderer Host request manager is unavailable');
17965
17994
  return { state: 'ready', reason: 'owned-request-bridge' };
17966
17995
  })()`;
17967
17996
  }
@@ -17995,6 +18024,10 @@ function installRendererDraftPrewarmPolicyDirect(renderer) {
17995
18024
  // packages/desktop-control/src/renderer-loaded-modules.ts
17996
18025
  var record2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
17997
18026
  var MAX_MODULES = 4096;
18027
+ var STALE_CONTEXT_ERROR = /uniqueContextId not found|Cannot find context with specified id|Execution context was destroyed|Cannot find default execution context/i;
18028
+ function isStaleContextError(error51) {
18029
+ return error51 instanceof Error && STALE_CONTEXT_ERROR.test(error51.message);
18030
+ }
17998
18031
  function nativeModuleCatalogExpression(urls, complete, owner) {
17999
18032
  return `Object.defineProperty(globalThis, '__codexhostLoadedModulesV1', {
18000
18033
  configurable: true,
@@ -18016,16 +18049,27 @@ async function installRendererLoadedModules(renderer) {
18016
18049
  const pending = [...dirty];
18017
18050
  dirty.clear();
18018
18051
  publishing = publishing.catch(() => void 0).then(async () => {
18052
+ let failure;
18019
18053
  for (const uniqueId of pending) {
18020
18054
  const context = contexts.get(uniqueId);
18021
18055
  if (!context || disposed) continue;
18022
- const response = await renderer.command("Runtime.evaluate", {
18023
- expression: nativeModuleCatalogExpression([...context.urls], context.complete, owner),
18024
- uniqueContextId: uniqueId
18025
- });
18026
- if (record2(response) && record2(response.exceptionDetails))
18027
- throw new Error("Loaded Renderer module catalog publication failed");
18056
+ try {
18057
+ const response = await renderer.command("Runtime.evaluate", {
18058
+ expression: nativeModuleCatalogExpression([...context.urls], context.complete, owner),
18059
+ uniqueContextId: uniqueId
18060
+ });
18061
+ if (record2(response) && record2(response.exceptionDetails))
18062
+ throw new Error("Loaded Renderer module catalog publication failed");
18063
+ } catch (error51) {
18064
+ if (contexts.get(uniqueId) !== context) continue;
18065
+ if (isStaleContextError(error51)) {
18066
+ forget(uniqueId);
18067
+ continue;
18068
+ }
18069
+ failure ??= error51;
18070
+ }
18028
18071
  }
18072
+ if (failure !== void 0) throw failure;
18029
18073
  });
18030
18074
  return publishing;
18031
18075
  };
@@ -18038,13 +18082,15 @@ async function installRendererLoadedModules(renderer) {
18038
18082
  });
18039
18083
  }, 10);
18040
18084
  };
18041
- const remove = (id) => {
18042
- const uniqueId = contextIds.get(id);
18043
- contextIds.delete(id);
18044
- if (uniqueId) {
18045
- contexts.delete(uniqueId);
18046
- dirty.delete(uniqueId);
18047
- }
18085
+ const forget = (uniqueId) => {
18086
+ contexts.delete(uniqueId);
18087
+ dirty.delete(uniqueId);
18088
+ for (const [id, value] of contextIds) if (value === uniqueId) contextIds.delete(id);
18089
+ };
18090
+ const remove = (params) => {
18091
+ const byId = typeof params.executionContextId === "number" ? contextIds.get(params.executionContextId) : void 0;
18092
+ const uniqueId = typeof params.executionContextUniqueId === "string" ? params.executionContextUniqueId : byId;
18093
+ if (uniqueId) forget(uniqueId);
18048
18094
  };
18049
18095
  const listeners = [
18050
18096
  renderer.on("Runtime.executionContextCreated", (params) => {
@@ -18056,8 +18102,7 @@ async function installRendererLoadedModules(renderer) {
18056
18102
  schedule(context.uniqueId);
18057
18103
  }),
18058
18104
  renderer.on("Runtime.executionContextDestroyed", (params) => {
18059
- if (record2(params) && typeof params.executionContextId === "number")
18060
- remove(params.executionContextId);
18105
+ if (record2(params)) remove(params);
18061
18106
  }),
18062
18107
  renderer.on("Runtime.executionContextsCleared", () => {
18063
18108
  contextIds.clear();