@codexhost/cli-darwin-x64 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.1.3
10
+ npm install -g @codexhost/cli@0.1.5
11
11
  ```
12
12
 
13
13
  Do not install this package directly. npm selects it through the optional dependencies of `@codexhost/cli`.
@@ -1 +1 @@
1
- {"schemaVersion":1,"version":"0.1.3","distribution":"npm","target":"macos-x64"}
1
+ {"schemaVersion":1,"version":"0.1.5","distribution":"npm","target":"macos-x64"}
@@ -788,30 +788,12 @@ async function installRendererDraftPrewarmPolicy(inspector, rendererWebContentsI
788
788
  }
789
789
 
790
790
  // packages/desktop-control/src/renderer-control-session.ts
791
- var RendererCompatibilityError = class extends Error {
792
- constructor(capability, reason, cause) {
793
- super(`Renderer compatibility boundary unavailable: ${capability}`, { cause });
794
- this.capability = capability;
795
- this.reason = reason;
796
- this.name = "RendererCompatibilityError";
797
- }
798
- capability;
799
- reason;
800
- };
801
791
  function isRecord4(value) {
802
792
  return typeof value === "object" && value !== null && !Array.isArray(value);
803
793
  }
804
794
  function sleep(milliseconds) {
805
795
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
806
796
  }
807
- async function requireCompatibilityBoundary(capability, reason, operation) {
808
- try {
809
- return await operation();
810
- } catch (error) {
811
- if (error instanceof RendererCompatibilityError) throw error;
812
- throw new RendererCompatibilityError(capability, reason, error);
813
- }
814
- }
815
797
  function sameAgents(actual, expected) {
816
798
  return actual.length === expected.length && actual.every((agent, index) => agent === expected[index]);
817
799
  }
@@ -1040,39 +1022,26 @@ var InstalledRendererControlSession = class {
1040
1022
  );
1041
1023
  const existing = await this.operations.readBinding(this.inspector, selected.id).catch(() => null);
1042
1024
  if (existing !== null) {
1043
- const binding2 = await requireCompatibilityBoundary(
1044
- "agent-routing",
1045
- "agent-routing-structure-unavailable",
1046
- () => validateBindingStatus(existing, this.enabledAgents)
1047
- );
1025
+ const binding2 = validateBindingStatus(existing, this.enabledAgents);
1048
1026
  this.#snapshot = { ...this.#snapshot, renderer: selected, binding: binding2 };
1049
1027
  return this.#snapshot;
1050
1028
  }
1051
- const titlePolicyReadiness = await requireCompatibilityBoundary(
1052
- "title-isolation",
1053
- "title-isolation-structure-unavailable",
1054
- () => waitForRendererTitlePolicyReady(
1055
- () => this.operations.markTitlePolicyReady(this.inspector, selected.id),
1056
- { timeoutMs: this.timeoutMs, pollIntervalMs: this.pollIntervalMs }
1057
- )
1029
+ const titlePolicyReadiness = await waitForRendererTitlePolicyReady(
1030
+ () => this.operations.markTitlePolicyReady(this.inspector, selected.id),
1031
+ { timeoutMs: this.timeoutMs, pollIntervalMs: this.pollIntervalMs }
1058
1032
  );
1059
1033
  await this.operations.execute(this.inspector, selected.id, this.rendererSource);
1060
- const binding = await requireCompatibilityBoundary(
1061
- "agent-routing",
1062
- "agent-routing-structure-unavailable",
1063
- () => waitForBinding(
1064
- this.inspector,
1065
- this.operations,
1066
- selected.id,
1067
- this.enabledAgents,
1068
- this.timeoutMs,
1069
- this.pollIntervalMs
1070
- )
1034
+ const binding = await waitForBinding(
1035
+ this.inspector,
1036
+ this.operations,
1037
+ selected.id,
1038
+ this.enabledAgents,
1039
+ this.timeoutMs,
1040
+ this.pollIntervalMs
1071
1041
  );
1072
- const draftPrewarmPolicy = await requireCompatibilityBoundary(
1073
- "draft-routing",
1074
- "draft-routing-structure-unavailable",
1075
- () => this.operations.installDraftPrewarmPolicy(this.inspector, selected.id)
1042
+ const draftPrewarmPolicy = await this.operations.installDraftPrewarmPolicy(
1043
+ this.inspector,
1044
+ selected.id
1076
1045
  );
1077
1046
  this.#snapshot = {
1078
1047
  ...this.#snapshot,
@@ -1118,47 +1087,49 @@ var InstalledRendererControlSession = class {
1118
1087
  this.inspector.close();
1119
1088
  }
1120
1089
  };
1090
+ var startupTraceStartedAt = Date.now();
1091
+ function startupTrace(stage) {
1092
+ if (process.env.CODEXHOST_STARTUP_TRACE !== "1") return;
1093
+ console.error(
1094
+ `[codexhost startup +${Date.now() - startupTraceStartedAt}ms] renderer-session: ${stage}`
1095
+ );
1096
+ }
1121
1097
  async function createRendererControlSession(options) {
1122
1098
  const enabledAgents = options.enabledAgents ?? ["codex", "pi"];
1123
1099
  const timeoutMs = options.timeoutMs ?? 3e4;
1124
1100
  const pollIntervalMs = options.pollIntervalMs ?? 250;
1125
1101
  const operations = options.operations ?? defaultOperations;
1102
+ startupTrace("waiting for initial Renderer");
1126
1103
  const initial = await waitForRenderer(options.inspector, operations, timeoutMs, pollIntervalMs);
1127
- const titlePolicy = await requireCompatibilityBoundary(
1128
- "title-isolation",
1129
- "title-isolation-structure-unavailable",
1130
- () => operations.installTitlePolicy(options.inspector, initial.id)
1131
- );
1104
+ startupTrace("installing title policy");
1105
+ const titlePolicy = await operations.installTitlePolicy(options.inspector, initial.id);
1106
+ startupTrace("reloading Renderer");
1132
1107
  await operations.reload(options.inspector, initial.id);
1108
+ startupTrace("waiting for reloaded Renderer");
1133
1109
  const selected = await waitForRenderer(options.inspector, operations, timeoutMs, pollIntervalMs);
1134
- const titlePolicyReadiness = await requireCompatibilityBoundary(
1135
- "title-isolation",
1136
- "title-isolation-structure-unavailable",
1137
- () => waitForRendererTitlePolicyReady(
1138
- () => operations.markTitlePolicyReady(options.inspector, selected.id),
1139
- {
1140
- timeoutMs,
1141
- pollIntervalMs
1142
- }
1143
- )
1144
- );
1145
- await operations.execute(options.inspector, selected.id, options.rendererSource);
1146
- const binding = await requireCompatibilityBoundary(
1147
- "agent-routing",
1148
- "agent-routing-structure-unavailable",
1149
- () => waitForBinding(
1150
- options.inspector,
1151
- operations,
1152
- selected.id,
1153
- enabledAgents,
1110
+ startupTrace("waiting for title policy readiness");
1111
+ const titlePolicyReadiness = await waitForRendererTitlePolicyReady(
1112
+ () => operations.markTitlePolicyReady(options.inspector, selected.id),
1113
+ {
1154
1114
  timeoutMs,
1155
1115
  pollIntervalMs
1156
- )
1116
+ }
1117
+ );
1118
+ startupTrace("injecting Renderer bundle");
1119
+ await operations.execute(options.inspector, selected.id, options.rendererSource);
1120
+ startupTrace("waiting for Renderer binding");
1121
+ const binding = await waitForBinding(
1122
+ options.inspector,
1123
+ operations,
1124
+ selected.id,
1125
+ enabledAgents,
1126
+ timeoutMs,
1127
+ pollIntervalMs
1157
1128
  );
1158
- const draftPrewarmPolicy = await requireCompatibilityBoundary(
1159
- "draft-routing",
1160
- "draft-routing-structure-unavailable",
1161
- () => operations.installDraftPrewarmPolicy(options.inspector, selected.id)
1129
+ startupTrace("installing draft prewarm policy");
1130
+ const draftPrewarmPolicy = await operations.installDraftPrewarmPolicy(
1131
+ options.inspector,
1132
+ selected.id
1162
1133
  );
1163
1134
  return new InstalledRendererControlSession(
1164
1135
  options.inspector,
@@ -1179,12 +1150,15 @@ async function createRendererControlSession(options) {
1179
1150
  async function installRendererControlSession(options) {
1180
1151
  const timeoutMs = options.timeoutMs ?? 3e4;
1181
1152
  const pollIntervalMs = options.pollIntervalMs ?? 250;
1153
+ startupTrace("waiting for Electron Inspector target");
1182
1154
  const target = await waitForInspectorTarget(options.inspectorEndpoint, {
1183
1155
  timeoutMs,
1184
1156
  pollIntervalMs
1185
1157
  });
1158
+ startupTrace("connecting to Electron Inspector");
1186
1159
  const inspector = await CdpClient.connect(target.webSocketDebuggerUrl);
1187
1160
  try {
1161
+ startupTrace("enabling Inspector Runtime domain");
1188
1162
  await inspector.command("Runtime.enable");
1189
1163
  return await createRendererControlSession({
1190
1164
  ...options,
@@ -1203,6 +1177,16 @@ var PRODUCTION_INSTALL_TIMEOUT_MS = 9e4;
1203
1177
  var DESKTOP_CONTROLLER_READINESS_MAX_BYTES = 512;
1204
1178
  var TRANSIENT_INSTALL_ATTEMPTS = 3;
1205
1179
  var TRANSIENT_INSTALL_RETRY_MS = 250;
1180
+ var RECOVERY_RETRY_INITIAL_MS = 3e4;
1181
+ var RECOVERY_RETRY_MAX_MS = 3e5;
1182
+ var startupTraceStartedAt2 = Date.now();
1183
+ function startupTrace2(stage, detail) {
1184
+ if (process.env.CODEXHOST_STARTUP_TRACE !== "1") return;
1185
+ const suffix = detail === void 0 ? "" : `: ${detail instanceof Error ? detail.message : String(detail)}`;
1186
+ console.error(
1187
+ `[codexhost startup +${Date.now() - startupTraceStartedAt2}ms] controller: ${stage}${suffix}`
1188
+ );
1189
+ }
1206
1190
  function validCompatibilityIssue(state, issue) {
1207
1191
  const keys = Object.keys(issue);
1208
1192
  if (state === "compatible-with-warning") {
@@ -1217,24 +1201,10 @@ function validCompatibilityIssue(state, issue) {
1217
1201
  "settings-surface"
1218
1202
  ].includes(issue.capability) && issue.reason === "capability-unavailable" && issue.observedIdentity === void 0 && keys.length === 2;
1219
1203
  }
1220
- if (state === "incompatible") {
1221
- const pair = `${issue.capability}:${issue.reason}`;
1222
- return [
1223
- "title-isolation:title-isolation-structure-unavailable",
1224
- "draft-routing:draft-routing-structure-unavailable",
1225
- "agent-routing:agent-routing-structure-unavailable"
1226
- ].includes(pair) && issue.observedIdentity === void 0 && keys.length === 2;
1227
- }
1228
- return state === "detection-failed" && issue.capability === "compatibility-detection" && issue.reason === "inspection-failed" && issue.observedIdentity === void 0 && keys.length === 2;
1204
+ return false;
1229
1205
  }
1230
1206
  function serializeDesktopControllerReadiness(readiness) {
1231
- if (readiness.schemaVersion !== 2 || ![
1232
- "compatible",
1233
- "compatible-with-warning",
1234
- "degraded",
1235
- "incompatible",
1236
- "detection-failed"
1237
- ].includes(readiness.state) || !Array.isArray(readiness.issues) || readiness.issues.length > 1 || readiness.state === "compatible" && readiness.issues.length !== 0 || readiness.state !== "compatible" && (readiness.issues.length !== 1 || !readiness.issues[0] || !validCompatibilityIssue(readiness.state, readiness.issues[0])) || Object.keys(readiness).length !== 3) {
1207
+ if (readiness.schemaVersion !== 2 || !["compatible", "compatible-with-warning", "degraded"].includes(readiness.state) || !Array.isArray(readiness.issues) || readiness.issues.length > 1 || readiness.state === "compatible" && readiness.issues.length !== 0 || readiness.state !== "compatible" && (readiness.issues.length !== 1 || !readiness.issues[0] || !validCompatibilityIssue(readiness.state, readiness.issues[0])) || Object.keys(readiness).length !== 3) {
1238
1208
  throw new Error("Desktop Controller readiness is invalid");
1239
1209
  }
1240
1210
  const line = JSON.stringify(readiness);
@@ -1344,15 +1314,12 @@ function isTransientElectronInstallError(error) {
1344
1314
  }
1345
1315
  return false;
1346
1316
  }
1347
- function shouldRetryInstallError(error) {
1348
- return !(error instanceof RendererCompatibilityError) || isTransientElectronInstallError(error);
1349
- }
1350
1317
  async function installProductionSession(options, dependencies) {
1351
1318
  for (let attempt = 1; attempt <= TRANSIENT_INSTALL_ATTEMPTS; attempt += 1) {
1352
1319
  try {
1353
1320
  return await dependencies.install(options);
1354
1321
  } catch (error) {
1355
- if (attempt === TRANSIENT_INSTALL_ATTEMPTS || !shouldRetryInstallError(error)) {
1322
+ if (attempt === TRANSIENT_INSTALL_ATTEMPTS || !isTransientElectronInstallError(error)) {
1356
1323
  throw error;
1357
1324
  }
1358
1325
  await dependencies.sleep(TRANSIENT_INSTALL_RETRY_MS);
@@ -1361,12 +1328,25 @@ async function installProductionSession(options, dependencies) {
1361
1328
  throw new Error("Desktop Controller exhausted Renderer installation attempts");
1362
1329
  }
1363
1330
  async function runDesktopController(options, signal, dependencies = defaultDependencies) {
1331
+ const configuration = `Object.defineProperty(window, "__codexhostProductionConfigV1", { configurable: true, value: { defaultAgent: ${JSON.stringify(options.defaultAgent)} } });`;
1332
+ const now = dependencies.now ?? Date.now;
1364
1333
  let session;
1365
- try {
1334
+ let nextRecoveryAt = 0;
1335
+ let recoveryDelayMs = RECOVERY_RETRY_INITIAL_MS;
1336
+ const recordRecoveryFailure = () => {
1337
+ nextRecoveryAt = now() + recoveryDelayMs;
1338
+ recoveryDelayMs = Math.min(recoveryDelayMs * 2, RECOVERY_RETRY_MAX_MS);
1339
+ };
1340
+ const recordRecoverySuccess = () => {
1341
+ nextRecoveryAt = 0;
1342
+ recoveryDelayMs = RECOVERY_RETRY_INITIAL_MS;
1343
+ };
1344
+ const createSession = async () => {
1345
+ startupTrace2("reading Renderer bundle");
1366
1346
  const rendererSource = await dependencies.readRenderer(options.rendererPath);
1367
1347
  if (rendererSource.trim().length === 0) throw new Error("production Renderer Bundle is empty");
1368
- const configuration = `Object.defineProperty(window, "__codexhostProductionConfigV1", { configurable: true, value: { defaultAgent: ${JSON.stringify(options.defaultAgent)} } });`;
1369
- session = await installProductionSession(
1348
+ startupTrace2("installing Renderer Session");
1349
+ const installed = await installProductionSession(
1370
1350
  {
1371
1351
  inspectorEndpoint: options.inspectorEndpoint,
1372
1352
  rendererSource: `${configuration}
@@ -1376,19 +1356,17 @@ ${rendererSource}`,
1376
1356
  },
1377
1357
  dependencies
1378
1358
  );
1359
+ startupTrace2("Renderer Session installed");
1360
+ return installed;
1361
+ };
1362
+ startupTrace2("initialization started");
1363
+ try {
1364
+ session = await createSession();
1365
+ recordRecoverySuccess();
1379
1366
  } catch (error) {
1380
- dependencies.ready(
1381
- error instanceof RendererCompatibilityError && !isTransientElectronInstallError(error) ? {
1382
- schemaVersion: 2,
1383
- state: "incompatible",
1384
- issues: [{ capability: error.capability, reason: error.reason }]
1385
- } : {
1386
- schemaVersion: 2,
1387
- state: "detection-failed",
1388
- issues: [{ capability: "compatibility-detection", reason: "inspection-failed" }]
1389
- }
1390
- );
1391
- return;
1367
+ startupTrace2("initial Renderer Session unavailable", error);
1368
+ session = void 0;
1369
+ recordRecoveryFailure();
1392
1370
  }
1393
1371
  let operation = Promise.resolve(void 0);
1394
1372
  const useSession = (callback) => {
@@ -1399,22 +1377,48 @@ ${rendererSource}`,
1399
1377
  );
1400
1378
  return next;
1401
1379
  };
1380
+ const resetSession = () => {
1381
+ session?.close();
1382
+ session = void 0;
1383
+ };
1384
+ const ensureSession = async () => {
1385
+ if (!session) session = await createSession();
1386
+ else await session.ensureInstalled();
1387
+ return session;
1388
+ };
1389
+ const recoverSession = async () => {
1390
+ try {
1391
+ const current = await ensureSession();
1392
+ recordRecoverySuccess();
1393
+ return current;
1394
+ } catch (error) {
1395
+ resetSession();
1396
+ recordRecoveryFailure();
1397
+ throw error;
1398
+ }
1399
+ };
1402
1400
  let attachmentServer;
1403
1401
  try {
1402
+ startupTrace2("starting attachment server");
1404
1403
  attachmentServer = await dependencies.startAttachmentServer({
1405
1404
  port: options.attachmentPort,
1406
1405
  nonce: options.attachmentNonce,
1407
1406
  attach: () => useSession(async () => {
1408
- await session.ensureInstalled();
1409
- await session.activateDesktop();
1407
+ const current = await recoverSession();
1408
+ await current.activateDesktop();
1410
1409
  }),
1411
1410
  compatibilityUpdate: () => useSession(async () => {
1412
- await session.ensureInstalled();
1413
- return session.requestCompatibilityUpdate();
1411
+ const current = await recoverSession();
1412
+ return current.requestCompatibilityUpdate();
1414
1413
  }),
1415
- shutdown: () => useSession(() => session.quitDesktop())
1414
+ shutdown: () => useSession(async () => {
1415
+ const current = await recoverSession();
1416
+ await current.quitDesktop();
1417
+ })
1416
1418
  });
1417
- const issues = session.snapshot.titlePolicy.warnings;
1419
+ startupTrace2("attachment server ready");
1420
+ const issues = session?.snapshot.titlePolicy.warnings ?? [];
1421
+ startupTrace2("publishing readiness");
1418
1422
  dependencies.ready({
1419
1423
  schemaVersion: 2,
1420
1424
  state: issues.length === 0 ? "compatible" : "compatible-with-warning",
@@ -1422,12 +1426,19 @@ ${rendererSource}`,
1422
1426
  });
1423
1427
  while (!signal.aborted) {
1424
1428
  await dependencies.sleep(dependencies.monitorIntervalMs);
1425
- if (!signal.aborted) await useSession(() => session.ensureInstalled());
1429
+ if (signal.aborted) continue;
1430
+ await useSession(async () => {
1431
+ if (!session && now() < nextRecoveryAt) return;
1432
+ try {
1433
+ await recoverSession();
1434
+ } catch {
1435
+ }
1436
+ });
1426
1437
  }
1427
1438
  } finally {
1428
1439
  await attachmentServer?.close();
1429
1440
  await operation;
1430
- session.close();
1441
+ resetSession();
1431
1442
  }
1432
1443
  }
1433
1444
 
@@ -50582,7 +50582,7 @@ function isHostApprovalRequestId(value) {
50582
50582
  function isHostQuestionRequestId(value) {
50583
50583
  return typeof value === "number" && Number.isSafeInteger(value) && value >= HOST_QUESTION_REQUEST_ID_MIN && value <= HOST_QUESTION_REQUEST_ID_MAX;
50584
50584
  }
50585
- function classifyCreateRequestRoute(request, defaultAgent) {
50585
+ function classifyCreateRequestRoute(request, defaultAgent2) {
50586
50586
  const route = decodeCreateRoute(request);
50587
50587
  if (!route) return null;
50588
50588
  if (route.harnessId !== "codex") {
@@ -50596,8 +50596,8 @@ function classifyCreateRequestRoute(request, defaultAgent) {
50596
50596
  return {
50597
50597
  requestMethod: "thread/start",
50598
50598
  modelCarrier: "official-model",
50599
- selectedHarness: defaultAgent,
50600
- selectionSource: defaultAgent === "pi" ? "default-agent" : "official-model"
50599
+ selectedHarness: defaultAgent2,
50600
+ selectionSource: defaultAgent2 === "pi" ? "default-agent" : "official-model"
50601
50601
  };
50602
50602
  }
50603
50603
  function requestObject(request) {
@@ -53101,8 +53101,7 @@ function createBackgroundUpdateManager(dependencies = {}) {
53101
53101
  kind: "npm",
53102
53102
  node_path: await requireRegularFile(options.nodePath, "npm Node.js executable"),
53103
53103
  npm_cli_path: await requireRegularFile(options.npmCliPath, "npm CLI"),
53104
- npm_launcher_path: await requireRegularFile(options.npmLauncherPath, "npm codexhost launcher"),
53105
- package_root: requireAbsolutePath(options.packageRoot, "npm platform package root")
53104
+ npm_launcher_path: await requireRegularFile(options.npmLauncherPath, "npm codexhost launcher")
53106
53105
  });
53107
53106
  } catch (error52) {
53108
53107
  await writeFailedStatus(common.statusPath, common.version, "npm", error52);
@@ -53164,20 +53163,6 @@ var ERROR_MAX_LENGTH = 500;
53164
53163
  var CONTROLLER_TIMEOUT_MS = 5e3;
53165
53164
  var UPDATER_READY_TIMEOUT_MS = 1e4;
53166
53165
  var UPDATER_READY_POLL_MS = 20;
53167
- async function startCompatibilityUpdate(coordinator) {
53168
- try {
53169
- const check2 = await coordinator.check();
53170
- if (check2.updateAvailable && check2.installationAvailable) {
53171
- void coordinator.start().catch(() => void 0);
53172
- return "update-started";
53173
- }
53174
- if (check2.error === null && check2.latestVersion !== null && check2.currentVersion === check2.latestVersion) {
53175
- return "current";
53176
- }
53177
- } catch {
53178
- }
53179
- return "unavailable";
53180
- }
53181
53166
  function boundedError(error52) {
53182
53167
  const message3 = error52 instanceof Error ? error52.message : String(error52);
53183
53168
  return message3.slice(0, ERROR_MAX_LENGTH) || "Update operation failed";
@@ -53428,31 +53413,25 @@ function createHostUpdateCoordinator(options) {
53428
53413
  // packages/host-runtime/src/release-main.ts
53429
53414
  var STOCK_CODEX_PATH_ENV = "CODEXHOST_STOCK_CODEX_PATH";
53430
53415
  var DEFAULT_AGENT_ENV = "CODEXHOST_DEFAULT_AGENT";
53431
- var COMPATIBILITY_UPDATE_ARGUMENT = "--codexhost-compatibility-update";
53432
53416
  var arguments_ = process.argv.slice(2);
53433
53417
  var updateCoordinator = createHostUpdateCoordinator({
53434
53418
  hostRuntimePath: fileURLToPath(import.meta.url),
53435
53419
  environment: process.env
53436
53420
  });
53437
- if (arguments_.length === 1 && arguments_[0] === COMPATIBILITY_UPDATE_ARGUMENT) {
53438
- process.stdout.write(`${await startCompatibilityUpdate(updateCoordinator)}
53439
- `);
53440
- } else {
53441
- const stockCodexPath = process.env[STOCK_CODEX_PATH_ENV];
53442
- if (!stockCodexPath) throw new Error(`${STOCK_CODEX_PATH_ENV} is required`);
53443
- const defaultAgent = process.env[DEFAULT_AGENT_ENV];
53444
- if (defaultAgent !== "codex" && defaultAgent !== "pi") {
53445
- throw new Error(`${DEFAULT_AGENT_ENV} must be 'codex' or 'pi'`);
53446
- }
53447
- const externalAdapters = createExternalHarnessAdapters(process.env);
53448
- const host = new AppServerHost({
53449
- stockCodexPath,
53450
- arguments: arguments_,
53451
- defaultAgent,
53452
- environment: process.env,
53453
- externalAdapters,
53454
- updateCoordinator
53455
- });
53456
- void prefetchClaudeCodeModelCatalog(externalAdapters);
53457
- process.exitCode = await host.run();
53458
- }
53421
+ var stockCodexPath = process.env[STOCK_CODEX_PATH_ENV];
53422
+ if (!stockCodexPath) throw new Error(`${STOCK_CODEX_PATH_ENV} is required`);
53423
+ var defaultAgent = process.env[DEFAULT_AGENT_ENV];
53424
+ if (defaultAgent !== "codex" && defaultAgent !== "pi") {
53425
+ throw new Error(`${DEFAULT_AGENT_ENV} must be 'codex' or 'pi'`);
53426
+ }
53427
+ var externalAdapters = createExternalHarnessAdapters(process.env);
53428
+ var host = new AppServerHost({
53429
+ stockCodexPath,
53430
+ arguments: arguments_,
53431
+ defaultAgent,
53432
+ environment: process.env,
53433
+ externalAdapters,
53434
+ updateCoordinator
53435
+ });
53436
+ void prefetchClaudeCodeModelCatalog(externalAdapters);
53437
+ process.exitCode = await host.run();
@@ -17223,9 +17223,52 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17223
17223
  var THREAD_PERMISSION_MODE_SELECT_METHOD = "codexhost/thread/permission-mode/select";
17224
17224
  var THREAD_OWNERSHIP_LIST_METHOD = "codexhost/thread/ownership/list";
17225
17225
  var THREAD_USAGE_INSPECT_METHOD = "codexhost/thread/usage/inspect";
17226
+ var THREAD_TOKEN_USAGE_UPDATED_METHOD = "thread/tokenUsage/updated";
17226
17227
  var UPDATE_CHECK_METHOD = "codexhost/update/check";
17227
17228
  var UPDATE_START_METHOD = "codexhost/update/start";
17228
17229
  var UPDATE_STATUS_METHOD = "codexhost/update/status";
17230
+ function isRecord4(value) {
17231
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17232
+ }
17233
+ function notifiedThreadId(notification) {
17234
+ if (!isRecord4(notification) || notification.method !== THREAD_TOKEN_USAGE_UPDATED_METHOD) {
17235
+ return null;
17236
+ }
17237
+ const params = notification.params;
17238
+ if (!isRecord4(params)) return null;
17239
+ const parsed = hostThreadIdSchema.safeParse(params.threadId);
17240
+ return parsed.success ? parsed.data : null;
17241
+ }
17242
+ function createThreadUsageSubscriptionRelay() {
17243
+ const listeners = /* @__PURE__ */ new Set();
17244
+ let removeNotificationCallback = null;
17245
+ return {
17246
+ connect(client) {
17247
+ if (removeNotificationCallback || listeners.size === 0) return;
17248
+ try {
17249
+ removeNotificationCallback = client.subscribeThreadUsage?.((update) => {
17250
+ for (const listener of listeners) listener(update);
17251
+ }) ?? null;
17252
+ } catch {
17253
+ removeNotificationCallback = null;
17254
+ }
17255
+ },
17256
+ subscribe(listener) {
17257
+ listeners.add(listener);
17258
+ return () => {
17259
+ listeners.delete(listener);
17260
+ if (listeners.size > 0) return;
17261
+ removeNotificationCallback?.();
17262
+ removeNotificationCallback = null;
17263
+ };
17264
+ },
17265
+ dispose() {
17266
+ removeNotificationCallback?.();
17267
+ removeNotificationCallback = null;
17268
+ listeners.clear();
17269
+ }
17270
+ };
17271
+ }
17229
17272
  function createRendererModelClient(candidates) {
17230
17273
  const managers = candidates.filter(
17231
17274
  (candidate) => typeof candidate.sendRequest === "function"
@@ -17279,6 +17322,29 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17279
17322
  return result;
17280
17323
  },
17281
17324
  inspectThreadUsage,
17325
+ subscribeThreadUsage(listener) {
17326
+ if (typeof manager.addNotificationCallback !== "function") return () => void 0;
17327
+ let disposed = false;
17328
+ const generations = /* @__PURE__ */ new Map();
17329
+ const removeNotificationCallback = manager.addNotificationCallback(
17330
+ THREAD_TOKEN_USAGE_UPDATED_METHOD,
17331
+ (notification) => {
17332
+ const threadId = notifiedThreadId(notification);
17333
+ if (!threadId) return;
17334
+ const generation = (generations.get(threadId) ?? 0) + 1;
17335
+ generations.set(threadId, generation);
17336
+ void inspectThreadUsage({ threadId }).then((update) => {
17337
+ if (!disposed && generations.get(threadId) === generation) listener(update);
17338
+ }).catch(() => void 0);
17339
+ }
17340
+ );
17341
+ return () => {
17342
+ if (disposed) return;
17343
+ disposed = true;
17344
+ generations.clear();
17345
+ removeNotificationCallback();
17346
+ };
17347
+ },
17282
17348
  selectThreadModel,
17283
17349
  selectThreadThinking,
17284
17350
  selectThreadPermissionMode,
@@ -17331,7 +17397,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17331
17397
  function isTransportModelId(model) {
17332
17398
  return isPiTransportModelId(model) || isClaudeTransportModelId(model);
17333
17399
  }
17334
- function isRecord4(value) {
17400
+ function isRecord5(value) {
17335
17401
  return typeof value === "object" && value !== null && !Array.isArray(value);
17336
17402
  }
17337
17403
  function piTransportModelId(model, thinkingOptionId) {
@@ -17402,7 +17468,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17402
17468
  return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.prewarmThreadStart === "function";
17403
17469
  }
17404
17470
  function isActiveRequestManager(value) {
17405
- if (!isRecord4(value) || typeof value.sendRequest !== "function") return false;
17471
+ if (!isRecord5(value) || typeof value.sendRequest !== "function") return false;
17406
17472
  const source = Function.prototype.toString.call(value.sendRequest);
17407
17473
  return source.includes("send-cli-request-for-host") && hasPrewarmMethod(value.requestClient);
17408
17474
  }
@@ -17438,7 +17504,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17438
17504
  let hook = fiber.memoizedState;
17439
17505
  for (let hookIndex = 0; hook && hookIndex < 100; hookIndex += 1) {
17440
17506
  const hookState = hook.memoizedState;
17441
- if (isRecord4(hookState)) {
17507
+ if (isRecord5(hookState)) {
17442
17508
  const requestClient = hookState.requestClient;
17443
17509
  if (isActiveRequestManager(hookState) && matchesCurrentPrewarmSignature(hookState)) {
17444
17510
  targets.add(hookState);
@@ -17455,10 +17521,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17455
17521
  return [...targets];
17456
17522
  }
17457
17523
  function isModelAtomState(value) {
17458
- return isRecord4(value) && isRecord4(value.atom) && typeof value.get === "function" && typeof value.set === "function";
17524
+ return isRecord5(value) && isRecord5(value.atom) && typeof value.get === "function" && typeof value.set === "function";
17459
17525
  }
17460
17526
  function isModelSelection(value) {
17461
- return value === null || isRecord4(value) && "model" in value && "reasoningEffort" in value;
17527
+ return value === null || isRecord5(value) && "model" in value && "reasoningEffort" in value;
17462
17528
  }
17463
17529
  function sameModelPowerSelection(left, right) {
17464
17530
  return left === right || left !== null && right !== null && left.model === right.model && left.reasoningEffort === right.reasoningEffort;
@@ -17470,7 +17536,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17470
17536
  const first = pairs[0];
17471
17537
  if (!first) return null;
17472
17538
  const value = first.optimistic.get();
17473
- if (isRecord4(value) && "modelSettings" in value) return first.optimistic;
17539
+ if (isRecord5(value) && "modelSettings" in value) return first.optimistic;
17474
17540
  if (first.optimistic.atom === first.committed.atom) return null;
17475
17541
  if (!pairs.every(
17476
17542
  (pair) => pair.optimistic.atom === first.optimistic.atom && pair.committed.atom === first.committed.atom && sameTarget2(pair.target, first.target)
@@ -17504,7 +17570,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17504
17570
  let fiber = fiberName ? Object.getOwnPropertyDescriptor(button, fiberName)?.value : null;
17505
17571
  for (let depth = 0; fiber && depth < 60; depth += 1) {
17506
17572
  const props = fiber.memoizedProps;
17507
- if (isRecord4(props) && typeof props.onSelectModel === "function" && typeof props.onSelectReasoningEffort === "function" && "reasoningEffort" in props && isRecord4(props.fallbackPowerSelection)) {
17573
+ if (isRecord5(props) && typeof props.onSelectModel === "function" && typeof props.onSelectReasoningEffort === "function" && "reasoningEffort" in props && isRecord5(props.fallbackPowerSelection)) {
17508
17574
  matches.push({ callback: props.onSelectModel, value: props.reasoningEffort });
17509
17575
  }
17510
17576
  const parent = fiber.return;
@@ -17529,9 +17595,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17529
17595
  let fiber = fiberName ? Object.getOwnPropertyDescriptor(button, fiberName)?.value : null;
17530
17596
  for (let depth = 0; fiber && depth < 60; depth += 1) {
17531
17597
  const props = fiber.memoizedProps;
17532
- if (!control && isRecord4(props) && "model" in props && "reasoningEffort" in props && typeof props.onSelectModel === "function") {
17598
+ if (!control && isRecord5(props) && "model" in props && "reasoningEffort" in props && typeof props.onSelectModel === "function") {
17533
17599
  const selection = { model: props.model, reasoningEffort: props.reasoningEffort };
17534
- const fallbackSelection = isRecord4(props.fallbackPowerSelection) ? props.fallbackPowerSelection : null;
17600
+ const fallbackSelection = isRecord5(props.fallbackPowerSelection) ? props.fallbackPowerSelection : null;
17535
17601
  control = {
17536
17602
  codexSelection: isTransportModelId(selection.model) ? fallbackSelection : selection,
17537
17603
  selection,
@@ -17557,8 +17623,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17557
17623
  let fiber = findComposerFiber(composer);
17558
17624
  for (let depth = 0; fiber && depth < 120; depth += 1) {
17559
17625
  const updateQueue = fiber.updateQueue;
17560
- const memoCache = isRecord4(updateQueue) ? updateQueue.memoCache : null;
17561
- const data = isRecord4(memoCache) && Array.isArray(memoCache.data) ? memoCache.data : [];
17626
+ const memoCache = isRecord5(updateQueue) ? updateQueue.memoCache : null;
17627
+ const data = isRecord5(memoCache) && Array.isArray(memoCache.data) ? memoCache.data : [];
17562
17628
  for (let index = 0; index + 1 < data.length; index += 1) {
17563
17629
  const compact = data[index];
17564
17630
  const resolved = data[index + 1];
@@ -17571,7 +17637,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17571
17637
  } catch {
17572
17638
  continue;
17573
17639
  }
17574
- if (!isRecord4(value) || !("modelSettings" in value)) continue;
17640
+ if (!isRecord5(value) || !("modelSettings" in value)) continue;
17575
17641
  const target = resolved[2] === null || resolved[2].startsWith("client-new-thread:") ? ["default", resolved[2]] : ["conversation", resolved[2]];
17576
17642
  compactPairs.push({ optimistic: compact[1], committed: resolved[3], target });
17577
17643
  }
@@ -17633,7 +17699,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17633
17699
  reasoningEffort: reasoningEffort.value
17634
17700
  };
17635
17701
  }
17636
- if (!isRecord4(current) || !("modelSettings" in current)) return null;
17702
+ if (!isRecord5(current) || !("modelSettings" in current)) return null;
17637
17703
  const picker = findModelPickerControl(composer);
17638
17704
  if (!picker.found) return null;
17639
17705
  return {
@@ -17648,10 +17714,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17648
17714
  };
17649
17715
  }
17650
17716
  function isMainProcessTitlePolicyReady(value) {
17651
- return isRecord4(value) && value.state === "ready";
17717
+ return isRecord5(value) && value.state === "ready";
17652
17718
  }
17653
17719
  function isDraftPrewarmPolicyReady(value) {
17654
- return isRecord4(value) && value.state === "ready" && typeof value.clear === "function";
17720
+ return isRecord5(value) && value.state === "ready" && typeof value.clear === "function";
17655
17721
  }
17656
17722
  async function waitForRendererDraftPrewarmPolicy(target) {
17657
17723
  const deadline = Date.now() + DRAFT_PREWARM_POLICY_WAIT_TIMEOUT_MS;
@@ -17695,9 +17761,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17695
17761
  dispose() {
17696
17762
  }
17697
17763
  });
17764
+ const usageSubscription = createThreadUsageSubscriptionRelay();
17698
17765
  const currentModelClient = () => {
17699
17766
  const client = createRendererModelClient(findActivePrewarmTargets(document));
17700
17767
  if (!client) throw new Error("Renderer Model request manager is unavailable");
17768
+ usageSubscription.connect(client);
17701
17769
  return client;
17702
17770
  };
17703
17771
  const modelControl = Object.freeze({
@@ -17705,6 +17773,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17705
17773
  inspectHarness: (input) => currentModelClient().inspectHarness(input),
17706
17774
  inspectThread: (input) => currentModelClient().inspectThread(input),
17707
17775
  inspectThreadUsage: (input) => currentModelClient().inspectThreadUsage(input),
17776
+ subscribeThreadUsage: (listener) => usageSubscription.subscribe(listener),
17708
17777
  listThreadOwnership: (input) => currentModelClient().listThreadOwnership(input),
17709
17778
  selectThreadModel: (input) => currentModelClient().selectThreadModel(input),
17710
17779
  selectThreadThinking: (input) => currentModelClient().selectThreadThinking(input),
@@ -17783,6 +17852,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17783
17852
  disposed = true;
17784
17853
  observer.disconnect();
17785
17854
  forkControl.dispose();
17855
+ usageSubscription.dispose();
17786
17856
  modelController = null;
17787
17857
  officialSelection = null;
17788
17858
  hasOfficialSelection = false;
@@ -17827,11 +17897,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17827
17897
  return null;
17828
17898
  }
17829
17899
  }
17830
- function isRecord5(value) {
17900
+ function isRecord6(value) {
17831
17901
  return typeof value === "object" && value !== null && !Array.isArray(value);
17832
17902
  }
17833
17903
  function parseExternalConfiguration(value) {
17834
- if (!isRecord5(value)) return void 0;
17904
+ if (!isRecord6(value)) return void 0;
17835
17905
  const model = harnessModelRefSchema.safeParse(value.model);
17836
17906
  if (!model.success) return void 0;
17837
17907
  const thinkingOptionId = harnessThinkingOptionIdSchema.safeParse(value.thinkingOptionId);
@@ -17846,9 +17916,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17846
17916
  const raw = storage.getItem(RENDERER_NEW_THREAD_PREFERENCE_KEY);
17847
17917
  if (!raw) return void 0;
17848
17918
  const parsed = JSON.parse(raw);
17849
- if (!isRecord5(parsed) || parsed.version !== 1) return void 0;
17919
+ if (!isRecord6(parsed) || parsed.version !== 1) return void 0;
17850
17920
  if (!KNOWN_RENDERER_AGENTS.some((agent) => agent === parsed.lastAgent)) return void 0;
17851
- const externalByAgent = isRecord5(parsed.externalByAgent) ? parsed.externalByAgent : {};
17921
+ const externalByAgent = isRecord6(parsed.externalByAgent) ? parsed.externalByAgent : {};
17852
17922
  const pi = parseExternalConfiguration(externalByAgent.pi);
17853
17923
  const claudeCode = parseExternalConfiguration(externalByAgent["claude-code"]);
17854
17924
  return {
@@ -17918,7 +17988,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17918
17988
  var GET_LOCALE_OVERRIDE_URL = "vscode://codex/get-setting";
17919
17989
  var GET_LOCALE_INFO_URL = "vscode://codex/locale-info";
17920
17990
  var DEFAULT_LOCALE_REQUEST_TIMEOUT_MS = 750;
17921
- function isRecord6(value) {
17991
+ function isRecord7(value) {
17922
17992
  return typeof value === "object" && value !== null && !Array.isArray(value);
17923
17993
  }
17924
17994
  function canonicalLocale(value) {
@@ -17940,7 +18010,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17940
18010
  return new DOMException("The operation was aborted", "AbortError");
17941
18011
  }
17942
18012
  function parseLocaleOverride(value) {
17943
- if (!isRecord6(value) || !("value" in value)) {
18013
+ if (!isRecord7(value) || !("value" in value)) {
17944
18014
  throw new Error("Codex locale override response is malformed");
17945
18015
  }
17946
18016
  if (value.value === null) return { value: null };
@@ -17955,7 +18025,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17955
18025
  return locale;
17956
18026
  }
17957
18027
  function parseLocaleInfo(value) {
17958
- if (!isRecord6(value)) throw new Error("Codex locale info response is malformed");
18028
+ if (!isRecord7(value)) throw new Error("Codex locale info response is malformed");
17959
18029
  return {
17960
18030
  ideLocale: optionalLocale(value.ideLocale, "IDE locale"),
17961
18031
  systemLocale: optionalLocale(value.systemLocale, "system locale")
@@ -17984,7 +18054,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17984
18054
  const onAbort = () => settle(() => reject(abortError()));
17985
18055
  const onMessage = (event) => {
17986
18056
  const message2 = event.data;
17987
- if (!isRecord6(message2) || message2.type !== "fetch-response") return;
18057
+ if (!isRecord7(message2) || message2.type !== "fetch-response") return;
17988
18058
  if (message2.requestId !== requestId) return;
17989
18059
  if (message2.responseType !== "success" || typeof message2.status !== "number" || message2.status < 200 || message2.status >= 300 || typeof message2.bodyJsonString !== "string") {
17990
18060
  settle(() => reject(new Error("Codex locale request failed")));
@@ -18098,15 +18168,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18098
18168
  locale: "en",
18099
18169
  title: "Settings",
18100
18170
  close: "Close settings",
18101
- interfaceLanguage: "Interface language",
18102
- automaticLanguage: "Automatic (follow Codex)",
18103
- englishLanguage: "English",
18104
- simplifiedChineseLanguage: "Simplified Chinese",
18105
- otherCodexLanguage: "Other Codex language",
18106
- languageUpdateFailed: "Could not update the language setting.",
18171
+ starOnGitHub: "Give us a Star~",
18107
18172
  sectionsLabel: "Settings sections",
18108
18173
  pageUnavailable: "Page unavailable",
18109
- availability: "Availability",
18174
+ inDevelopment: "In development",
18110
18175
  notAvailable: "Not available",
18111
18176
  runtimeCapabilityNotInstalled: "This runtime capability is not installed yet.",
18112
18177
  openSettings: "Open codexhost settings",
@@ -18125,11 +18190,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18125
18190
  updateChecking: "Checking for updates...",
18126
18191
  updateDownloading: "Downloading update...",
18127
18192
  updatePreparing: "Preparing update...",
18193
+ updateWaitingForExit: "Waiting for the application to close...",
18194
+ updateInstalling: "Installing update...",
18195
+ updateInstallingNpm: "Installing update through npm...",
18128
18196
  updateRequestTimeout: "The update service did not respond. Try again.",
18129
18197
  updateRestarting: "Restarting to finish the update...",
18130
18198
  updateSucceeded: "Update installed successfully.",
18131
18199
  updateFailed: "Update failed.",
18132
18200
  updateRetry: "Retry",
18201
+ updateManualNpmDescription: "To update manually, quit codexhost and run this command:",
18133
18202
  updateDownloadFromReleases: "Download from GitHub Releases",
18134
18203
  pageLabels: Object.freeze({
18135
18204
  connections: "Connections",
@@ -18143,15 +18212,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18143
18212
  locale: "zh-CN",
18144
18213
  title: "\u8BBE\u7F6E",
18145
18214
  close: "\u5173\u95ED\u8BBE\u7F6E",
18146
- interfaceLanguage: "\u754C\u9762\u8BED\u8A00",
18147
- automaticLanguage: "\u81EA\u52A8\uFF08\u8DDF\u968F Codex\uFF09",
18148
- englishLanguage: "English",
18149
- simplifiedChineseLanguage: "\u7B80\u4F53\u4E2D\u6587",
18150
- otherCodexLanguage: "\u5176\u4ED6 Codex \u8BED\u8A00",
18151
- languageUpdateFailed: "\u65E0\u6CD5\u66F4\u65B0\u8BED\u8A00\u8BBE\u7F6E\u3002",
18215
+ starOnGitHub: "\u70B9\u4E2A Star~",
18152
18216
  sectionsLabel: "\u8BBE\u7F6E\u5206\u7C7B",
18153
18217
  pageUnavailable: "\u9875\u9762\u4E0D\u53EF\u7528",
18154
- availability: "\u53EF\u7528\u6027",
18218
+ inDevelopment: "\u5F00\u53D1\u4E2D",
18155
18219
  notAvailable: "\u6682\u4E0D\u53EF\u7528",
18156
18220
  runtimeCapabilityNotInstalled: "\u8FD0\u884C\u65F6\u5C1A\u672A\u5B89\u88C5\u8BE5\u9879\u80FD\u529B\uFF0C\u56E0\u6B64\u6682\u4E0D\u53EF\u7528\u3002",
18157
18221
  openSettings: "\u6253\u5F00 codexhost \u8BBE\u7F6E",
@@ -18170,11 +18234,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18170
18234
  updateChecking: "\u6B63\u5728\u68C0\u67E5\u66F4\u65B0...",
18171
18235
  updateDownloading: "\u6B63\u5728\u4E0B\u8F7D\u66F4\u65B0...",
18172
18236
  updatePreparing: "\u6B63\u5728\u51C6\u5907\u66F4\u65B0...",
18237
+ updateWaitingForExit: "\u6B63\u5728\u7B49\u5F85\u5E94\u7528\u9000\u51FA...",
18238
+ updateInstalling: "\u6B63\u5728\u5B89\u88C5\u66F4\u65B0...",
18239
+ updateInstallingNpm: "\u6B63\u5728\u901A\u8FC7 npm \u5B89\u88C5...",
18173
18240
  updateRequestTimeout: "\u66F4\u65B0\u670D\u52A1\u672A\u54CD\u5E94\uFF0C\u8BF7\u91CD\u8BD5\u3002",
18174
18241
  updateRestarting: "\u6B63\u5728\u91CD\u542F\u4EE5\u5B8C\u6210\u66F4\u65B0...",
18175
18242
  updateSucceeded: "\u66F4\u65B0\u5B89\u88C5\u6210\u529F\u3002",
18176
18243
  updateFailed: "\u66F4\u65B0\u5931\u8D25\u3002",
18177
18244
  updateRetry: "\u91CD\u8BD5",
18245
+ updateManualNpmDescription: "\u5982\u9700\u624B\u52A8\u66F4\u65B0\uFF0C\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u3002\u66F4\u65B0\u5B8C\u6210\u540E\uFF0C\u8BF7\u9000\u51FA Codex \u5E76\u901A\u8FC7 codexhost \u91CD\u65B0\u542F\u52A8\u3002",
18178
18246
  updateDownloadFromReleases: "\u524D\u5F80 GitHub Releases \u4E0B\u8F7D",
18179
18247
  pageLabels: Object.freeze({
18180
18248
  connections: "\u8FDE\u63A5",
@@ -18310,6 +18378,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18310
18378
  ["circle", { cx: "12", cy: "12", r: "3" }]
18311
18379
  ];
18312
18380
 
18381
+ // ../../node_modules/lucide/dist/esm/icons/star.mjs
18382
+ var Star = [
18383
+ [
18384
+ "path",
18385
+ {
18386
+ d: "M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z"
18387
+ }
18388
+ ]
18389
+ ];
18390
+
18313
18391
  // ../../node_modules/lucide/dist/esm/icons/x.mjs
18314
18392
  var X = [
18315
18393
  ["path", { d: "M18 6 6 18" }],
@@ -18323,6 +18401,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18323
18401
  var RENDERER_SETTINGS_ICON_NAMES = [
18324
18402
  "settings",
18325
18403
  "close",
18404
+ "star",
18326
18405
  "language",
18327
18406
  "connections",
18328
18407
  "model-pool",
@@ -18336,6 +18415,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18336
18415
  var iconNodes = {
18337
18416
  settings: Settings,
18338
18417
  close: X,
18418
+ star: Star,
18339
18419
  language: Languages,
18340
18420
  connections: PlugZap,
18341
18421
  "model-pool": Boxes,
@@ -18522,6 +18602,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18522
18602
 
18523
18603
  // src/settings/pages.ts
18524
18604
  var CODEXHOST_RELEASES_LATEST_URL = "https://github.com/BytePioneer-AI/codex-host/releases/latest";
18605
+ var CODEXHOST_NPM_MANUAL_UPDATE_COMMAND = "npm install -g @codexhost/cli@latest";
18525
18606
  var DEFAULT_RENDERER_SETTINGS_PAGE_IDS = [
18526
18607
  "connections",
18527
18608
  "model-pool",
@@ -18530,19 +18611,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18530
18611
  "updates"
18531
18612
  ];
18532
18613
  function appendUnavailableStatus(content, messages) {
18533
- const heading = content.ownerDocument.createElement("div");
18534
- heading.className = "settings-section-label";
18535
- heading.textContent = messages.availability;
18536
18614
  const status = content.ownerDocument.createElement("div");
18537
18615
  status.className = "settings-empty";
18538
18616
  const copy = content.ownerDocument.createElement("div");
18539
18617
  const title = content.ownerDocument.createElement("strong");
18540
- title.textContent = messages.notAvailable;
18541
- const detail = content.ownerDocument.createElement("span");
18542
- detail.textContent = messages.runtimeCapabilityNotInstalled;
18543
- copy.append(title, detail);
18618
+ title.textContent = messages.inDevelopment;
18619
+ copy.append(title);
18544
18620
  status.append(copy);
18545
- content.append(heading, status);
18621
+ content.append(status);
18546
18622
  }
18547
18623
  function unavailablePage(id, messages) {
18548
18624
  return Object.freeze({
@@ -18580,6 +18656,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18580
18656
  if (!status) return null;
18581
18657
  if (status.phase === "succeeded") return messages.updateSucceeded;
18582
18658
  if (status.phase === "failed") return status.error ?? messages.updateFailed;
18659
+ if (status.phase === "waiting-for-exit") return messages.updateWaitingForExit;
18660
+ if (status.phase === "installing") {
18661
+ return status.installation === "npm" ? messages.updateInstallingNpm : messages.updateInstalling;
18662
+ }
18583
18663
  if (status.phase === "restarting") return messages.updateRestarting;
18584
18664
  if (status.phase === "downloading") return messages.updateDownloading;
18585
18665
  return messages.updatePreparing;
@@ -18626,6 +18706,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18626
18706
  const panel = document2.createElement("section");
18627
18707
  panel.className = "settings-update-panel";
18628
18708
  panel.setAttribute("aria-live", "polite");
18709
+ const manualNpm = document2.createElement("div");
18710
+ manualNpm.className = "settings-update-manual";
18711
+ manualNpm.hidden = true;
18712
+ const manualNpmDescription = document2.createElement("span");
18713
+ manualNpmDescription.textContent = messages.updateManualNpmDescription;
18714
+ const manualNpmCommand = document2.createElement("code");
18715
+ manualNpmCommand.textContent = CODEXHOST_NPM_MANUAL_UPDATE_COMMAND;
18716
+ manualNpm.append(manualNpmDescription, manualNpmCommand);
18629
18717
  const actions = document2.createElement("div");
18630
18718
  actions.className = "settings-update-actions";
18631
18719
  const releaseLink = document2.createElement("a");
@@ -18638,7 +18726,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18638
18726
  createRendererSettingsIcon("external-link", 14)
18639
18727
  );
18640
18728
  actions.append(releaseLink);
18641
- context.content.append(heading, metadata, panel, actions);
18729
+ context.content.append(heading, metadata, panel, manualNpm, actions);
18642
18730
  let pollTimer;
18643
18731
  let pollAttempts = 0;
18644
18732
  let pending = false;
@@ -18743,6 +18831,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18743
18831
  const renderCheck = (result, client) => {
18744
18832
  currentVersionValue.textContent = `v${result.currentVersion}`;
18745
18833
  installationValue.textContent = installationLabel(result.installation, messages);
18834
+ manualNpm.hidden = result.installation !== "npm";
18746
18835
  if (result.releaseNotesUrl) releaseLink.href = result.releaseNotesUrl;
18747
18836
  const operationMessage = statusMessage(result.status, messages);
18748
18837
  if (isPendingStatus(result.status)) {
@@ -18834,10 +18923,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18834
18923
  }
18835
18924
 
18836
18925
  // src/settings/shell.css
18837
- var shell_default = ':host {\n --settings-bg: #181818;\n --settings-sidebar: #1c1c1c;\n --settings-panel: transparent;\n --settings-text: #f5f5f5;\n --settings-muted: #a1a1a1;\n --settings-border: rgb(255 255 255 / 10%);\n --settings-divider: rgb(255 255 255 / 10%);\n --settings-hover: rgb(255 255 255 / 6%);\n --settings-active: rgb(51 156 255 / 12%);\n --settings-focus: #339cff;\n color: var(--settings-text);\n color-scheme: dark;\n font:\n 14px/1.5 system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n letter-spacing: 0;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n letter-spacing: 0;\n}\n\nbutton,\ninput,\nselect {\n font: inherit;\n}\n\nbutton {\n color: inherit;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.codexhost-settings-dialog {\n width: min(1120px, calc(100vw - 32px));\n height: min(780px, calc(100vh - 32px));\n max-width: none;\n max-height: none;\n margin: auto;\n padding: 0;\n overflow: hidden;\n color: var(--settings-text);\n background: var(--settings-bg);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n box-shadow: 0 24px 64px rgb(0 0 0 / 38%);\n}\n\n.codexhost-settings-dialog::backdrop {\n background: rgb(0 0 0 / 52%);\n}\n\n.settings-frame,\n.settings-layout {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n}\n\n.settings-frame {\n display: flex;\n flex-direction: column;\n}\n\n.settings-layout {\n flex: 1;\n display: grid;\n grid-template-columns: 240px minmax(0, 1fr);\n background: var(--settings-bg);\n}\n\n.settings-sidebar {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--settings-sidebar);\n border-right: 1px solid var(--settings-border);\n padding-top: 28px;\n}\n\n.settings-header {\n display: flex;\n align-items: center;\n min-width: 0;\n height: 64px;\n flex: none;\n padding: 0 16px;\n border-bottom: 1px solid var(--settings-border);\n}\n\n.settings-brand {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-brand__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n flex: none;\n color: var(--settings-text);\n}\n\n.settings-brand__mark .codexhost-settings-icon {\n width: 32px;\n height: 32px;\n object-fit: contain;\n}\n\n.settings-brand__copy {\n display: flex;\n min-width: 0;\n align-items: baseline;\n gap: 0;\n line-height: 20px;\n}\n\n.settings-brand__name {\n overflow: hidden;\n color: var(--settings-text);\n font-size: 14px;\n font-weight: 500;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-brand__title {\n margin-left: 14px;\n padding-left: 14px;\n color: var(--settings-muted);\n font-size: 14px;\n font-weight: 400;\n border-left: 1px solid var(--settings-border);\n}\n\n.settings-nav {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n padding: 0 12px 16px;\n overflow-y: auto;\n}\n\n.settings-nav-button {\n display: grid;\n grid-template-columns: 16px minmax(0, 1fr);\n align-items: center;\n width: 100%;\n position: relative;\n min-height: 40px;\n flex: none;\n gap: 12px;\n padding: 8px 12px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 21px;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.settings-nav-button .codexhost-settings-icon {\n width: 18px;\n height: 18px;\n opacity: 0.9;\n}\n\n.settings-nav-button:hover {\n background: var(--settings-hover);\n}\n\n.settings-nav-button[aria-current="page"] {\n background: var(--settings-active);\n color: var(--settings-focus);\n}\n\n.settings-nav-button[aria-current="page"]::before {\n position: absolute;\n left: 0;\n width: 3px;\n height: 22px;\n content: "";\n background: var(--settings-focus);\n border-radius: 0 3px 3px 0;\n}\n\n.settings-nav-button span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n padding: 0;\n color: var(--settings-muted);\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.settings-icon-button:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-icon-button:focus-visible,\n.settings-nav-button:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 1px;\n}\n\n.settings-page {\n display: block;\n position: relative;\n min-width: 0;\n min-height: 0;\n overflow-y: auto;\n background: var(--settings-bg);\n scrollbar-gutter: stable;\n}\n\n.settings-page__content {\n width: min(930px, calc(100% - 80px));\n margin-inline: auto;\n}\n\n.settings-page__content {\n min-width: 0;\n padding: 44px 0 56px;\n}\n\n.settings-section-label {\n min-height: auto;\n padding: 0 0 28px;\n color: var(--settings-text);\n font-size: 24px;\n font-weight: 600;\n line-height: 30px;\n}\n\n.settings-status-list,\n.settings-empty {\n overflow: hidden;\n background: var(--settings-panel);\n}\n\n.settings-status-row {\n display: grid;\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\n position: relative;\n align-items: center;\n min-height: 88px;\n gap: 28px;\n padding: 16px 0;\n}\n\n.settings-status-row:not(:last-child)::after {\n content: "";\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 1px;\n background: var(--settings-divider);\n}\n\n.settings-status-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--settings-text);\n gap: 14px;\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-status-row__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n flex: none;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 7%);\n border-radius: 50%;\n}\n\n.settings-status-badge {\n flex: none;\n padding: 5px 12px;\n color: var(--settings-muted);\n font-size: 13px;\n font-weight: 500;\n line-height: 18px;\n background: rgb(255 255 255 / 9%);\n border: 1px solid rgb(255 255 255 / 6%);\n border-radius: 6px;\n}\n\n.settings-status-row__detail {\n min-width: 0;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-empty {\n display: flex;\n align-items: center;\n min-height: 72px;\n padding: 12px 16px;\n}\n\n.settings-empty > div {\n display: grid;\n min-width: 0;\n gap: 2px;\n}\n\n.settings-empty strong {\n color: var(--settings-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 19px;\n}\n\n.settings-empty span {\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 19px;\n}\n\n.settings-page-error {\n padding: 16px;\n color: var(--settings-text);\n font-size: 13px;\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 20px;\n}\n\n.settings-update-metadata {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 24px;\n margin-bottom: 24px;\n padding: 0 4px 20px;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-update-metadata__item {\n display: grid;\n min-width: 0;\n gap: 4px;\n}\n\n.settings-update-metadata__item span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-metadata__item strong {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-update-panel {\n display: grid;\n gap: 16px;\n min-height: 128px;\n padding: 20px;\n color: var(--settings-text);\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n}\n\n.settings-update-panel > strong,\n.settings-update-panel > span,\n.settings-update-summary,\n.settings-update-error {\n margin: 0;\n overflow-wrap: anywhere;\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-panel > span,\n.settings-update-summary {\n color: var(--settings-muted);\n}\n\n.settings-update-error {\n color: #ef4444;\n}\n\n.settings-update-progress {\n width: 100%;\n height: 8px;\n accent-color: #238be8;\n}\n\n.settings-update-progress-detail {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-notes {\n max-height: 240px;\n padding: 14px;\n overflow: auto;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n background: rgb(255 255 255 / 4%);\n border: 1px solid var(--settings-divider);\n border-radius: 6px;\n}\n\n.settings-update-version-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: 20px;\n min-height: 32px;\n color: var(--settings-muted);\n font-size: 13px;\n}\n\n.settings-update-version-row strong {\n color: var(--settings-text);\n font-size: 15px;\n}\n\n.settings-update-actions {\n display: flex;\n min-width: 0;\n margin-top: 16px;\n}\n\n.settings-command-button,\n.settings-update-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n min-height: 36px;\n gap: 8px;\n padding: 8px 12px;\n color: white;\n font: inherit;\n font-size: 13px;\n text-decoration: none;\n background: #1677d2;\n border: 1px solid #238be8;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.settings-command-button--secondary,\n.settings-update-link {\n color: var(--settings-text);\n background: transparent;\n border-color: var(--settings-border);\n}\n\n.settings-command-button:hover,\n.settings-update-link:hover {\n filter: brightness(1.08);\n}\n\n.settings-command-button:focus-visible,\n.settings-update-link:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 2px;\n}\n\n.codexhost-settings-icon {\n display: block;\n flex: none;\n stroke: currentColor;\n}\n\n@media (max-width: 720px) {\n .codexhost-settings-dialog {\n width: calc(100vw - 16px);\n height: calc(100vh - 16px);\n border-radius: 10px;\n }\n\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n }\n\n .settings-sidebar {\n border-right: 0;\n border-bottom: 1px solid var(--settings-border);\n }\n\n .settings-header {\n height: 56px;\n padding-inline: 14px;\n }\n\n .settings-sidebar {\n padding-top: 20px;\n }\n\n .settings-nav {\n flex: none;\n flex-direction: row;\n gap: 4px;\n padding: 7px 8px 8px;\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .settings-nav-button {\n width: auto;\n min-width: max-content;\n min-height: 34px;\n grid-template-columns: 16px auto;\n border-radius: 10px;\n }\n\n .settings-page__content {\n width: calc(100% - 40px);\n }\n\n .settings-page__content {\n padding-top: 32px;\n padding-bottom: 32px;\n }\n\n .settings-section-label {\n padding-bottom: 20px;\n font-size: 20px;\n line-height: 26px;\n }\n\n .settings-status-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 16px;\n }\n\n .settings-status-row__detail {\n grid-column: 1 / -1;\n margin: -8px 0 0 58px;\n }\n}\n\n@media (forced-colors: active) {\n :host,\n :host([data-theme="dark"]) {\n --settings-bg: Canvas;\n --settings-sidebar: Canvas;\n --settings-panel: Canvas;\n --settings-text: CanvasText;\n --settings-muted: GrayText;\n --settings-border: ButtonBorder;\n --settings-divider: ButtonBorder;\n --settings-hover: Highlight;\n --settings-active: Highlight;\n --settings-focus: Highlight;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n }\n}\n';
18926
+ var shell_default = ':host {\n --settings-bg: #181818;\n --settings-sidebar: #1c1c1c;\n --settings-panel: transparent;\n --settings-text: #f5f5f5;\n --settings-muted: #a1a1a1;\n --settings-border: rgb(255 255 255 / 10%);\n --settings-divider: rgb(255 255 255 / 10%);\n --settings-hover: rgb(255 255 255 / 6%);\n --settings-active: rgb(51 156 255 / 12%);\n --settings-focus: #339cff;\n color: var(--settings-text);\n color-scheme: dark;\n font:\n 14px/1.5 system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n letter-spacing: 0;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n letter-spacing: 0;\n}\n\nbutton,\ninput,\nselect {\n font: inherit;\n}\n\nbutton {\n color: inherit;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.codexhost-settings-dialog {\n width: min(1120px, calc(100vw - 32px));\n height: min(780px, calc(100vh - 32px));\n max-width: none;\n max-height: none;\n margin: auto;\n padding: 0;\n overflow: hidden;\n color: var(--settings-text);\n background: var(--settings-bg);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n box-shadow: 0 24px 64px rgb(0 0 0 / 38%);\n}\n\n.codexhost-settings-dialog::backdrop {\n background: rgb(0 0 0 / 52%);\n}\n\n.settings-frame,\n.settings-layout {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n}\n\n.settings-frame {\n display: flex;\n flex-direction: column;\n}\n\n.settings-layout {\n flex: 1;\n display: grid;\n grid-template-columns: 240px minmax(0, 1fr);\n background: var(--settings-bg);\n}\n\n.settings-sidebar {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--settings-sidebar);\n border-right: 1px solid var(--settings-border);\n padding-top: 28px;\n}\n\n.settings-header {\n display: flex;\n align-items: center;\n min-width: 0;\n height: 64px;\n flex: none;\n padding: 0 16px;\n border-bottom: 1px solid var(--settings-border);\n}\n\n.settings-header-actions {\n display: flex;\n align-items: center;\n flex: none;\n gap: 12px;\n margin-left: auto;\n}\n\n.settings-icon-button.settings-star-link {\n width: auto;\n gap: 6px;\n padding-inline: 8px;\n font-size: 12px;\n line-height: 18px;\n text-decoration: none;\n border: 2px dashed #f5c542;\n white-space: nowrap;\n}\n\n.settings-star-link .codexhost-settings-icon {\n color: #f5c542;\n fill: currentColor;\n stroke: currentColor;\n}\n\n.settings-brand {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-brand__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n flex: none;\n color: var(--settings-text);\n}\n\n.settings-brand__mark .codexhost-settings-icon {\n width: 32px;\n height: 32px;\n object-fit: contain;\n}\n\n.settings-brand__copy {\n display: flex;\n min-width: 0;\n align-items: baseline;\n gap: 0;\n line-height: 20px;\n}\n\n.settings-brand__name {\n overflow: hidden;\n color: var(--settings-text);\n font-size: 14px;\n font-weight: 500;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-brand__title {\n margin-left: 14px;\n padding-left: 14px;\n color: var(--settings-muted);\n font-size: 14px;\n font-weight: 400;\n border-left: 1px solid var(--settings-border);\n}\n\n.settings-nav {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n padding: 0 12px 16px;\n overflow-y: auto;\n}\n\n.settings-nav-button {\n display: grid;\n grid-template-columns: 16px minmax(0, 1fr);\n align-items: center;\n width: 100%;\n position: relative;\n min-height: 40px;\n flex: none;\n gap: 12px;\n padding: 8px 12px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 21px;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.settings-nav-button .codexhost-settings-icon {\n width: 18px;\n height: 18px;\n opacity: 0.9;\n}\n\n.settings-nav-button:hover {\n background: var(--settings-hover);\n}\n\n.settings-nav-button[aria-current="page"] {\n background: var(--settings-active);\n color: var(--settings-focus);\n}\n\n.settings-nav-button[aria-current="page"]::before {\n position: absolute;\n left: 0;\n width: 3px;\n height: 22px;\n content: "";\n background: var(--settings-focus);\n border-radius: 0 3px 3px 0;\n}\n\n.settings-nav-button span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n padding: 0;\n color: var(--settings-muted);\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.settings-icon-button:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-icon-button:focus-visible,\n.settings-nav-button:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 1px;\n}\n\n.settings-page {\n display: block;\n position: relative;\n min-width: 0;\n min-height: 0;\n overflow-y: auto;\n background: var(--settings-bg);\n scrollbar-gutter: stable;\n}\n\n.settings-page__content {\n width: min(930px, calc(100% - 80px));\n margin-inline: auto;\n}\n\n.settings-page__content {\n min-width: 0;\n padding: 44px 0 56px;\n}\n\n.settings-section-label {\n min-height: auto;\n padding: 0 0 28px;\n color: var(--settings-text);\n font-size: 24px;\n font-weight: 600;\n line-height: 30px;\n}\n\n.settings-status-list,\n.settings-empty {\n overflow: hidden;\n background: var(--settings-panel);\n}\n\n.settings-status-row {\n display: grid;\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\n position: relative;\n align-items: center;\n min-height: 88px;\n gap: 28px;\n padding: 16px 0;\n}\n\n.settings-status-row:not(:last-child)::after {\n content: "";\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 1px;\n background: var(--settings-divider);\n}\n\n.settings-status-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--settings-text);\n gap: 14px;\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-status-row__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n flex: none;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 7%);\n border-radius: 50%;\n}\n\n.settings-status-badge {\n flex: none;\n padding: 5px 12px;\n color: var(--settings-muted);\n font-size: 13px;\n font-weight: 500;\n line-height: 18px;\n background: rgb(255 255 255 / 9%);\n border: 1px solid rgb(255 255 255 / 6%);\n border-radius: 6px;\n}\n\n.settings-status-row__detail {\n min-width: 0;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-empty {\n display: flex;\n align-items: center;\n min-height: 72px;\n padding: 12px 16px;\n}\n\n.settings-empty > div {\n display: grid;\n min-width: 0;\n gap: 2px;\n}\n\n.settings-empty strong {\n color: var(--settings-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 19px;\n}\n\n.settings-empty span {\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 19px;\n}\n\n.settings-page-error {\n padding: 16px;\n color: var(--settings-text);\n font-size: 13px;\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 20px;\n}\n\n.settings-update-metadata {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 24px;\n margin-bottom: 24px;\n padding: 0 4px 20px;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-update-metadata__item {\n display: grid;\n min-width: 0;\n gap: 4px;\n}\n\n.settings-update-metadata__item span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-metadata__item strong {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-update-panel {\n display: grid;\n gap: 16px;\n min-height: 128px;\n padding: 20px;\n color: var(--settings-text);\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n}\n\n.settings-update-panel > strong,\n.settings-update-panel > span,\n.settings-update-summary,\n.settings-update-error {\n margin: 0;\n overflow-wrap: anywhere;\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-panel > span,\n.settings-update-summary {\n color: var(--settings-muted);\n}\n\n.settings-update-error {\n color: #ef4444;\n}\n\n.settings-update-progress {\n width: 100%;\n height: 8px;\n accent-color: #238be8;\n}\n\n.settings-update-progress-detail {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-notes {\n max-height: 240px;\n padding: 14px;\n overflow: auto;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n background: rgb(255 255 255 / 4%);\n border: 1px solid var(--settings-divider);\n border-radius: 6px;\n}\n\n.settings-update-version-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: 20px;\n min-height: 32px;\n color: var(--settings-muted);\n font-size: 13px;\n}\n\n.settings-update-version-row strong {\n color: var(--settings-text);\n font-size: 15px;\n}\n\n.settings-update-manual {\n display: grid;\n min-width: 0;\n gap: 6px;\n margin-top: 14px;\n padding-inline: 4px;\n}\n\n.settings-update-manual[hidden] {\n display: none;\n}\n\n.settings-update-manual span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-manual code {\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-actions {\n display: flex;\n min-width: 0;\n margin-top: 16px;\n}\n\n.settings-command-button,\n.settings-update-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n min-height: 36px;\n gap: 8px;\n padding: 8px 12px;\n color: white;\n font: inherit;\n font-size: 13px;\n text-decoration: none;\n background: #1677d2;\n border: 1px solid #238be8;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.settings-command-button--secondary,\n.settings-update-link {\n color: var(--settings-text);\n background: transparent;\n border-color: var(--settings-border);\n}\n\n.settings-command-button:hover,\n.settings-update-link:hover {\n filter: brightness(1.08);\n}\n\n.settings-command-button:focus-visible,\n.settings-update-link:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 2px;\n}\n\n.codexhost-settings-icon {\n display: block;\n flex: none;\n stroke: currentColor;\n}\n\n@media (max-width: 720px) {\n .codexhost-settings-dialog {\n width: calc(100vw - 16px);\n height: calc(100vh - 16px);\n border-radius: 10px;\n }\n\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n }\n\n .settings-sidebar {\n border-right: 0;\n border-bottom: 1px solid var(--settings-border);\n }\n\n .settings-header {\n height: 56px;\n padding-inline: 14px;\n }\n\n .settings-icon-button.settings-star-link {\n width: 28px;\n padding: 0;\n }\n\n .settings-star-link span {\n display: none;\n }\n\n .settings-sidebar {\n padding-top: 20px;\n }\n\n .settings-nav {\n flex: none;\n flex-direction: row;\n gap: 4px;\n padding: 7px 8px 8px;\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .settings-nav-button {\n width: auto;\n min-width: max-content;\n min-height: 34px;\n grid-template-columns: 16px auto;\n border-radius: 10px;\n }\n\n .settings-page__content {\n width: calc(100% - 40px);\n }\n\n .settings-page__content {\n padding-top: 32px;\n padding-bottom: 32px;\n }\n\n .settings-section-label {\n padding-bottom: 20px;\n font-size: 20px;\n line-height: 26px;\n }\n\n .settings-status-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 16px;\n }\n\n .settings-status-row__detail {\n grid-column: 1 / -1;\n margin: -8px 0 0 58px;\n }\n}\n\n@media (forced-colors: active) {\n :host,\n :host([data-theme="dark"]) {\n --settings-bg: Canvas;\n --settings-sidebar: Canvas;\n --settings-panel: Canvas;\n --settings-text: CanvasText;\n --settings-muted: GrayText;\n --settings-border: ButtonBorder;\n --settings-divider: ButtonBorder;\n --settings-hover: Highlight;\n --settings-active: Highlight;\n --settings-focus: Highlight;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n }\n}\n';
18838
18927
 
18839
18928
  // src/settings/shell.ts
18840
18929
  var SETTINGS_SHELL_ATTRIBUTE = "data-codexhost-settings-shell";
18930
+ var CODEXHOST_GITHUB_REPOSITORY_URL = "https://github.com/BytePioneer-AI/codex-host";
18841
18931
  function isRendererSettingsDialogSupported(dialog) {
18842
18932
  return typeof dialog.showModal === "function" && typeof dialog.close === "function";
18843
18933
  }
@@ -18882,13 +18972,26 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18882
18972
  brandTitle.textContent = messages.title;
18883
18973
  brandCopy.append(brandName, brandTitle);
18884
18974
  brand.append(brandMark, brandCopy);
18975
+ const headerActions = ownerDocument.createElement("div");
18976
+ headerActions.className = "settings-header-actions";
18977
+ const starLink = ownerDocument.createElement("a");
18978
+ starLink.className = "settings-icon-button settings-star-link";
18979
+ starLink.href = CODEXHOST_GITHUB_REPOSITORY_URL;
18980
+ starLink.target = "_blank";
18981
+ starLink.rel = "noopener noreferrer";
18982
+ starLink.setAttribute("aria-label", messages.starOnGitHub);
18983
+ starLink.title = messages.starOnGitHub;
18984
+ const starLabel = ownerDocument.createElement("span");
18985
+ starLabel.textContent = messages.starOnGitHub;
18986
+ starLink.append(createRendererSettingsIcon("star", 16), starLabel);
18885
18987
  const closeButton = ownerDocument.createElement("button");
18886
18988
  closeButton.type = "button";
18887
18989
  closeButton.className = "settings-icon-button";
18888
18990
  closeButton.setAttribute("aria-label", messages.close);
18889
18991
  closeButton.title = messages.close;
18890
18992
  closeButton.append(createRendererSettingsIcon("close", 18));
18891
- header.append(brand, closeButton);
18993
+ headerActions.append(starLink, closeButton);
18994
+ header.append(brand, headerActions);
18892
18995
  const layout = ownerDocument.createElement("div");
18893
18996
  layout.className = "settings-layout";
18894
18997
  const sidebar = ownerDocument.createElement("aside");
@@ -19573,6 +19676,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19573
19676
  let adapterDispose = null;
19574
19677
  let applyAdapterAgent = null;
19575
19678
  let modelControl = null;
19679
+ let usageNotificationDispose = null;
19576
19680
  const sidebarAgentIcons = installRendererSidebarAgentIcons({
19577
19681
  getClient: () => modelControl
19578
19682
  });
@@ -19629,6 +19733,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19629
19733
  mounted.usage
19630
19734
  );
19631
19735
  };
19736
+ const applyThreadUsageUpdate = (update) => {
19737
+ for (const mounted of mountedByComposer.values()) {
19738
+ if (threadIdFromComposerModelTarget(mounted.modelTarget) !== update.threadId) continue;
19739
+ mounted.usageRequestGeneration += 1;
19740
+ mounted.usage = update.usage;
19741
+ usageRefreshAttempts.delete(mounted.composer);
19742
+ renderMounted(mounted);
19743
+ }
19744
+ };
19632
19745
  const refreshThreadUsage = async (mounted) => {
19633
19746
  const threadId = threadIdFromComposerModelTarget(mounted.modelTarget);
19634
19747
  if (!threadId || !modelControl || controller.get(mounted.composer).agent === "codex") {
@@ -19692,6 +19805,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19692
19805
  return;
19693
19806
  }
19694
19807
  const generation = controller.beginOwnershipRequest(mounted.composer);
19808
+ const usageGeneration = mounted.usageRequestGeneration;
19695
19809
  mounted.ownershipStatus = "loading";
19696
19810
  renderMounted(mounted);
19697
19811
  try {
@@ -19701,7 +19815,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19701
19815
  return;
19702
19816
  }
19703
19817
  const { agent, model, thinkingOptionId, permissionModeId } = restoredThreadOwnership(inspection);
19704
- mounted.usage = inspection.owner === "external" ? inspection.usage ?? null : null;
19818
+ if (mounted.usageRequestGeneration === usageGeneration) {
19819
+ mounted.usage = inspection.owner === "external" ? inspection.usage ?? null : null;
19820
+ }
19705
19821
  const restored = controller.restore(
19706
19822
  mounted.composer,
19707
19823
  agent,
@@ -20602,15 +20718,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20602
20718
  };
20603
20719
  const mutationObserver = new MutationObserver((mutations) => {
20604
20720
  transferReplacedComposers(mutations);
20605
- for (const mounted of mountedByComposer.values()) {
20606
- const anchor = nativeContextUsageControlForComposer(mounted.composer);
20607
- if (!anchor) continue;
20608
- const changed = mutations.some((mutation) => {
20609
- const target = mutation.target instanceof Element ? mutation.target : mutation.target.parentElement;
20610
- return target === anchor || target === anchor.parentElement || anchor.contains(target);
20611
- });
20612
- if (changed) scheduleThreadUsageRefresh(mounted);
20613
- }
20614
20721
  scheduleScan(mutations.some(mutationMayChangeComposerTarget));
20615
20722
  });
20616
20723
  const onAdapterStatus = () => {
@@ -20682,10 +20789,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20682
20789
  return startCompatibilityUpdate(modelControl);
20683
20790
  },
20684
20791
  setAdapter(status, dispose, applyAgent, nextModelControl) {
20792
+ usageNotificationDispose?.();
20793
+ usageNotificationDispose = null;
20685
20794
  adapterDispose?.();
20686
20795
  adapterDispose = dispose ?? null;
20687
20796
  applyAdapterAgent = applyAgent ?? null;
20688
20797
  modelControl = nextModelControl ?? null;
20798
+ try {
20799
+ usageNotificationDispose = modelControl?.subscribeThreadUsage?.(applyThreadUsageUpdate) ?? null;
20800
+ } catch {
20801
+ usageNotificationDispose = null;
20802
+ }
20689
20803
  adapterStatus = status;
20690
20804
  const installedModelControl = modelControl;
20691
20805
  queueMicrotask(() => {
@@ -20716,6 +20830,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20716
20830
  dispose() {
20717
20831
  if (disposed) return;
20718
20832
  disposed = true;
20833
+ usageNotificationDispose?.();
20834
+ usageNotificationDispose = null;
20719
20835
  adapterDispose?.();
20720
20836
  adapterDispose = null;
20721
20837
  applyAdapterAgent = null;
@@ -20791,6 +20907,7 @@ lucide/dist/esm/icons/plug-zap.mjs:
20791
20907
  lucide/dist/esm/icons/refresh-cw.mjs:
20792
20908
  lucide/dist/esm/icons/route.mjs:
20793
20909
  lucide/dist/esm/icons/settings.mjs:
20910
+ lucide/dist/esm/icons/star.mjs:
20794
20911
  lucide/dist/esm/icons/x.mjs:
20795
20912
  (**
20796
20913
  * @license lucide v1.28.0 - ISC
package/bin/codexhost CHANGED
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codexhost/cli-darwin-x64",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Run Pi and Claude Code as first-class external harnesses inside Codex Desktop.",
5
5
  "type": "module",
6
6
  "files": [