@codexhost/cli-darwin-x64 0.11.0 → 0.12.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.
@@ -23,7 +23,8 @@
23
23
  "hermes",
24
24
  "qoder",
25
25
  "qoder-cn",
26
- "kimi-code"
26
+ "kimi-code",
27
+ "zcode"
27
28
  ];
28
29
  var DEFAULT_RENDERER_AGENTS = KNOWN_RENDERER_AGENTS;
29
30
  function defaultIdFactory(sequence) {
@@ -163,6 +164,8 @@
163
164
  else if (agent === "qoder-cn") delete state.qoderCnModel;
164
165
  if (agent === "kimi-code" && model) state.kimiCodeModel = model;
165
166
  else if (agent === "kimi-code") delete state.kimiCodeModel;
167
+ if (agent === "zcode" && model) state.zcodeModel = model;
168
+ else if (agent === "zcode") delete state.zcodeModel;
166
169
  if (agent === "pi" && thinkingOptionId) state.piThinkingOptionId = thinkingOptionId;
167
170
  else if (agent === "pi") delete state.piThinkingOptionId;
168
171
  if (agent === "claude-code" && thinkingOptionId) {
@@ -196,6 +199,9 @@
196
199
  if (agent === "kimi-code" && thinkingOptionId) {
197
200
  state.kimiCodeThinkingOptionId = thinkingOptionId;
198
201
  } else if (agent === "kimi-code") delete state.kimiCodeThinkingOptionId;
202
+ if (agent === "zcode" && thinkingOptionId) {
203
+ state.zcodeThinkingOptionId = thinkingOptionId;
204
+ } else if (agent === "zcode") delete state.zcodeThinkingOptionId;
199
205
  if (agent !== "codex") {
200
206
  const permissionModeByAgent = {};
201
207
  for (const candidate of [
@@ -213,7 +219,8 @@
213
219
  "hermes",
214
220
  "qoder",
215
221
  "qoder-cn",
216
- "kimi-code"
222
+ "kimi-code",
223
+ "zcode"
217
224
  ]) {
218
225
  const current = state.permissionModeByAgent?.[candidate];
219
226
  if (candidate !== agent && current) permissionModeByAgent[candidate] = current;
@@ -244,6 +251,7 @@
244
251
  if (agent === "qoder") return state.qoderModel;
245
252
  if (agent === "qoder-cn") return state.qoderCnModel;
246
253
  if (agent === "kimi-code") return state.kimiCodeModel;
254
+ if (agent === "zcode") return state.zcodeModel;
247
255
  return void 0;
248
256
  }
249
257
  thinkingOptionForAgent(composer, agent) {
@@ -260,6 +268,7 @@
260
268
  if (agent === "qoder") return state.qoderThinkingOptionId;
261
269
  if (agent === "qoder-cn") return state.qoderCnThinkingOptionId;
262
270
  if (agent === "kimi-code") return state.kimiCodeThinkingOptionId;
271
+ if (agent === "zcode") return state.zcodeThinkingOptionId;
263
272
  return void 0;
264
273
  }
265
274
  permissionModeForAgent(composer, agent) {
@@ -290,6 +299,7 @@
290
299
  else if (agent === "qoder") state.qoderModel = model;
291
300
  else if (agent === "qoder-cn") state.qoderCnModel = model;
292
301
  else if (agent === "kimi-code") state.kimiCodeModel = model;
302
+ else if (agent === "zcode") state.zcodeModel = model;
293
303
  else state.antigravityModel = model;
294
304
  return state;
295
305
  }
@@ -351,6 +361,10 @@
351
361
  state.kimiCodeThinkingOptionId = thinkingOptionId;
352
362
  } else if (agent === "kimi-code") {
353
363
  delete state.kimiCodeThinkingOptionId;
364
+ } else if (agent === "zcode" && thinkingOptionId) {
365
+ state.zcodeThinkingOptionId = thinkingOptionId;
366
+ } else if (agent === "zcode") {
367
+ delete state.zcodeThinkingOptionId;
354
368
  }
355
369
  return state;
356
370
  }
@@ -439,6 +453,213 @@
439
453
  }
440
454
  };
441
455
 
456
+ // src/codex-ssh-adapter.ts
457
+ var record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
458
+ function createCodexSshClient(ownerWindow) {
459
+ function request(operation, params, signal) {
460
+ const bridge = ownerWindow.electronBridge;
461
+ if (!bridge?.sendMessageFromView)
462
+ return Promise.reject(new Error("Codex SSH settings are unavailable in this window"));
463
+ if (signal?.aborted) return Promise.reject(new DOMException("Aborted", "AbortError"));
464
+ return new Promise((resolve, reject) => {
465
+ const requestId = ownerWindow.crypto.randomUUID();
466
+ const finish = (error51, value) => {
467
+ ownerWindow.clearTimeout(timer);
468
+ ownerWindow.removeEventListener("message", receive);
469
+ signal?.removeEventListener("abort", abort);
470
+ if (error51) reject(error51);
471
+ else resolve(value);
472
+ };
473
+ const abort = () => finish(new DOMException("Aborted", "AbortError"));
474
+ const receive = (event) => {
475
+ const message = event.data;
476
+ if (!record(message) || message.type !== "fetch-response" || message.requestId !== requestId)
477
+ return;
478
+ if (message.responseType !== "success" || typeof message.status !== "number" || message.status < 200 || message.status >= 300 || typeof message.bodyJsonString !== "string") {
479
+ finish(new Error("Codex SSH request failed; check the native Connections settings"));
480
+ return;
481
+ }
482
+ try {
483
+ finish(null, JSON.parse(message.bodyJsonString));
484
+ } catch {
485
+ finish(new Error("Invalid Codex SSH response"));
486
+ }
487
+ };
488
+ const timer = ownerWindow.setTimeout(
489
+ () => finish(new Error("Codex SSH request timed out; refresh before retrying")),
490
+ 15e3
491
+ );
492
+ ownerWindow.addEventListener("message", receive);
493
+ signal?.addEventListener("abort", abort, { once: true });
494
+ try {
495
+ Promise.resolve(
496
+ bridge.sendMessageFromView({
497
+ type: "fetch",
498
+ requestId,
499
+ method: "POST",
500
+ url: `vscode://codex/${operation}`,
501
+ body: JSON.stringify(params),
502
+ reportUploadProgress: false
503
+ })
504
+ ).catch((error51) => finish(error51));
505
+ } catch (error51) {
506
+ finish(error51);
507
+ }
508
+ });
509
+ }
510
+ async function list(signal) {
511
+ const result = await request("refresh-remote-connections", {}, signal);
512
+ if (!record(result) || !Array.isArray(result.remoteConnections))
513
+ throw new Error("Invalid Codex SSH connection list");
514
+ return result.remoteConnections.filter(
515
+ (item) => !(record(item) && ["wsl", "remote-control"].includes(String(item.source)))
516
+ ).map((item) => {
517
+ if (!record(item) || typeof item.hostId !== "string" || typeof item.displayName !== "string" || !["codex-managed", "discovered"].includes(String(item.source)) || typeof item.sshHost !== "string" || typeof item.autoConnect !== "boolean" || !(item.sshAlias == null || typeof item.sshAlias === "string") || !(item.identity == null || typeof item.identity === "string") || !(item.sshPort == null || Number.isInteger(item.sshPort) && Number(item.sshPort) > 0 && Number(item.sshPort) <= 65535)) {
518
+ throw new Error(
519
+ "This Codex version uses an unsupported SSH configuration; open native settings"
520
+ );
521
+ }
522
+ return {
523
+ ...item,
524
+ sshAlias: item.sshAlias ?? null,
525
+ identity: item.identity ?? null,
526
+ sshPort: item.sshPort ?? null
527
+ };
528
+ });
529
+ }
530
+ function saved(connection) {
531
+ return {
532
+ hostId: connection.hostId,
533
+ displayName: connection.displayName,
534
+ source: connection.source,
535
+ alias: connection.sshAlias,
536
+ hostname: connection.source === "discovered" ? null : connection.sshHost,
537
+ sshPort: connection.source === "discovered" ? null : connection.sshPort,
538
+ identity: connection.source === "discovered" ? null : connection.identity,
539
+ ...connection.connectionAnalyticsId ? { connectionAnalyticsId: connection.connectionAnalyticsId } : {}
540
+ };
541
+ }
542
+ async function mutate(previous, draft, signal) {
543
+ const current = await list(signal);
544
+ if (previous) {
545
+ const latest = current.find((item) => item.hostId === previous.hostId);
546
+ if (!latest || JSON.stringify(saved(latest)) !== JSON.stringify(saved(previous))) {
547
+ throw new Error("This connection changed in Codex. Refresh and edit it again.");
548
+ }
549
+ }
550
+ const next = current.filter((item) => item.hostId !== previous?.hostId).map(saved);
551
+ if (draft) {
552
+ const hostname3 = draft.hostname.trim();
553
+ const displayName = draft.displayName.trim();
554
+ if (!displayName || !hostname3 || hostname3.startsWith("-") || /\s/u.test(hostname3) || draft.sshPort !== null && (!Number.isInteger(draft.sshPort) || draft.sshPort < 1 || draft.sshPort > 65535)) {
555
+ throw new Error("Enter a name, SSH hostname (or alias), and a port between 1 and 65535");
556
+ }
557
+ if (current.some(
558
+ (item) => item.hostId !== previous?.hostId && item.displayName.trim() === displayName
559
+ )) {
560
+ throw new Error("A connection with this name already exists");
561
+ }
562
+ if (previous?.source === "discovered" && (hostname3 !== previous.sshHost || draft.sshPort !== previous.sshPort || draft.identity !== previous.identity)) {
563
+ throw new Error("Edit SSH-config connection details in native Codex settings");
564
+ }
565
+ next.push(
566
+ previous?.source === "discovered" ? { ...saved(previous), displayName } : {
567
+ hostId: previous?.hostId ?? `remote-ssh-codex-managed:${ownerWindow.crypto.randomUUID()}`,
568
+ displayName,
569
+ source: "codex-managed",
570
+ alias: null,
571
+ hostname: hostname3,
572
+ sshPort: draft.sshPort,
573
+ identity: draft.identity?.trim() || null,
574
+ ...previous?.connectionAnalyticsId ? { connectionAnalyticsId: previous.connectionAnalyticsId } : {}
575
+ }
576
+ );
577
+ }
578
+ await request("save-codex-managed-remote-ssh-connections", { remoteConnections: next }, signal);
579
+ }
580
+ return {
581
+ list,
582
+ save: (draft, previous, signal) => mutate(previous, draft, signal),
583
+ remove: (previous, signal) => mutate(previous, null, signal),
584
+ async connect(hostId, enabled, signal) {
585
+ await request("set-remote-connection-auto-connect", { hostId, autoConnect: enabled }, signal);
586
+ },
587
+ async state(hostId, signal) {
588
+ const result = await request("app-server-connection-state", { hostId }, signal);
589
+ if (!record(result) || typeof result.state !== "string")
590
+ throw new Error("Invalid Codex connection state");
591
+ return {
592
+ state: result.state,
593
+ error: typeof result.error === "string" ? result.error : record(result.error) && typeof result.error.message === "string" ? result.error.message : null
594
+ };
595
+ }
596
+ };
597
+ }
598
+
599
+ // src/remote-connections-control.ts
600
+ function remoteUpdateTarget(local, remote) {
601
+ if (!remote.updateSupported || !remote.remote || !remote.installedVersion || !local?.runningVersion)
602
+ return null;
603
+ if (["installing", "restarting", "failed"].includes(remote.update.phase)) return null;
604
+ const stable = /^(\d+)\.(\d+)\.(\d+)$/u;
605
+ if (!stable.test(local.runningVersion) || !stable.test(remote.installedVersion)) return null;
606
+ const left = local.runningVersion.split(".").map(Number);
607
+ const right = remote.installedVersion.split(".").map(Number);
608
+ const index = left.findIndex((part, index2) => part !== right[index2]);
609
+ if (index >= 0 && (left[index] ?? 0) > (right[index] ?? 0)) return local.runningVersion;
610
+ if (index < 0 && remote.restartRequired) return remote.installedVersion;
611
+ return null;
612
+ }
613
+ function createRemoteConnectionsControl(ownerWindow, getClient) {
614
+ async function bounded(operation) {
615
+ let timer;
616
+ try {
617
+ return await Promise.race([
618
+ operation,
619
+ new Promise((_, reject) => {
620
+ timer = ownerWindow.setTimeout(
621
+ () => reject(new Error("Remote service did not respond; reconnect and retry")),
622
+ 8e3
623
+ );
624
+ })
625
+ ]);
626
+ } finally {
627
+ ownerWindow.clearTimeout(timer);
628
+ }
629
+ }
630
+ const control = {
631
+ ssh: createCodexSshClient(ownerWindow),
632
+ async setup(connection, action, version2) {
633
+ const client = getClient("local");
634
+ if (!client?.setupSsh)
635
+ throw new Error("SSH installation is unavailable; update local codexhost and restart");
636
+ return client.setupSsh({
637
+ hostname: connection.sshAlias ?? connection.sshHost,
638
+ port: connection.sshPort,
639
+ identity: connection.identity,
640
+ action,
641
+ version: version2 ?? null
642
+ });
643
+ },
644
+ async runtime(hostId) {
645
+ const client = getClient(hostId);
646
+ if (!client?.runtimeStatus)
647
+ throw new Error(
648
+ "Version management is unavailable; update codexhost on this host and reconnect"
649
+ );
650
+ return bounded(client.runtimeStatus());
651
+ },
652
+ async update(hostId, version2) {
653
+ if (hostId === "local") throw new Error("Choose a remote SSH connection");
654
+ const client = getClient(hostId);
655
+ if (!client?.updateRemote)
656
+ throw new Error("Remote update is unavailable; update this remote once and reconnect");
657
+ return bounded(client.updateRemote(version2));
658
+ }
659
+ };
660
+ return control;
661
+ }
662
+
442
663
  // ../../node_modules/zod/v4/classic/external.js
443
664
  var external_exports = {};
444
665
  __export(external_exports, {
@@ -633,7 +854,7 @@
633
854
  promise: () => promise,
634
855
  property: () => _property,
635
856
  readonly: () => readonly,
636
- record: () => record,
857
+ record: () => record2,
637
858
  refine: () => refine,
638
859
  regex: () => _regex,
639
860
  regexes: () => regexes_exports,
@@ -12971,7 +13192,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
12971
13192
  preprocess: () => preprocess,
12972
13193
  promise: () => promise,
12973
13194
  readonly: () => readonly,
12974
- record: () => record,
13195
+ record: () => record2,
12975
13196
  refine: () => refine,
12976
13197
  set: () => set,
12977
13198
  strictObject: () => strictObject,
@@ -13940,7 +14161,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13940
14161
  inst.keyType = def.keyType;
13941
14162
  inst.valueType = def.valueType;
13942
14163
  });
13943
- function record(keyType, valueType, params) {
14164
+ function record2(keyType, valueType, params) {
13944
14165
  if (!valueType || !valueType._zod) {
13945
14166
  return new ZodRecord({
13946
14167
  type: "record",
@@ -14407,7 +14628,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14407
14628
  }, ...args);
14408
14629
  function json(params) {
14409
14630
  const jsonSchema = lazy(() => {
14410
- return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record(string2(), jsonSchema)]);
14631
+ return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record2(string2(), jsonSchema)]);
14411
14632
  });
14412
14633
  return jsonSchema;
14413
14634
  }
@@ -15043,6 +15264,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15043
15264
  "codexhost/harness/display-settings/get",
15044
15265
  "codexhost/harness/display-settings/set",
15045
15266
  "codexhost/harness/inspect",
15267
+ "codexhost/harness/installation",
15046
15268
  "codexhost/harness/launch-settings/get",
15047
15269
  "codexhost/harness/launch-settings/set",
15048
15270
  "codexhost/harness/web-ui/open",
@@ -15728,14 +15950,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15728
15950
  var HARNESS_INSTALLATION_METHOD = "codexhost/harness/installation";
15729
15951
  var harnessInstallationParamsSchema = external_exports.object({
15730
15952
  harnessId: harnessIdSchema,
15731
- action: external_exports.enum(["check", "update"])
15953
+ action: external_exports.enum(["check", "update", "install"])
15732
15954
  }).strict();
15733
15955
  var harnessInstallationStateSchema = external_exports.object({
15734
15956
  currentVersion: external_exports.string().min(1).max(128),
15735
15957
  latestVersion: external_exports.string().min(1).max(128),
15736
15958
  updateAvailable: external_exports.boolean(),
15737
15959
  canUpdate: external_exports.boolean(),
15738
- message: external_exports.string().max(2048).optional()
15960
+ // Adapter-owned diagnostic text; UI uses messageCode rather than displaying it verbatim.
15961
+ message: external_exports.string().max(2048).optional(),
15962
+ messageCode: external_exports.string().min(1).max(128).optional(),
15963
+ latestVersionKind: external_exports.enum(["unknown", "tracking-branch"]).optional()
15739
15964
  }).strict();
15740
15965
 
15741
15966
  // ../shared-contracts/dist/codex-accounts.js
@@ -16061,6 +16286,41 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16061
16286
  return `${HARNESS_COMMAND_MENTION_PATH_PREFIX}${encodeURIComponent(name).replace(/[()]/gu, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`)}`;
16062
16287
  }
16063
16288
 
16289
+ // ../shared-contracts/dist/remote-runtime.js
16290
+ var RUNTIME_STATUS_METHOD = "codexhost/runtime/status";
16291
+ var REMOTE_UPDATE_METHOD = "codexhost/remote/update";
16292
+ var remoteUpdatePhaseSchema = external_exports.enum([
16293
+ "idle",
16294
+ "installing",
16295
+ "restarting",
16296
+ "succeeded",
16297
+ "failed"
16298
+ ]);
16299
+ var runtimeStatusSchema = external_exports.object({
16300
+ runningVersion: external_exports.string().nullable(),
16301
+ installedVersion: external_exports.string().nullable(),
16302
+ restartRequired: external_exports.boolean(),
16303
+ remote: external_exports.boolean(),
16304
+ updateSupported: external_exports.boolean(),
16305
+ update: external_exports.object({
16306
+ phase: remoteUpdatePhaseSchema,
16307
+ targetVersion: external_exports.string().nullable(),
16308
+ error: external_exports.string().nullable()
16309
+ })
16310
+ });
16311
+ var remoteUpdateParamsSchema = external_exports.object({
16312
+ version: external_exports.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u)
16313
+ }).strict();
16314
+ var REMOTE_SSH_SETUP_METHOD = "codexhost/remote/ssh-setup";
16315
+ var remoteSshSetupParamsSchema = external_exports.object({
16316
+ hostname: external_exports.string().min(1).max(1024).refine((value) => !value.startsWith("-") && !/\s/u.test(value)),
16317
+ port: external_exports.number().int().min(1).max(65535).nullable(),
16318
+ identity: external_exports.string().max(4096).nullable(),
16319
+ action: external_exports.enum(["inspect", "install", "update", "repair"]),
16320
+ version: external_exports.string().regex(/^\d+\.\d+\.\d+$/u).nullable()
16321
+ }).strict();
16322
+ var remoteSshSetupResultSchema = external_exports.object({ state: external_exports.enum(["installed", "not-installed"]) }).strict();
16323
+
16064
16324
  // ../shared-contracts/dist/index.js
16065
16325
  var workspaceContractVersionSchema = external_exports.literal(WORKSPACE_CONTRACT_VERSION);
16066
16326
 
@@ -16172,6 +16432,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16172
16432
  return sharedStore;
16173
16433
  }
16174
16434
 
16435
+ // src/assets/zcode-agent.svg
16436
+ var zcode_agent_default = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">%0A <path fill="%232D2D2D" stroke="%23FFFFFF" stroke-width="0.6317" stroke-miterlimit="10" d="M24.51,28.51H5.49c-2.21,0-4-1.79-4-4V5.49c0-2.21,1.79-4,4-4h19.03c2.21,0,4,1.79,4,4v19.03C28.51,26.72,26.72,28.51,24.51,28.51z"/>%0A <g fill="%23FFFFFF">%0A <path d="M15.47,7.1l-1.3,1.85c-0.2,0.29-0.54,0.47-0.9,0.47h-7.1V7.09C6.16,7.1,15.47,7.1,15.47,7.1z"/>%0A <polygon points="24.3,7.1 13.14,22.91 5.7,22.91 16.86,7.1"/>%0A <path d="M14.53,22.91l1.31-1.86c0.2-0.29,0.54-0.47,0.9-0.47h7.09v2.33H14.53z"/>%0A </g>%0A</svg>%0A';
16437
+
16175
16438
  // src/assets/kimi-agent.svg
16176
16439
  var kimi_agent_default = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40" fill="none">%0A <rect width="40" height="40" rx="10" fill="%23000000"/>%0A <path d="M31.6957 7.11414C32.8305 7.11414 33.7505 8.0341 33.7505 9.16893V9.16893C33.7505 10.3038 32.8305 11.2237 31.6957 11.2237L29.8826 11.2237C29.7491 11.2237 29.6409 11.1155 29.6409 10.982L29.6409 9.16893C29.6409 8.0341 30.5609 7.11414 31.6957 7.11414Z" fill="%231783FF"/>%0A <path d="M20.1757 19.0798L27.9302 11.3864C28.0761 11.2413 27.9939 10.9498 27.8056 10.9498H23.633C23.5877 10.9498 23.5438 10.9683 23.5084 11.0038L15.1533 19.2911C15.023 19.4192 14.8304 19.305 14.8304 19.0998V11.1951C14.8304 11.0593 14.7411 10.9498 14.6321 10.9498H11.7569C11.6478 10.9498 11.5586 11.0593 11.5586 11.1951V28.2387C11.5586 28.3745 11.6478 28.484 11.7569 28.484H14.6321C14.7411 28.484 14.8304 28.3745 14.8304 28.2387V24.7661C14.8304 24.6921 14.8573 24.6211 14.904 24.5748L17.4945 22.0047C17.5568 21.943 17.6432 21.9337 17.7126 21.98L24.6414 27.0786C25.7759 27.8423 27.0436 28.3112 28.331 28.4501C28.4458 28.4624 28.5449 28.3483 28.5449 28.2048V24.9343C28.5449 24.8093 28.4698 24.7075 28.3693 24.6921C27.6115 24.5733 26.8722 24.2802 26.2037 23.8297L20.2054 19.487C20.0808 19.4037 20.0652 19.1893 20.1757 19.0798Z" fill="%23FFFFFF"/>%0A</svg>%0A';
16177
16440
 
@@ -16225,7 +16488,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16225
16488
  hermes: "Hermes",
16226
16489
  qoder: "Qoder",
16227
16490
  "qoder-cn": "Qoder CN",
16228
- "kimi-code": "Kimi Code"
16491
+ "kimi-code": "Kimi Code",
16492
+ zcode: "ZCode"
16229
16493
  };
16230
16494
  var PI_PATHS = [
16231
16495
  {
@@ -16301,9 +16565,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16301
16565
  image.style.flex = "none";
16302
16566
  return image;
16303
16567
  }
16304
- if (agent === "antigravity" || agent === "kiro-cli" || agent === "codebuddy" || agent === "workbuddy" || agent === "cursor-cli" || agent === "kimi-code") {
16568
+ if (agent === "antigravity" || agent === "kiro-cli" || agent === "codebuddy" || agent === "workbuddy" || agent === "cursor-cli" || agent === "kimi-code" || agent === "zcode") {
16305
16569
  const image = ownerDocument.createElement("img");
16306
- image.src = agent === "kimi-code" ? kimi_agent_default : agent === "codebuddy" ? codebuddy_agent_default : agent === "workbuddy" ? workbuddy_agent_default : agent === "cursor-cli" ? cursor_agent_default : agent === "kiro-cli" ? kiro_agent_default : antigravity_agent_default;
16570
+ image.src = agent === "zcode" ? zcode_agent_default : agent === "kimi-code" ? kimi_agent_default : agent === "codebuddy" ? codebuddy_agent_default : agent === "workbuddy" ? workbuddy_agent_default : agent === "cursor-cli" ? cursor_agent_default : agent === "kiro-cli" ? kiro_agent_default : antigravity_agent_default;
16307
16571
  image.alt = "";
16308
16572
  image.draggable = false;
16309
16573
  image.style.width = `${size}px`;
@@ -16838,11 +17102,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16838
17102
  input.type = "text";
16839
17103
  input.autocomplete = "off";
16840
17104
  input.spellcheck = false;
16841
- input.placeholder = messages.launchPathPlaceholder;
17105
+ input.placeholder = agent === "zcode" ? messages.launchPathZcodePlaceholder : messages.launchPathPlaceholder;
16842
17106
  input.setAttribute("aria-label", messages.launchPathLabel);
16843
17107
  label.append(input);
16844
17108
  const help = document2.createElement("p");
16845
- help.textContent = messages.launchPathWorkbuddyHelp;
17109
+ help.textContent = agent === "zcode" ? messages.launchPathZcodeHelp : messages.launchPathWorkbuddyHelp;
16846
17110
  const actions = document2.createElement("div");
16847
17111
  actions.className = "settings-harness-launch__actions";
16848
17112
  const save = document2.createElement("button");
@@ -16921,6 +17185,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16921
17185
  ];
16922
17186
  var node = ["Requires Node.js (including npm).", "\u9700\u5148\u5B89\u88C5 Node.js\uFF08\u5305\u542B npm\uFF09\u3002"];
16923
17187
  var guides = {
17188
+ zcode: {
17189
+ // codexhost runs the Agent CLI bundled with the installed ZCode Desktop.
17190
+ url: "https://github.com/BytePioneer-AI/codex-host/blob/main/docs/harnesses/zcode/zcode-harness-integration.md#%E5%AE%89%E8%A3%85",
17191
+ commands: [],
17192
+ before: [
17193
+ "Install ZCode Desktop and sign in with a Start Plan account. If ZCode is not in the default location (/Applications/ZCode.app on macOS, %LOCALAPPDATA%\\Programs\\ZCode on Windows, /opt/ZCode on Linux), set its path in the connection settings.",
17194
+ "\u8BF7\u5B89\u88C5 ZCode Desktop \u5E76\u767B\u5F55 Start Plan \u8D26\u53F7\u3002ZCode \u4E0D\u5728\u9ED8\u8BA4\u5B89\u88C5\u4F4D\u7F6E\uFF08macOS \u4E3A /Applications/ZCode.app\uFF0CWindows \u4E3A %LOCALAPPDATA%\\Programs\\ZCode\uFF0CLinux \u4E3A /opt/ZCode\uFF09\u65F6\uFF0C\u53EF\u5728\u8FDE\u63A5\u8BBE\u7F6E\u4E2D\u586B\u5199\u5176\u8DEF\u5F84\u3002"
17195
+ ],
17196
+ downloads: [{ label: "ZCode", url: "https://zcode.z.ai/" }],
17197
+ after: [
17198
+ "Check again after signing in. The ZCode Desktop window does not need to stay open.",
17199
+ "\u767B\u5F55\u540E\u91CD\u65B0\u68C0\u6D4B\uFF1B\u4F7F\u7528\u65F6\u4E0D\u9700\u8981\u4FDD\u6301 ZCode \u7A97\u53E3\u6253\u5F00\u3002"
17200
+ ]
17201
+ },
16924
17202
  pi: {
16925
17203
  url: "https://pi.dev/",
16926
17204
  commands: shells(
@@ -16939,11 +17217,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16939
17217
  },
16940
17218
  "deepseek-harness": {
16941
17219
  url: "https://github.com/deepseek-ai/deepseek-harness#run",
16942
- // Must match the Adapter's exact supported range, not npm's latest tag.
16943
- commands: npm("@deepseek-ai/dsh@0.1.5-rc.1"),
17220
+ commands: npm("@deepseek-ai/dsh@latest"),
16944
17221
  before: [
16945
- "Requires Node.js. Installs the compatible version 0.1.5-rc.1.",
16946
- "\u9700\u5148\u5B89\u88C5 Node.js\u3002\u4E0B\u65B9\u547D\u4EE4\u5B89\u88C5\u517C\u5BB9\u7248\u672C 0.1.5-rc.1\u3002"
17222
+ "Requires Node.js. Installs npm's latest version; connection still requires native protocol validation.",
17223
+ "\u9700\u5148\u5B89\u88C5 Node.js\u3002\u5B89\u88C5 npm \u6700\u65B0\u7248\u672C\uFF1B\u8FDE\u63A5\u4ECD\u9700\u901A\u8FC7\u539F\u751F\u534F\u8BAE\u6821\u9A8C\u3002"
16947
17224
  ],
16948
17225
  after: [
16949
17226
  "Check again, then open Web UI to configure your provider. Stop any manually started dsh web first.",
@@ -17002,17 +17279,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17002
17279
  url: "https://www.workbuddy.ai/docs/workbuddy/Quickstart",
17003
17280
  commands: [],
17004
17281
  before: [
17005
- "Install the WorkBuddy desktop app. codexhost uses its bundled CLI, not the standalone CodeBuddy CLI.",
17006
- "\u8BF7\u5B89\u88C5 WorkBuddy \u684C\u9762\u5E94\u7528\u3002codexhost \u4F7F\u7528\u5E94\u7528\u5185\u7F6E CLI\uFF0C\u4E0D\u80FD\u7528 CodeBuddy CLI \u4EE3\u66FF\u3002"
17282
+ "Download and install the WorkBuddy desktop app.",
17283
+ "\u8BF7\u4E0B\u8F7D\u5E76\u5B89\u88C5 WorkBuddy \u684C\u9762\u5E94\u7528\u3002"
17007
17284
  ],
17008
17285
  downloads: [
17009
17286
  {
17010
- label: "macOS",
17011
- url: "https://www.workbuddy.ai/docs/workbuddy/From-Beginner-to-Expert-Guide/Installation-Mac-Guide"
17012
- },
17013
- {
17014
- label: "Windows",
17015
- url: "https://www.workbuddy.ai/docs/workbuddy/From-Beginner-to-Expert-Guide/Installation-Win-Guide"
17287
+ label: "WorkBuddy",
17288
+ url: "https://www.workbuddy.ai/"
17016
17289
  }
17017
17290
  ],
17018
17291
  after: [
@@ -17066,6 +17339,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17066
17339
  after: start("kimi")
17067
17340
  }
17068
17341
  };
17342
+ function harnessHasInstallCommands(agent) {
17343
+ return guides[agent].commands.length > 0;
17344
+ }
17069
17345
  function harnessInstallationGuide(agent, locale) {
17070
17346
  const guide = guides[agent];
17071
17347
  const index = locale === "zh-CN" ? 1 : 0;
@@ -17073,7 +17349,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17073
17349
  }
17074
17350
 
17075
17351
  // src/settings/harness-installation-panel.ts
17076
- function createHarnessInstallationPanel(document2, agent, hostId, messages, copy, refresh) {
17352
+ function createHarnessInstallationPanel(document2, agent, hostId, messages, copy, installation) {
17077
17353
  const zh = messages.locale === "zh-CN";
17078
17354
  const guide = harnessInstallationGuide(agent, messages.locale);
17079
17355
  const panel = document2.createElement("div");
@@ -17087,6 +17363,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17087
17363
  paragraph(zh ? "\u8BF7\u5728\u8FDC\u7A0B Host \u4E0A\u5B89\u88C5\u3002" : "Install on the remote Host.");
17088
17364
  }
17089
17365
  if (guide.before) paragraph(guide.before);
17366
+ const install2 = installation && guide.commands.length > 0 ? document2.createElement("button") : null;
17367
+ if (install2 && installation) {
17368
+ install2.type = "button";
17369
+ install2.className = "settings-command-button";
17370
+ install2.dataset.connectionAction = "install";
17371
+ install2.disabled = installation.status !== "idle";
17372
+ install2.textContent = installation.status === "installing" ? messages.connectionStatusInstalling : installation.status === "checking" ? messages.connectionStatusChecking : zh ? "\u4E00\u952E\u5B89\u88C5" : "Install automatically";
17373
+ install2.addEventListener("click", () => {
17374
+ if (install2.disabled) return;
17375
+ install2.disabled = true;
17376
+ install2.textContent = messages.connectionStatusInstalling;
17377
+ installation.run();
17378
+ });
17379
+ if (guide.commands.length > 1) panel.append(install2);
17380
+ }
17090
17381
  for (const { terminal, command } of guide.commands) {
17091
17382
  const block = document2.createElement("div");
17092
17383
  block.className = "settings-harness-installation-command";
@@ -17104,40 +17395,153 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17104
17395
  button.setAttribute("aria-label", `${copyLabel}: ${terminal}`);
17105
17396
  button.append(createRendererSettingsIcon("copy", 16), copyLabel);
17106
17397
  button.addEventListener("click", () => copy(button, command, copyLabel));
17107
- block.append(label, pre, button);
17398
+ const actions = document2.createElement("div");
17399
+ actions.className = "settings-harness-installation-actions";
17400
+ if (install2 && guide.commands.length === 1) actions.append(install2);
17401
+ actions.append(button);
17402
+ block.append(label, pre, actions);
17108
17403
  panel.append(block);
17109
17404
  }
17110
- const link = (url2, label) => {
17111
- const anchor = document2.createElement("a");
17112
- anchor.className = "settings-command-button settings-command-button--secondary";
17113
- anchor.href = url2;
17114
- anchor.target = "_blank";
17115
- anchor.rel = "noopener noreferrer";
17116
- anchor.append(label, createRendererSettingsIcon("external-link", 14));
17117
- panel.append(anchor);
17118
- };
17119
17405
  for (const download of guide.downloads ?? []) {
17120
- link(
17121
- download.url,
17122
- `${download.label} \xB7 ${zh ? "\u4E0B\u8F7D\u4E0E\u5B89\u88C5\u6307\u5357" : "Download and installation guide"}`
17123
- );
17406
+ const link = document2.createElement("a");
17407
+ link.className = "settings-command-button settings-command-button--secondary";
17408
+ link.href = download.url;
17409
+ link.target = "_blank";
17410
+ link.rel = "noopener noreferrer";
17411
+ link.textContent = zh ? "\u4E0B\u8F7D" : "Download";
17412
+ link.setAttribute("aria-label", `${link.textContent} ${download.label}`);
17413
+ panel.append(link);
17124
17414
  }
17125
17415
  paragraph(guide.after);
17126
- const check2 = document2.createElement("button");
17127
- check2.type = "button";
17128
- check2.className = "settings-command-button";
17129
- check2.dataset.connectionAction = "check-install";
17130
- check2.textContent = zh ? "\u91CD\u65B0\u68C0\u6D4B" : "Check again";
17131
- check2.addEventListener("click", () => {
17132
- check2.disabled = true;
17133
- check2.textContent = messages.connectionRefreshing;
17134
- refresh();
17135
- });
17136
- panel.append(check2);
17137
- link(guide.url, zh ? "\u67E5\u770B\u5B98\u65B9\u5B89\u88C5\u8BF4\u660E" : "View official installation instructions");
17138
17416
  return panel;
17139
17417
  }
17140
17418
 
17419
+ // src/settings/harness-official-websites.ts
17420
+ var HARNESS_OFFICIAL_WEBSITES = {
17421
+ pi: "https://pi.dev/",
17422
+ "claude-code": "https://code.claude.com/",
17423
+ "deepseek-harness": "https://github.com/deepseek-ai/deepseek-harness",
17424
+ opencode: "https://opencode.ai/",
17425
+ grok: "https://www.npmjs.com/package/@xai-official/grok",
17426
+ omp: "https://github.com/can1357/oh-my-pi",
17427
+ antigravity: "https://antigravity.google/",
17428
+ "kiro-cli": "https://kiro.dev/",
17429
+ codebuddy: "https://www.codebuddy.ai/",
17430
+ workbuddy: "https://www.workbuddy.ai/",
17431
+ "cursor-cli": "https://cursor.com/",
17432
+ hermes: "https://hermes-agent.nousresearch.com/",
17433
+ qoder: "https://qoder.com/",
17434
+ "qoder-cn": "https://qoder.cn/",
17435
+ "kimi-code": "https://code.kimi.com/",
17436
+ zcode: "https://zcode.z.ai/"
17437
+ };
17438
+
17439
+ // src/settings/harness-install-store.ts
17440
+ function errorMessage(error51) {
17441
+ return typeof error51 === "object" && error51 !== null && "message" in error51 && typeof error51.message === "string" ? error51.message.slice(0, 2048) : "Could not install the Harness. Check the official installation guide and retry.";
17442
+ }
17443
+ var stores = /* @__PURE__ */ new WeakMap();
17444
+ function createStore(diagnostics) {
17445
+ const states = /* @__PURE__ */ new Map();
17446
+ const listeners = /* @__PURE__ */ new Set();
17447
+ const key = (host, agent) => JSON.stringify([host, agent]);
17448
+ const notify = () => listeners.forEach((listener) => listener());
17449
+ return {
17450
+ get: (host, agent) => states.get(key(host, agent)),
17451
+ clearErrors() {
17452
+ for (const [id, state] of states) if (state.status === "error") states.delete(id);
17453
+ notify();
17454
+ },
17455
+ subscribe(listener) {
17456
+ listeners.add(listener);
17457
+ return () => {
17458
+ listeners.delete(listener);
17459
+ };
17460
+ },
17461
+ async install(host, agent) {
17462
+ const id = key(host, agent);
17463
+ const previous = states.get(id);
17464
+ if (!diagnostics.installation || previous && previous.status !== "error") return;
17465
+ states.set(id, { status: "installing" });
17466
+ notify();
17467
+ let failure;
17468
+ try {
17469
+ await diagnostics.installation(host, agent, "install");
17470
+ states.set(id, { status: "checking" });
17471
+ notify();
17472
+ } catch (error51) {
17473
+ failure = errorMessage(error51);
17474
+ }
17475
+ try {
17476
+ await diagnostics.refresh(host);
17477
+ const refreshed = diagnostics.snapshot().hosts.find((candidate) => candidate.hostId === host)?.agents.find((candidate) => candidate.agent === agent);
17478
+ if (refreshed?.availability === "ready") failure = void 0;
17479
+ } catch (error51) {
17480
+ failure ??= errorMessage(error51);
17481
+ }
17482
+ if (failure) states.set(id, { status: "error", error: failure });
17483
+ else states.delete(id);
17484
+ notify();
17485
+ }
17486
+ };
17487
+ }
17488
+ function harnessInstallStore(diagnostics) {
17489
+ let store = stores.get(diagnostics);
17490
+ if (!store) {
17491
+ store = createStore(diagnostics);
17492
+ stores.set(diagnostics, store);
17493
+ }
17494
+ return store;
17495
+ }
17496
+
17497
+ // src/settings/harness-version-messages.ts
17498
+ var harnessVersionEnglish = Object.freeze({
17499
+ unknown: "Unknown",
17500
+ trackingBranch: "Tracking branch (new commits)",
17501
+ notes: Object.freeze({
17502
+ "original-installer": "Use the original installer, package manager, or launcher to update this installation.",
17503
+ "claude-policy-restricted": "Claude updates are restricted by the installation's settings or policy.",
17504
+ "codebuddy-npm-required": "Use CodeBuddy's native updater or original package manager. Latest-version checks here require an identified npm installation.",
17505
+ "deepseek-original-installer": "Update with the original installer. Python, desktop, and project-local installations are not upgraded through global npm.",
17506
+ "kiro-native-updater": "Use Kiro CLI's original installer or native updater to check for and install updates.",
17507
+ "zcode-desktop-updater": "The CLI ships inside ZCode Desktop. Update ZCode Desktop itself to update it.",
17508
+ "qoder-check-unavailable": "This Qoder China release does not expose a non-installing update check. Use its original installer.",
17509
+ "hermes-update-plan-unavailable": "This Hermes release does not expose a safe update plan. Use the original installer.",
17510
+ "hermes-externally-managed": "This Hermes installation is managed externally. Update it through its desktop app, container, or original package manager.",
17511
+ "hermes-update-channel": "Uses Hermes's configured update channel. Gateway restarts are deferred; existing processes keep their running code.",
17512
+ "hermes-manual-update": "Use the native updater manually. Safe non-interactive updates require a clean source checkout, commit identity, and Gateway restart deferral."
17513
+ })
17514
+ });
17515
+ var harnessVersionChinese = Object.freeze({
17516
+ unknown: "\u672A\u77E5",
17517
+ trackingBranch: "\u8DDF\u8E2A\u5206\u652F\uFF08\u6709\u65B0\u63D0\u4EA4\uFF09",
17518
+ notes: Object.freeze({
17519
+ "original-installer": "\u8BF7\u4F7F\u7528\u539F\u5B89\u88C5\u7A0B\u5E8F\u3001\u5305\u7BA1\u7406\u5668\u6216\u542F\u52A8\u5668\u66F4\u65B0\u6B64\u5B89\u88C5\u3002",
17520
+ "claude-policy-restricted": "\u6B64 Claude \u5B89\u88C5\u7684\u8BBE\u7F6E\u6216\u7B56\u7565\u9650\u5236\u4E86\u66F4\u65B0\u3002",
17521
+ "codebuddy-npm-required": "\u8BF7\u4F7F\u7528 CodeBuddy \u539F\u751F\u66F4\u65B0\u5668\u6216\u539F\u5305\u7BA1\u7406\u5668\u66F4\u65B0\u3002\u6B64\u5904\u4EC5\u80FD\u67E5\u8BE2\u5DF2\u8BC6\u522B\u7684 npm \u5B89\u88C5\u7684\u6700\u65B0\u7248\u672C\u3002",
17522
+ "deepseek-original-installer": "\u8BF7\u4F7F\u7528\u539F\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\u3002Python\u3001\u684C\u9762\u5E94\u7528\u53CA\u9879\u76EE\u5185\u5B89\u88C5\u4E0D\u4F1A\u901A\u8FC7\u5168\u5C40 npm \u66F4\u65B0\u3002",
17523
+ "kiro-native-updater": "\u8BF7\u4F7F\u7528 Kiro CLI \u539F\u5B89\u88C5\u7A0B\u5E8F\u6216\u539F\u751F\u66F4\u65B0\u5668\u68C0\u67E5\u5E76\u5B89\u88C5\u66F4\u65B0\u3002",
17524
+ "zcode-desktop-updater": "CLI \u968F ZCode Desktop \u4E00\u8D77\u53D1\u5E03\uFF0C\u8BF7\u76F4\u63A5\u66F4\u65B0 ZCode Desktop\u3002",
17525
+ "qoder-check-unavailable": "\u6B64 Qoder \u4E2D\u56FD\u7248\u5C1A\u4E0D\u652F\u6301\u53EA\u68C0\u67E5\u3001\u4E0D\u5B89\u88C5\u7684\u66F4\u65B0\u67E5\u8BE2\uFF0C\u8BF7\u4F7F\u7528\u539F\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\u3002",
17526
+ "hermes-update-plan-unavailable": "\u6B64 Hermes \u7248\u672C\u4E0D\u63D0\u4F9B\u5B89\u5168\u7684\u66F4\u65B0\u8BA1\u5212\uFF0C\u8BF7\u4F7F\u7528\u539F\u5B89\u88C5\u65B9\u5F0F\u66F4\u65B0\u3002",
17527
+ "hermes-externally-managed": "\u6B64 Hermes \u5B89\u88C5\u7531\u5916\u90E8\u5DE5\u5177\u7BA1\u7406\uFF0C\u8BF7\u901A\u8FC7\u5176\u684C\u9762\u5E94\u7528\u3001\u5BB9\u5668\u6216\u539F\u5305\u7BA1\u7406\u5668\u66F4\u65B0\u3002",
17528
+ "hermes-update-channel": "\u4F7F\u7528 Hermes \u914D\u7F6E\u7684\u66F4\u65B0\u6E20\u9053\uFF0C\u6682\u4E0D\u91CD\u542F Gateway\uFF1B\u73B0\u6709\u8FDB\u7A0B\u7EE7\u7EED\u8FD0\u884C\u539F\u6709\u4EE3\u7801\u3002",
17529
+ "hermes-manual-update": "\u8BF7\u624B\u52A8\u4F7F\u7528\u539F\u751F\u66F4\u65B0\u5668\u3002\u5B89\u5168\u7684\u975E\u4EA4\u4E92\u66F4\u65B0\u8981\u6C42\u6E90\u7801\u76EE\u5F55\u65E0\u672A\u63D0\u4EA4\u4FEE\u6539\u3001\u53EF\u8BC6\u522B\u5F53\u524D\u63D0\u4EA4\uFF0C\u5E76\u652F\u6301\u63A8\u8FDF Gateway \u91CD\u542F\u3002"
17530
+ })
17531
+ });
17532
+ function harnessVersionLabel(state, messages) {
17533
+ if (!state) return "\u2014";
17534
+ if (state.latestVersionKind === "unknown" || state.latestVersion === "Unknown")
17535
+ return messages.unknown;
17536
+ if (state.latestVersionKind === "tracking-branch" || state.latestVersion === "Tracking branch (new commits)")
17537
+ return messages.trackingBranch;
17538
+ return state.latestVersion;
17539
+ }
17540
+ function harnessVersionNote(state, messages) {
17541
+ const code = state?.messageCode;
17542
+ return code && Object.hasOwn(messages.notes, code) ? messages.notes[code] : void 0;
17543
+ }
17544
+
17141
17545
  // src/settings/harness-version-panel.ts
17142
17546
  function createHarnessVersionPanel(document2, messages, signal, agent, client) {
17143
17547
  const panel = document2.createElement("section");
@@ -17158,29 +17562,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17158
17562
  update.type = "button";
17159
17563
  update.className = "settings-command-button settings-command-button--secondary";
17160
17564
  update.dataset.harnessVersionAction = "update";
17161
- const guide = document2.createElement("a");
17162
- guide.className = update.className;
17163
- guide.href = harnessInstallationGuide(agent, messages.locale).url;
17164
- guide.target = "_blank";
17165
- guide.rel = "noopener noreferrer";
17166
- guide.append(
17167
- messages.connectionOpenInstallation,
17168
- createRendererSettingsIcon("external-link", 14)
17169
- );
17170
17565
  const note = document2.createElement("p");
17171
17566
  note.className = "settings-connection-issue-note";
17172
17567
  note.textContent = messages.harnessVersionNote;
17173
- actions.append(update, guide);
17568
+ actions.append(update);
17174
17569
  panel.append(heading, versions, status, actions, note);
17175
17570
  let state;
17176
17571
  let busy = false;
17177
17572
  let unsupported = false;
17178
17573
  const render = () => {
17179
17574
  current.textContent = `${messages.harnessVersionCurrent}: ${state?.currentVersion ?? "\u2014"}`;
17180
- latest.textContent = `${messages.harnessVersionLatest}: ${state?.latestVersion ?? "\u2014"}`;
17575
+ latest.textContent = `${messages.harnessVersionLatest}: ${harnessVersionLabel(state, messages.harnessVersion)}`;
17181
17576
  update.disabled = busy || signal.aborted || !state?.canUpdate || !state.updateAvailable;
17182
17577
  update.textContent = state?.canUpdate && state.latestVersion !== "Unknown" && !state.updateAvailable ? messages.harnessVersionUpToDate : messages.harnessVersionUpdate;
17183
- note.textContent = state?.message ? `${state.message} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
17578
+ const detail = harnessVersionNote(state, messages.harnessVersion);
17579
+ note.textContent = detail ? `${detail} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
17184
17580
  };
17185
17581
  const run = async (action) => {
17186
17582
  if (busy || unsupported || signal.aborted) return;
@@ -17213,6 +17609,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17213
17609
  // src/settings/connections-page.ts
17214
17610
  var CODEXHOST_GITHUB_ISSUES_NEW_URL = "https://github.com/BytePioneer-AI/codex-host/issues/new";
17215
17611
  function connectionStatusLabel(availability, messages, hasError = false) {
17612
+ if (availability === "updating") return messages.connectionStatusUpdating;
17216
17613
  if (hasError && availability !== "notInstalled") return messages.connectionStatusError;
17217
17614
  if (availability === "ready") return messages.connectionStatusReady;
17218
17615
  if (availability === "checking") return messages.connectionStatusChecking;
@@ -17223,6 +17620,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17223
17620
  return availability === "installing" ? messages.connectionStatusInstalling : messages.connectionStatusUnsupported;
17224
17621
  }
17225
17622
  function connectionStatusTone(availability, hasError = false) {
17623
+ if (availability === "updating") return "checking";
17226
17624
  if (hasError && availability !== "notInstalled") return "failed";
17227
17625
  if (availability === "ready") return "ready";
17228
17626
  if (availability === "checking" || availability === "installing") return "checking";
@@ -17248,16 +17646,6 @@ ${error51.stderrTail}`] : []
17248
17646
  ] : []
17249
17647
  ].join("\n");
17250
17648
  }
17251
- function detailLine(document2, label, value) {
17252
- const line = document2.createElement("div");
17253
- line.className = "settings-connection-detail-line";
17254
- const name = document2.createElement("span");
17255
- name.textContent = label;
17256
- const content = document2.createElement("code");
17257
- content.textContent = value;
17258
- line.append(name, content);
17259
- return line;
17260
- }
17261
17649
  function setCopyButtonLabel(button, label) {
17262
17650
  button.replaceChildren(createRendererSettingsIcon("copy", 16), label);
17263
17651
  }
@@ -17380,20 +17768,23 @@ ${error51.stderrTail}`] : []
17380
17768
  status.className = "settings-connection-row__status";
17381
17769
  status.dataset.connectionTone = connectionStatusTone(item.availability, item.error !== null);
17382
17770
  status.setAttribute("role", "cell");
17383
- status.textContent = connectionStatusLabel(item.availability, messages, item.error !== null);
17771
+ status.textContent = item.error?.code === "authenticationRequired" ? messages.connectionLoginRequired : item.error?.code === "configurationRequired" ? messages.connectionConfigurationRequired : connectionStatusLabel(item.availability, messages, item.error !== null);
17384
17772
  const action = document2.createElement("div");
17385
17773
  action.className = "settings-connection-row__action";
17386
17774
  action.setAttribute("role", "cell");
17387
- if (item.agentSnapshot?.availability === "notInstalled") {
17775
+ if (item.agentSnapshot?.availability === "notInstalled" && item.install) {
17388
17776
  const install2 = document2.createElement("button");
17389
17777
  install2.type = "button";
17390
17778
  install2.className = "settings-connection-install-link";
17391
17779
  install2.addEventListener("click", (event) => {
17392
17780
  event.stopPropagation();
17393
17781
  select();
17782
+ item.install?.();
17394
17783
  });
17395
- install2.setAttribute("aria-label", `${messages.connectionOpenInstallation}: ${item.name}`);
17396
- install2.title = messages.connectionOpenInstallation;
17784
+ install2.disabled = item.availability === "installing" || item.availability === "checking";
17785
+ const installLabel = messages.connectionInstall;
17786
+ install2.setAttribute("aria-label", `${installLabel}: ${item.name}`);
17787
+ install2.title = installLabel;
17397
17788
  install2.append(createRendererSettingsIcon("download", 17));
17398
17789
  action.append(install2);
17399
17790
  } else if (item.error) {
@@ -17429,6 +17820,7 @@ ${error51.stderrTail}`] : []
17429
17820
  select();
17430
17821
  });
17431
17822
  row.addEventListener("keydown", (event) => {
17823
+ if (event.target?.closest?.("button, a")) return;
17432
17824
  if (event.key !== "Enter" && event.key !== " ") return;
17433
17825
  event.preventDefault();
17434
17826
  select();
@@ -17453,10 +17845,22 @@ ${error51.stderrTail}`] : []
17453
17845
  const title = document2.createElement("strong");
17454
17846
  title.textContent = item.name;
17455
17847
  identity.append(createConnectionIdentityIcon(document2, item, 20), title);
17848
+ if (item.agentSnapshot) {
17849
+ const website = document2.createElement("a");
17850
+ website.className = "settings-connection-website";
17851
+ website.href = HARNESS_OFFICIAL_WEBSITES[item.agentSnapshot.agent];
17852
+ website.target = "_blank";
17853
+ website.rel = "noopener noreferrer";
17854
+ website.title = messages.connectionOfficialWebsite;
17855
+ website.setAttribute("aria-label", `${messages.connectionOfficialWebsite}: ${item.name}`);
17856
+ website.append(createRendererSettingsIcon("external-link", 13));
17857
+ website.addEventListener("click", (event) => event.stopPropagation());
17858
+ identity.append(website);
17859
+ }
17456
17860
  const status = document2.createElement("span");
17457
17861
  status.className = "settings-connection-row__status";
17458
17862
  status.dataset.connectionTone = connectionStatusTone(item.availability, item.error !== null);
17459
- status.textContent = connectionStatusLabel(item.availability, messages, item.error !== null);
17863
+ status.textContent = item.error?.code === "authenticationRequired" ? messages.connectionLoginRequired : item.error?.code === "configurationRequired" ? messages.connectionConfigurationRequired : item.agentSnapshot && item.availability === "ready" && !item.error ? messages.connectionStatusConnected : connectionStatusLabel(item.availability, messages, item.error !== null);
17460
17864
  header.append(identity, status);
17461
17865
  return header;
17462
17866
  }
@@ -17464,6 +17868,13 @@ ${error51.stderrTail}`] : []
17464
17868
  inspector.replaceChildren(createInspectorHeader(document2, item, messages));
17465
17869
  const body = document2.createElement("div");
17466
17870
  body.className = "settings-connection-inspector__body";
17871
+ if (item.installError) {
17872
+ const error51 = document2.createElement("div");
17873
+ error51.className = "settings-connection-error-summary";
17874
+ error51.setAttribute("role", "alert");
17875
+ error51.textContent = item.installError;
17876
+ body.append(error51);
17877
+ }
17467
17878
  if (item.agentSnapshot?.agent === "deepseek-harness") {
17468
17879
  const compatibility = document2.createElement("p");
17469
17880
  compatibility.className = "settings-connection-compatibility";
@@ -17471,52 +17882,49 @@ ${error51.stderrTail}`] : []
17471
17882
  body.append(compatibility);
17472
17883
  }
17473
17884
  if (item.agentSnapshot?.availability === "notInstalled") {
17474
- const callout = document2.createElement("div");
17475
- callout.className = "settings-connection-install-callout";
17476
- const icon2 = document2.createElement("span");
17477
- icon2.className = "settings-connection-install-callout__icon";
17478
- icon2.append(createRendererSettingsIcon("download", 18));
17479
- const copy = document2.createElement("div");
17480
- const title = document2.createElement("strong");
17481
- title.textContent = `${messages.connectionInstall} ${item.name}`;
17482
- const description = document2.createElement("p");
17483
- description.textContent = messages.connectionInstallDescription;
17484
- copy.append(title, description);
17485
- callout.append(icon2, copy);
17486
- const install2 = createHarnessInstallationPanel(
17487
- document2,
17488
- item.agentSnapshot.agent,
17489
- hostId,
17490
- messages,
17491
- (button, command, label) => copyDiagnosticsToClipboard(document2, button, command, messages, label),
17492
- refresh
17885
+ const zh = messages.locale === "zh-CN";
17886
+ const busy = item.availability === "installing" || item.availability === "checking";
17887
+ const note = (text) => {
17888
+ const paragraph = document2.createElement("p");
17889
+ paragraph.textContent = text;
17890
+ body.append(paragraph);
17891
+ };
17892
+ if (busy) note(messages.connectionInstallRunning);
17893
+ body.append(
17894
+ createHarnessInstallationPanel(
17895
+ document2,
17896
+ item.agentSnapshot.agent,
17897
+ hostId,
17898
+ messages,
17899
+ (button, command, label) => copyDiagnosticsToClipboard(document2, button, command, messages, label),
17900
+ item.install ? {
17901
+ run: item.install,
17902
+ status: busy ? item.availability : "idle"
17903
+ } : void 0
17904
+ )
17493
17905
  );
17494
- body.append(callout, install2);
17906
+ const check2 = document2.createElement("button");
17907
+ check2.type = "button";
17908
+ check2.className = "settings-command-button";
17909
+ check2.dataset.connectionAction = "check-install";
17910
+ check2.textContent = zh ? "\u91CD\u65B0\u68C0\u6D4B" : "Check again";
17911
+ check2.disabled = busy;
17912
+ check2.addEventListener("click", () => {
17913
+ check2.disabled = true;
17914
+ check2.textContent = messages.connectionRefreshing;
17915
+ refresh();
17916
+ });
17917
+ body.append(check2);
17495
17918
  } else if (item.error) {
17496
17919
  const summary = document2.createElement("div");
17497
17920
  summary.className = "settings-connection-error-summary";
17498
17921
  const title = document2.createElement("strong");
17499
- title.textContent = messages.connectionErrorTitle;
17922
+ const needsLogin = item.error.code === "authenticationRequired";
17923
+ const needsConfiguration = item.error.code === "configurationRequired";
17924
+ title.textContent = needsLogin ? messages.connectionLoginRequired : needsConfiguration ? messages.connectionConfigurationRequired : messages.connectionErrorTitle;
17500
17925
  const description = document2.createElement("p");
17501
- description.textContent = item.error.message;
17926
+ description.textContent = needsLogin ? messages.connectionLoginDescription : needsConfiguration ? messages.connectionConfigurationDescription : item.error.message;
17502
17927
  summary.append(title, description);
17503
- const metadata = document2.createElement("div");
17504
- metadata.className = "settings-connection-error-metadata";
17505
- metadata.append(
17506
- detailLine(document2, messages.connectionErrorCode, item.error.code),
17507
- detailLine(document2, messages.connectionRetryable, String(item.error.retryable))
17508
- );
17509
- if (item.error.stage) {
17510
- metadata.append(detailLine(document2, messages.connectionFailureStage, item.error.stage));
17511
- }
17512
- if (item.error.durationMs !== void 0) {
17513
- metadata.append(
17514
- detailLine(document2, messages.connectionDuration, `${item.error.durationMs} ms`)
17515
- );
17516
- }
17517
- if (item.error.diagnostic) {
17518
- metadata.append(detailLine(document2, messages.connectionDiagnostic, item.error.diagnostic));
17519
- }
17520
17928
  const logHeader = document2.createElement("div");
17521
17929
  logHeader.className = "settings-connection-error-log-header";
17522
17930
  const logTitle = document2.createElement("strong");
@@ -17532,7 +17940,7 @@ ${error51.stderrTail}`] : []
17532
17940
  logHeader.append(logTitle, copy);
17533
17941
  const log = document2.createElement("pre");
17534
17942
  log.className = "settings-connection-stderr";
17535
- log.textContent = item.error.stderrTail ?? item.error.diagnostic ?? report;
17943
+ log.textContent = report;
17536
17944
  const actions = document2.createElement("div");
17537
17945
  actions.className = "settings-connection-error-actions";
17538
17946
  const issue2 = document2.createElement("a");
@@ -17545,16 +17953,16 @@ ${error51.stderrTail}`] : []
17545
17953
  const issueNote = document2.createElement("p");
17546
17954
  issueNote.className = "settings-connection-issue-note";
17547
17955
  issueNote.textContent = messages.connectionIssueDescription;
17548
- body.append(summary, metadata, logHeader, log, actions, issueNote);
17956
+ body.append(summary, logHeader, log, actions, issueNote);
17549
17957
  } else {
17550
17958
  const status = document2.createElement("div");
17551
17959
  status.className = "settings-connection-state-summary";
17552
17960
  const title = document2.createElement("strong");
17553
17961
  title.textContent = connectionStatusLabel(item.availability, messages);
17554
17962
  const description = document2.createElement("p");
17555
- description.textContent = item.availability === "ready" ? messages.connectionReadyDescription : messages.connectionUnavailableDescription;
17963
+ description.textContent = item.availability === "updating" ? messages.harnessVersionUpdating : item.availability === "ready" ? messages.connectionReadyDescription : messages.connectionUnavailableDescription;
17556
17964
  status.append(title, description);
17557
- body.append(status);
17965
+ if (!item.agentSnapshot || item.availability !== "ready") body.append(status);
17558
17966
  if (item.openWebUi) {
17559
17967
  const open = document2.createElement("button");
17560
17968
  open.type = "button";
@@ -17578,7 +17986,7 @@ ${error51.stderrTail}`] : []
17578
17986
  const agent = item.agentSnapshot?.agent;
17579
17987
  const getLaunchSettings = diagnostics?.getLaunchSettings?.bind(diagnostics);
17580
17988
  const setLaunchSettings = diagnostics?.setLaunchSettings?.bind(diagnostics);
17581
- if (hostId === "local" && agent === "workbuddy" && getLaunchSettings && setLaunchSettings) {
17989
+ if (hostId === "local" && (agent === "zcode" || agent === "workbuddy") && getLaunchSettings && setLaunchSettings) {
17582
17990
  launchControls = existingLaunchControls ?? createHarnessLaunchControls(document2, messages, agent, {
17583
17991
  get: () => getLaunchSettings(hostId, agent),
17584
17992
  set: (path) => setLaunchSettings(hostId, agent, path)
@@ -17672,9 +18080,11 @@ ${error51.stderrTail}`] : []
17672
18080
  let selectedItemKey = null;
17673
18081
  let launchControls = null;
17674
18082
  const versionPanels = /* @__PURE__ */ new Map();
18083
+ const updatingVersions = /* @__PURE__ */ new Set();
17675
18084
  let latestSnapshot = null;
17676
18085
  let disposeHostScroller = () => void 0;
17677
18086
  const diagnostics = getDiagnostics();
18087
+ const installs = diagnostics ? harnessInstallStore(diagnostics) : null;
17678
18088
  const runRefresh = () => {
17679
18089
  if (pending || !diagnostics) return;
17680
18090
  pending = true;
@@ -17685,6 +18095,7 @@ ${error51.stderrTail}`] : []
17685
18095
  );
17686
18096
  void context.runLatest(() => diagnostics.refresh(), {
17687
18097
  success() {
18098
+ installs?.clearErrors();
17688
18099
  pending = false;
17689
18100
  refresh.disabled = false;
17690
18101
  refresh.replaceChildren(
@@ -17728,12 +18139,11 @@ ${error51.stderrTail}`] : []
17728
18139
  latestSnapshot = snapshot;
17729
18140
  disposeHostScroller();
17730
18141
  disposeHostScroller = () => void 0;
17731
- content.replaceChildren();
17732
18142
  if (!snapshot) {
17733
18143
  const empty = document2.createElement("div");
17734
18144
  empty.className = "settings-empty";
17735
18145
  empty.textContent = messages.connectionNoRuntime;
17736
- content.append(empty);
18146
+ content.replaceChildren(empty);
17737
18147
  return;
17738
18148
  }
17739
18149
  const selectedHost = (selectedHostId ? snapshot.hosts.find((host) => host.hostId === selectedHostId) : void 0) ?? snapshot.hosts.find((host) => host.active) ?? snapshot.hosts.find((host) => host.hostId === "local") ?? snapshot.hosts[0];
@@ -17805,7 +18215,24 @@ ${error51.stderrTail}`] : []
17805
18215
  const inspector = document2.createElement("aside");
17806
18216
  inspector.className = "settings-connection-inspector";
17807
18217
  inspector.setAttribute("aria-live", "polite");
17808
- const items = connectionItems(snapshot, selectedHost, messages, diagnostics);
18218
+ const items = connectionItems(snapshot, selectedHost, messages, diagnostics).map(
18219
+ (item) => {
18220
+ const agent = item.agentSnapshot?.agent;
18221
+ if (!agent) return item;
18222
+ const state = installs?.get(selectedHost.hostId, agent);
18223
+ return {
18224
+ ...item,
18225
+ ...state ? { availability: state.status } : {},
18226
+ ...updatingVersions.has(JSON.stringify([selectedHost.hostId, agent])) ? { availability: "updating" } : {},
18227
+ ...state?.error ? { installError: state.error } : {},
18228
+ ...diagnostics?.installation && harnessHasInstallCommands(agent) ? {
18229
+ install: () => {
18230
+ void installs?.install(selectedHost.hostId, agent);
18231
+ }
18232
+ } : {}
18233
+ };
18234
+ }
18235
+ );
17809
18236
  if (!items.some((item) => item.key === selectedItemKey)) {
17810
18237
  const requestedFocus = pendingFocusAgent;
17811
18238
  pendingFocusAgent = null;
@@ -17835,13 +18262,23 @@ ${error51.stderrTail}`] : []
17835
18262
  launchControls = { hostId: selectedHost.hostId, itemKey: item.key, element };
17836
18263
  const agent = item.agentSnapshot?.agent;
17837
18264
  const installation = diagnostics?.installation?.bind(diagnostics);
17838
- if (agent && item.availability !== "notInstalled" && item.availability !== "checking" && installation) {
18265
+ if (agent && item.agentSnapshot?.availability !== "notInstalled" && item.availability !== "installing" && item.availability !== "checking" && installation) {
17839
18266
  const key = JSON.stringify([selectedHost.hostId, agent]);
17840
18267
  let panel = versionPanels.get(key);
17841
18268
  if (!panel) {
17842
18269
  const hostId = selectedHost.hostId;
17843
18270
  panel = createHarnessVersionPanel(document2, messages, context.signal, agent, {
17844
- run: (action) => installation(hostId, agent, action)
18271
+ async run(action) {
18272
+ if (action !== "update") return installation(hostId, agent, action);
18273
+ updatingVersions.add(key);
18274
+ if (diagnostics) render(diagnostics.snapshot());
18275
+ try {
18276
+ return await installation(hostId, agent, action);
18277
+ } finally {
18278
+ updatingVersions.delete(key);
18279
+ if (!context.signal.aborted && diagnostics) render(diagnostics.snapshot());
18280
+ }
18281
+ }
17845
18282
  });
17846
18283
  versionPanels.set(key, panel);
17847
18284
  }
@@ -17866,7 +18303,7 @@ ${error51.stderrTail}`] : []
17866
18303
  const agentByKey = new Map(groupableItems.map((item) => [item.key, item]));
17867
18304
  const preferenceOrder = groupPreference.list(
17868
18305
  new Set(
17869
- groupableItems.filter((item) => item.availability === "notInstalled").map((item) => item.agentSnapshot.agent)
18306
+ groupableItems.filter((item) => item.agentSnapshot.availability === "notInstalled").map((item) => item.agentSnapshot.agent)
17870
18307
  )
17871
18308
  ).filter((entry) => agentByKey.has(entry.agent));
17872
18309
  for (const item of groupableItems) {
@@ -18007,7 +18444,7 @@ ${error51.stderrTail}`] : []
18007
18444
  if (selectedItem) selectItem(selectedItem);
18008
18445
  list.append(hostStrip, tableHeader, rows);
18009
18446
  layout.append(list, inspector);
18010
- content.append(layout);
18447
+ content.replaceChildren(layout);
18011
18448
  disposeHostScroller = configureHostScroller(
18012
18449
  document2,
18013
18450
  hostStrip,
@@ -18018,7 +18455,14 @@ ${error51.stderrTail}`] : []
18018
18455
  const selectedTab = [...tabs.children].find(
18019
18456
  (child) => child.getAttribute("aria-selected") === "true"
18020
18457
  );
18021
- selectedTab?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
18458
+ if (selectedTab) {
18459
+ const left = selectedTab.offsetLeft;
18460
+ const right = left + selectedTab.offsetWidth;
18461
+ if (left < tabs.scrollLeft) tabs.scrollLeft = left;
18462
+ else if (right > tabs.scrollLeft + tabs.clientWidth) {
18463
+ tabs.scrollLeft = right - tabs.clientWidth;
18464
+ }
18465
+ }
18022
18466
  };
18023
18467
  render(diagnostics?.snapshot() ?? null);
18024
18468
  const unsubscribeGroup = groupPreference.subscribe(
@@ -18033,8 +18477,10 @@ ${error51.stderrTail}`] : []
18033
18477
  }
18034
18478
  refresh.addEventListener("click", runRefresh);
18035
18479
  const unsubscribe = diagnostics.subscribe(() => render(diagnostics.snapshot()));
18480
+ const unsubscribeInstalls = installs?.subscribe(() => render(diagnostics.snapshot()));
18036
18481
  return () => {
18037
18482
  disposeHostScroller();
18483
+ unsubscribeInstalls?.();
18038
18484
  unsubscribe();
18039
18485
  unsubscribeGroup();
18040
18486
  };
@@ -18241,19 +18687,20 @@ ${error51.stderrTail}`] : []
18241
18687
  connectionCopyDetails: "Copy diagnostics",
18242
18688
  connectionCopied: "Copied",
18243
18689
  connectionCopyFailed: "Copy failed",
18244
- connectionErrorCode: "Error code",
18245
18690
  connectionErrorMessage: "Error message",
18246
- connectionRetryable: "Retryable",
18247
- connectionFailureStage: "Failure stage",
18248
- connectionDuration: "Duration",
18249
- connectionDiagnostic: "Diagnostic",
18250
18691
  connectionNoRuntime: "The renderer request bridge is not available yet.",
18251
18692
  connectionStatusReady: "Ready",
18693
+ connectionStatusConnected: "Connected",
18694
+ connectionLoginRequired: "Login required",
18695
+ connectionConfigurationRequired: "Setup required",
18696
+ connectionConfigurationDescription: "Configure a Provider and its required credentials in this Harness on the selected Host, then check the connection again.",
18697
+ connectionLoginDescription: "Complete login or authentication setup in this Harness on the selected Host, then check the connection again.",
18252
18698
  connectionStatusChecking: "Checking",
18253
18699
  connectionStatusNotInstalled: "Not installed",
18254
18700
  connectionStatusUnavailable: "Unavailable",
18255
18701
  connectionStatusError: "Error",
18256
18702
  connectionStatusInstalling: "Installing",
18703
+ connectionStatusUpdating: "Updating",
18257
18704
  connectionStatusUnsupported: "Unsupported",
18258
18705
  connectionComponent: "Component",
18259
18706
  connectionStatus: "Status",
@@ -18262,6 +18709,8 @@ ${error51.stderrTail}`] : []
18262
18709
  launchPathLabel: "Installed application path",
18263
18710
  launchPathPlaceholder: "Installation folder, e.g. D:\\program\\WorkBuddy",
18264
18711
  launchPathWorkbuddyHelp: "This integration requires the WorkBuddy app. If it is not detected automatically, enter its installation folder. codexhost locates the required files inside it. Restart codexhost after saving to apply.",
18712
+ launchPathZcodePlaceholder: "Application path, e.g. /Applications/ZCode.app or D:\\program\\ZCode",
18713
+ launchPathZcodeHelp: "This integration requires the ZCode Desktop app. If it is not detected automatically, enter its application path (the .app bundle on macOS, the installation folder on Windows and Linux). codexhost locates the required files inside it. Restart codexhost after saving to apply.",
18265
18714
  launchPathSave: "Save path",
18266
18715
  launchPathReset: "Clear override",
18267
18716
  launchPathRestart: "Saved. Restart codexhost to apply; running sessions are unchanged.",
@@ -18271,11 +18720,13 @@ ${error51.stderrTail}`] : []
18271
18720
  launchPathLoading: "Loading launch settings\u2026",
18272
18721
  launchPathLoadError: "Could not load launch settings. Reopen this detail panel to retry.",
18273
18722
  launchPathSaveError: "Could not save. Enter an existing absolute installation folder on this Host and check configuration permissions.",
18274
- connectionOpenInstallation: "Show installation instructions",
18723
+ connectionOfficialWebsite: "Visit official website",
18275
18724
  connectionOpenHarnessWeb: "Open DeepSeek Harness Web",
18276
- connectionDeepSeekTestedVersions: "Supported DSH versions: 0.1.2-rc.1, 0.1.5-rc.1, 0.1.5-rc.2, 0.1.5-rc.3, 0.1.7-rc.1 and 0.1.7-rc.2. Other versions may connect after native protocol checks, but are not listed as supported.",
18725
+ connectionDeepSeekTestedVersions: "Supported DSH versions: 0.1.7-rc.1, 0.1.7-rc.2, 0.2.0-rc.1 and 0.2.0-rc.2. Versions newer than 0.2.0-rc.2 can be tried, but compatibility may be limited; versions older than 0.1.7-rc.1 must be upgraded first.",
18277
18726
  connectionInstall: "Install",
18278
18727
  connectionInstallDescription: "This Harness was not detected.",
18728
+ connectionInstallRunning: "Installing on the selected Host. You can leave this page; login or setup may still be required afterward.",
18729
+ harnessVersion: harnessVersionEnglish,
18279
18730
  harnessVersionTitle: "Harness CLI version",
18280
18731
  harnessVersionCurrent: "Current version",
18281
18732
  harnessVersionLatest: "Latest version",
@@ -18362,6 +18813,7 @@ ${error51.stderrTail}`] : []
18362
18813
  aboutConsoleFailed: "The console could not be opened",
18363
18814
  pageLabels: Object.freeze({
18364
18815
  connections: "Connections",
18816
+ "remote-connections": "Remote connections",
18365
18817
  appearance: "General",
18366
18818
  accounts: "Accounts",
18367
18819
  "session-import": "Session Import",
@@ -18505,19 +18957,20 @@ ${error51.stderrTail}`] : []
18505
18957
  connectionCopyDetails: "\u590D\u5236\u8BCA\u65AD\u4FE1\u606F",
18506
18958
  connectionCopied: "\u5DF2\u590D\u5236",
18507
18959
  connectionCopyFailed: "\u590D\u5236\u5931\u8D25",
18508
- connectionErrorCode: "\u9519\u8BEF\u7801",
18509
18960
  connectionErrorMessage: "\u9519\u8BEF\u4FE1\u606F",
18510
- connectionRetryable: "\u53EF\u91CD\u8BD5",
18511
- connectionFailureStage: "\u5931\u8D25\u9636\u6BB5",
18512
- connectionDuration: "\u68C0\u67E5\u8017\u65F6",
18513
- connectionDiagnostic: "\u8BCA\u65AD\u4FE1\u606F",
18514
18961
  connectionNoRuntime: "Renderer \u8BF7\u6C42\u6865\u5C1A\u672A\u53EF\u7528\u3002",
18515
18962
  connectionStatusReady: "\u6B63\u5E38",
18963
+ connectionStatusConnected: "\u5DF2\u8FDE\u63A5",
18964
+ connectionLoginRequired: "\u9700\u8981\u767B\u5F55",
18965
+ connectionConfigurationRequired: "\u9700\u8981\u914D\u7F6E",
18966
+ connectionConfigurationDescription: "\u8BF7\u5728\u6240\u9009 Host \u4E0A\u914D\u7F6E\u8BE5 Harness \u7684 Provider \u53CA\u6240\u9700\u8BA4\u8BC1\u4FE1\u606F\uFF0C\u7136\u540E\u91CD\u65B0\u68C0\u6D4B\u8FDE\u63A5\u3002",
18967
+ connectionLoginDescription: "\u8BF7\u5728\u6240\u9009 Host \u4E0A\u5B8C\u6210\u8BE5 Harness \u7684\u767B\u5F55\u6216\u8BA4\u8BC1\u914D\u7F6E\uFF0C\u7136\u540E\u91CD\u65B0\u68C0\u6D4B\u8FDE\u63A5\u3002",
18516
18968
  connectionStatusChecking: "\u68C0\u67E5\u4E2D",
18517
18969
  connectionStatusNotInstalled: "\u672A\u5B89\u88C5",
18518
18970
  connectionStatusUnavailable: "\u4E0D\u53EF\u7528",
18519
18971
  connectionStatusError: "\u9519\u8BEF",
18520
18972
  connectionStatusInstalling: "\u5B89\u88C5\u4E2D",
18973
+ connectionStatusUpdating: "\u66F4\u65B0\u4E2D",
18521
18974
  connectionStatusUnsupported: "\u4E0D\u652F\u6301",
18522
18975
  connectionComponent: "\u7EC4\u4EF6",
18523
18976
  connectionStatus: "\u72B6\u6001",
@@ -18526,6 +18979,8 @@ ${error51.stderrTail}`] : []
18526
18979
  launchPathLabel: "\u5E94\u7528\u5B89\u88C5\u8DEF\u5F84",
18527
18980
  launchPathPlaceholder: "\u586B\u5199\u5B89\u88C5\u76EE\u5F55\uFF0C\u4F8B\u5982 D:\\program\\WorkBuddy",
18528
18981
  launchPathWorkbuddyHelp: "\u6B64\u63A5\u5165\u4F9D\u8D56 WorkBuddy \u5E94\u7528\u3002\u82E5\u672A\u81EA\u52A8\u8BC6\u522B\uFF0C\u8BF7\u586B\u5199\u5E94\u7528\u5B89\u88C5\u76EE\u5F55\uFF0Ccodexhost \u4F1A\u81EA\u52A8\u5B9A\u4F4D\u6240\u9700\u6587\u4EF6\u3002\u4FDD\u5B58\u540E\u91CD\u542F codexhost \u751F\u6548\u3002",
18982
+ launchPathZcodePlaceholder: "\u586B\u5199\u5E94\u7528\u8DEF\u5F84\uFF0C\u4F8B\u5982 /Applications/ZCode.app \u6216 D:\\program\\ZCode",
18983
+ launchPathZcodeHelp: "\u6B64\u63A5\u5165\u4F9D\u8D56 ZCode Desktop \u5E94\u7528\u3002\u82E5\u672A\u81EA\u52A8\u8BC6\u522B\uFF0C\u8BF7\u586B\u5199\u5E94\u7528\u8DEF\u5F84\uFF08macOS \u4E3A .app\uFF0CWindows \u548C Linux \u4E3A\u5B89\u88C5\u76EE\u5F55\uFF09\uFF0Ccodexhost \u4F1A\u81EA\u52A8\u5B9A\u4F4D\u6240\u9700\u6587\u4EF6\u3002\u4FDD\u5B58\u540E\u91CD\u542F codexhost \u751F\u6548\u3002",
18529
18984
  launchPathSave: "\u4FDD\u5B58\u8DEF\u5F84",
18530
18985
  launchPathReset: "\u6E05\u9664\u81EA\u5B9A\u4E49\u8DEF\u5F84",
18531
18986
  launchPathRestart: "\u5DF2\u4FDD\u5B58\uFF0C\u91CD\u542F codexhost \u540E\u751F\u6548\uFF1B\u5F53\u524D\u8FD0\u884C\u4E2D\u7684\u4F1A\u8BDD\u4E0D\u53D7\u5F71\u54CD\u3002",
@@ -18535,11 +18990,13 @@ ${error51.stderrTail}`] : []
18535
18990
  launchPathLoading: "\u6B63\u5728\u8BFB\u53D6\u542F\u52A8\u8BBE\u7F6E\u2026",
18536
18991
  launchPathLoadError: "\u65E0\u6CD5\u8BFB\u53D6\u542F\u52A8\u8BBE\u7F6E\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6B64\u8BE6\u60C5\u9762\u677F\u91CD\u8BD5\u3002",
18537
18992
  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",
18538
- connectionOpenInstallation: "\u67E5\u770B\u5B89\u88C5\u6307\u5F15",
18993
+ connectionOfficialWebsite: "\u8BBF\u95EE\u5B98\u7F51",
18539
18994
  connectionOpenHarnessWeb: "\u6253\u5F00 DeepSeek Harness Web",
18540
- connectionDeepSeekTestedVersions: "\u652F\u6301 DSH \u7248\u672C\uFF1A0.1.2-rc.1\u30010.1.5-rc.1\u30010.1.5-rc.2\u30010.1.5-rc.3\u30010.1.7-rc.1 \u548C 0.1.7-rc.2\u3002\u5176\u4ED6\u7248\u672C\u53EF\u4EE5\u5728\u901A\u8FC7\u539F\u751F\u534F\u8BAE\u68C0\u67E5\u540E\u5C1D\u8BD5\u8FDE\u63A5\uFF0C\u4F46\u5C1A\u672A\u5217\u5165\u652F\u6301\u5217\u8868\u3002",
18995
+ connectionDeepSeekTestedVersions: "\u652F\u6301 DSH \u7248\u672C\uFF1A0.1.7-rc.1\u30010.1.7-rc.2\u30010.2.0-rc.1 \u548C 0.2.0-rc.2\u3002\u9AD8\u4E8E 0.2.0-rc.2 \u7684\u7248\u672C\u53EF\u4EE5\u5C1D\u8BD5\u8FDE\u63A5\uFF0C\u4F46\u9002\u914D\u5EA6\u53EF\u80FD\u6709\u9650\uFF1B\u4F4E\u4E8E 0.1.7-rc.1 \u7684\u7248\u672C\u9700\u8981\u5148\u5347\u7EA7\u3002",
18541
18996
  connectionInstall: "\u5B89\u88C5",
18542
18997
  connectionInstallDescription: "\u5C1A\u672A\u68C0\u6D4B\u5230\u8BE5 Harness\u3002",
18998
+ connectionInstallRunning: "\u6B63\u5728\u6240\u9009 Host \u4E0A\u5B89\u88C5\uFF0C\u79BB\u5F00\u9875\u9762\u4E0D\u4F1A\u4E2D\u65AD\u3002\u5B89\u88C5\u540E\u53EF\u80FD\u4ECD\u9700\u767B\u5F55\u6216\u914D\u7F6E\u3002",
18999
+ harnessVersion: harnessVersionChinese,
18543
19000
  harnessVersionTitle: "Harness CLI \u7248\u672C",
18544
19001
  harnessVersionCurrent: "\u5F53\u524D\u7248\u672C",
18545
19002
  harnessVersionLatest: "\u6700\u65B0\u7248\u672C",
@@ -18626,6 +19083,7 @@ ${error51.stderrTail}`] : []
18626
19083
  aboutConsoleFailed: "\u63A7\u5236\u53F0\u6253\u5F00\u5931\u8D25",
18627
19084
  pageLabels: Object.freeze({
18628
19085
  connections: "\u8FDE\u63A5",
19086
+ "remote-connections": "\u8FDC\u7A0B\u8FDE\u63A5",
18629
19087
  appearance: "\u901A\u7528",
18630
19088
  accounts: "\u8D26\u53F7",
18631
19089
  "session-import": "\u4F1A\u8BDD\u5BFC\u5165",
@@ -18684,7 +19142,8 @@ ${error51.stderrTail}`] : []
18684
19142
  hermes: "https://hermes-agent.nousresearch.com/docs",
18685
19143
  qoder: "https://docs.qoder.com/",
18686
19144
  "qoder-cn": "https://docs.qoder.cn/",
18687
- "kimi-code": "https://moonshotai.github.io/kimi-code/en/guides/getting-started.html"
19145
+ "kimi-code": "https://moonshotai.github.io/kimi-code/en/guides/getting-started.html",
19146
+ zcode: "https://github.com/BytePioneer-AI/codex-host/blob/main/docs/harnesses/zcode/zcode-harness-integration.md#%E5%AE%89%E8%A3%85"
18688
19147
  };
18689
19148
  var CONTROL_ATTRIBUTE = "data-codexhost-agent-control";
18690
19149
  var AGENT_MENU_WIDTH = 224;
@@ -22027,7 +22486,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22027
22486
  const thinkingReady = availableThinkingOptions.length === 0 || availableThinkingOptions.some(({ id }) => id === modelView.selectedThinkingOptionId);
22028
22487
  const modelReady = selectedModel !== void 0 && selectedCatalogModel !== void 0;
22029
22488
  const modelBlocked = state.agent !== "codex" && (modelView.status === "selecting" || !modelReady || !thinkingReady);
22030
- const permissionModeBlocked = state.agent !== "codex" && (!isPermissionModeControlReady(permissionModeView) || permissionModeView.status !== "unsupported" && !control.nativePermissionModeControlVerified);
22489
+ const permissionModeBlocked = state.agent !== "codex" && !isPermissionModeControlReady(permissionModeView);
22031
22490
  const submissionBlocked = switching || ownershipError || modelBlocked || permissionModeBlocked;
22032
22491
  if (submissionBlocked && control.sendDisabledBeforeSwitch === null) {
22033
22492
  control.sendDisabledBeforeSwitch = control.sendButton.disabled;
@@ -22665,6 +23124,18 @@ Codex Fast mode prioritizes requests and may increase usage.`
22665
23124
  };
22666
23125
  }
22667
23126
 
23127
+ // src/renderer-native-thread.ts
23128
+ function isRecord3(value) {
23129
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23130
+ }
23131
+ async function verifyNativeCodexThread(sendRequest, threadId) {
23132
+ const native = await sendRequest("thread/read", { threadId, includeTurns: false });
23133
+ const thread = isRecord3(native) ? native.thread : null;
23134
+ if (!isRecord3(thread) || thread.id !== threadId || typeof thread.modelProvider !== "string" || !thread.modelProvider || thread.modelProvider === "codexhost" || typeof thread.cliVersion !== "string" || !thread.cliVersion || thread.cliVersion === "codexhost") {
23135
+ throw new Error("Native Thread response cannot establish Codex ownership");
23136
+ }
23137
+ }
23138
+
22668
23139
  // src/renderer-session-import-client.ts
22669
23140
  var RendererSessionImportUnavailableError = class extends Error {
22670
23141
  constructor() {
@@ -22739,15 +23210,15 @@ Codex Fast mode prioritizes requests and may increase usage.`
22739
23210
  var CODEX_ACCOUNT_LIST_METHOD = "codexhost/account/list";
22740
23211
  var CODEX_ACCOUNT_REFRESH_METHOD = "codexhost/account/refresh";
22741
23212
  var CODEX_ACCOUNT_CHANGED_METHOD = "codexhost/account/changed";
22742
- function isRecord3(value) {
23213
+ function isRecord4(value) {
22743
23214
  return typeof value === "object" && value !== null && !Array.isArray(value);
22744
23215
  }
22745
23216
  function notifiedThreadId(notification) {
22746
- if (!isRecord3(notification) || !THREAD_USAGE_REFRESH_METHODS.includes(notification.method)) {
23217
+ if (!isRecord4(notification) || !THREAD_USAGE_REFRESH_METHODS.includes(notification.method)) {
22747
23218
  return null;
22748
23219
  }
22749
23220
  const params = notification.params;
22750
- if (!isRecord3(params)) return null;
23221
+ if (!isRecord4(params)) return null;
22751
23222
  const parsed = hostThreadIdSchema.safeParse(params.threadId);
22752
23223
  return parsed.success ? parsed.data : null;
22753
23224
  }
@@ -22919,14 +23390,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22919
23390
  result = await manager.sendRequest(THREAD_INSPECT_METHOD, params);
22920
23391
  } catch (error51) {
22921
23392
  if (!(error51 instanceof RendererMethodUnavailableError)) throw error51;
22922
- const native = await manager.sendRequest("thread/read", {
22923
- threadId: params.threadId,
22924
- includeTurns: false
22925
- });
22926
- const thread = isRecord3(native) ? native.thread : null;
22927
- if (!isRecord3(thread) || thread.id !== params.threadId || typeof thread.modelProvider !== "string" || !thread.modelProvider || thread.modelProvider === "codexhost" || typeof thread.cliVersion !== "string" || !thread.cliVersion || thread.cliVersion === "codexhost") {
22928
- throw new Error("Native Thread response cannot establish Codex ownership");
22929
- }
23393
+ await verifyNativeCodexThread(manager.sendRequest, params.threadId);
22930
23394
  return { owner: "codex", locked: true };
22931
23395
  }
22932
23396
  return threadInspectionSchema.parse(result);
@@ -22973,6 +23437,24 @@ Codex Fast mode prioritizes requests and may increase usage.`
22973
23437
  selectThreadModel,
22974
23438
  selectThreadThinking,
22975
23439
  selectThreadPermissionMode,
23440
+ async setupSsh(input) {
23441
+ return remoteSshSetupResultSchema.parse(
23442
+ await manager.sendRequest(REMOTE_SSH_SETUP_METHOD, remoteSshSetupParamsSchema.parse(input))
23443
+ );
23444
+ },
23445
+ async runtimeStatus() {
23446
+ try {
23447
+ return runtimeStatusSchema.parse(await source.sendRequest(RUNTIME_STATUS_METHOD, {}));
23448
+ } catch (error51) {
23449
+ if (!isUnsupportedMethod(error51, RUNTIME_STATUS_METHOD)) throw error51;
23450
+ throw new RendererMethodUnavailableError(RUNTIME_STATUS_METHOD, error51);
23451
+ }
23452
+ },
23453
+ async updateRemote(version2) {
23454
+ return runtimeStatusSchema.parse(
23455
+ await manager.sendRequest(REMOTE_UPDATE_METHOD, { version: version2 })
23456
+ );
23457
+ },
22976
23458
  async checkUpdate() {
22977
23459
  const result = await manager.sendRequest(
22978
23460
  UPDATE_CHECK_METHOD,
@@ -23051,7 +23533,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23051
23533
  throw new Error("Renderer Account notification callback is unavailable");
23052
23534
  }
23053
23535
  return notifications.addNotificationCallback(CODEX_ACCOUNT_CHANGED_METHOD, (notification) => {
23054
- if (!isRecord3(notification) || notification.method !== CODEX_ACCOUNT_CHANGED_METHOD) return;
23536
+ if (!isRecord4(notification) || notification.method !== CODEX_ACCOUNT_CHANGED_METHOD) return;
23055
23537
  const state = codexAccountChangedSchema.safeParse(notification.params);
23056
23538
  if (state.success) listener(state.data);
23057
23539
  });
@@ -23060,16 +23542,16 @@ Codex Fast mode prioritizes requests and may increase usage.`
23060
23542
  }
23061
23543
 
23062
23544
  // src/renderer-external-queue.ts
23063
- function isRecord4(value) {
23545
+ function isRecord5(value) {
23064
23546
  return value !== null && typeof value === "object" && !Array.isArray(value);
23065
23547
  }
23066
23548
  function installRendererExternalQueue(target) {
23067
- if (!isRecord4(target) || typeof target.getTurnCoordinator !== "function" || typeof target.getConversation !== "function") {
23549
+ if (!isRecord5(target) || typeof target.getTurnCoordinator !== "function" || typeof target.getConversation !== "function") {
23068
23550
  return null;
23069
23551
  }
23070
23552
  const coordinator = target.getTurnCoordinator();
23071
- const queue = isRecord4(coordinator) ? coordinator.serverQueue : null;
23072
- if (!isRecord4(queue) || typeof queue.isEnabled !== "function") return null;
23553
+ const queue = isRecord5(coordinator) ? coordinator.serverQueue : null;
23554
+ if (!isRecord5(queue) || typeof queue.isEnabled !== "function") return null;
23073
23555
  const descriptor = Object.getOwnPropertyDescriptor(queue, "isEnabled");
23074
23556
  if (!descriptor?.writable) return null;
23075
23557
  const original = queue.isEnabled;
@@ -23078,7 +23560,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23078
23560
  const threadId = args[0];
23079
23561
  if (typeof threadId === "string") {
23080
23562
  const conversation = getConversation.call(target, threadId);
23081
- if (isRecord4(conversation) && conversation.id === threadId && // Reserved transport marker from externalThreadValue, retained by
23563
+ if (isRecord5(conversation) && conversation.id === threadId && // Reserved transport marker from externalThreadValue, retained by
23082
23564
  // Desktop in both newly created and hydrated conversation metadata.
23083
23565
  conversation.modelProvider === "codexhost") {
23084
23566
  return false;
@@ -23093,19 +23575,20 @@ Codex Fast mode prioritizes requests and may increase usage.`
23093
23575
  }
23094
23576
 
23095
23577
  // src/renderer-external-steering.ts
23096
- function isRecord5(value) {
23578
+ var THREAD_OWNERSHIP_LIST_METHOD2 = "codexhost/thread/ownership/list";
23579
+ function isRecord6(value) {
23097
23580
  return value !== null && typeof value === "object" && !Array.isArray(value);
23098
23581
  }
23099
23582
  function isManager(value) {
23100
- return isRecord5(value) && ["sendRequest", "steerTurn", "startTurn", "getTurnCoordinator"].every(
23583
+ return isRecord6(value) && ["sendRequest", "steerTurn", "startTurn", "getTurnCoordinator"].every(
23101
23584
  (key) => typeof value[key] === "function"
23102
23585
  );
23103
23586
  }
23104
23587
  function submissionHost(manager) {
23105
23588
  const coordinator = manager.getTurnCoordinator();
23106
- const options = isRecord5(coordinator) ? coordinator.options : null;
23107
- const host = isRecord5(options) ? options.submissionHost : null;
23108
- if (!isRecord5(host) || typeof host.getActiveTurnId !== "function" || typeof host.hasPendingTurnStart !== "function") {
23589
+ const options = isRecord6(coordinator) ? coordinator.options : null;
23590
+ const host = isRecord6(options) ? options.submissionHost : null;
23591
+ if (!isRecord6(host) || typeof host.getActiveTurnId !== "function" || typeof host.hasPendingTurnStart !== "function") {
23109
23592
  throw new Error("Desktop turn submission binding is unavailable");
23110
23593
  }
23111
23594
  return {
@@ -23147,20 +23630,20 @@ Codex Fast mode prioritizes requests and may increase usage.`
23147
23630
  var INTERRUPTED_QUEUE_REASON = "Interrupted before the steer was accepted.";
23148
23631
  async function preserveQueuedFollowUps(manager, threadId) {
23149
23632
  const coordinator = manager.getTurnCoordinator();
23150
- if (!isRecord5(coordinator) || typeof coordinator.loadMessages !== "function" || typeof coordinator.readMessages !== "function" || typeof coordinator.mutate !== "function") {
23633
+ if (!isRecord6(coordinator) || typeof coordinator.loadMessages !== "function" || typeof coordinator.readMessages !== "function" || typeof coordinator.mutate !== "function") {
23151
23634
  throw new Error("Desktop follow-up queue binding is unavailable");
23152
23635
  }
23153
23636
  await coordinator.loadMessages.call(coordinator, threadId);
23154
23637
  const messages = coordinator.readMessages.call(coordinator, threadId);
23155
23638
  if (!Array.isArray(messages)) throw new Error("Desktop follow-up queue is unavailable");
23156
23639
  const alreadyPaused = new Set(
23157
- messages.filter((message) => isRecord5(message) && message.pausedReason != null).map((message) => message.id)
23640
+ messages.filter((message) => isRecord6(message) && message.pausedReason != null).map((message) => message.id)
23158
23641
  );
23159
23642
  return () => {
23160
23643
  coordinator.mutate.call(coordinator, threadId, (current) => {
23161
23644
  if (!Array.isArray(current)) return current;
23162
23645
  return current.map((message) => {
23163
- if (!isRecord5(message) || alreadyPaused.has(message.id) || message.pausedReason !== INTERRUPTED_QUEUE_REASON)
23646
+ if (!isRecord6(message) || alreadyPaused.has(message.id) || message.pausedReason !== INTERRUPTED_QUEUE_REASON)
23164
23647
  return message;
23165
23648
  const resumed = { ...message };
23166
23649
  delete resumed.pausedReason;
@@ -23178,9 +23661,9 @@ Codex Fast mode prioritizes requests and may increase usage.`
23178
23661
  const pending = /* @__PURE__ */ new Map();
23179
23662
  let disposed = false;
23180
23663
  const send = function(method, params, options) {
23181
- const messageId = isRecord5(params) ? params.clientUserMessageId : null;
23182
- const route = typeof messageId === "string" && isRecord5(params) ? routes.get(`${params.threadId}\0${messageId}`) : void 0;
23183
- if (method !== "turn/start" || !isRecord5(params) || !route || route.threadId !== params.threadId) {
23664
+ const messageId = isRecord6(params) ? params.clientUserMessageId : null;
23665
+ const route = typeof messageId === "string" && isRecord6(params) ? routes.get(`${params.threadId}\0${messageId}`) : void 0;
23666
+ if (method !== "turn/start" || !isRecord6(params) || !route || route.threadId !== params.threadId) {
23184
23667
  return originalSend.call(manager, method, params, options);
23185
23668
  }
23186
23669
  if (disposed) return Promise.reject(new Error("External steering binding was disposed"));
@@ -23199,7 +23682,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23199
23682
  options
23200
23683
  )
23201
23684
  ).then((response) => {
23202
- if (!isRecord5(response) || typeof response.turnId !== "string" || !response.turnId) {
23685
+ if (!isRecord6(response) || typeof response.turnId !== "string" || !response.turnId) {
23203
23686
  throw new Error("External steering returned no replacement Turn identity");
23204
23687
  }
23205
23688
  return {
@@ -23230,7 +23713,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23230
23713
  ] = args;
23231
23714
  if (typeof threadId !== "string") return originalSteer.apply(manager, args);
23232
23715
  const role = manager.getStreamRole?.(threadId);
23233
- if (isRecord5(role) && role.role === "follower") return originalSteer.apply(manager, args);
23716
+ if (isRecord6(role) && role.role === "follower") return originalSteer.apply(manager, args);
23234
23717
  let host = null;
23235
23718
  let expectedTurnId;
23236
23719
  try {
@@ -23238,26 +23721,36 @@ Codex Fast mode prioritizes requests and may increase usage.`
23238
23721
  expectedTurnId = host.getActiveTurnId(threadId);
23239
23722
  } catch {
23240
23723
  }
23241
- const ownership = threadOwnershipListResultSchema.parse(
23242
- await originalSend.call(manager, "codexhost/thread/ownership/list", {
23724
+ let ownershipResponse;
23725
+ try {
23726
+ ownershipResponse = await originalSend.call(manager, THREAD_OWNERSHIP_LIST_METHOD2, {
23243
23727
  threadIds: [threadId]
23244
- })
23245
- );
23728
+ });
23729
+ } catch (error51) {
23730
+ if (!isUnsupportedMethod(error51, THREAD_OWNERSHIP_LIST_METHOD2)) throw error51;
23731
+ await verifyNativeCodexThread(
23732
+ (method, params) => originalSend.call(manager, method, params),
23733
+ threadId
23734
+ );
23735
+ if (disposed) throw new Error("External steering binding was disposed");
23736
+ return originalSteer.apply(manager, args);
23737
+ }
23738
+ const ownership = threadOwnershipListResultSchema.parse(ownershipResponse);
23246
23739
  const owner = ownership.threads.find((thread) => thread.threadId === threadId)?.owner;
23247
23740
  if (!owner) throw new Error("Thread ownership could not be resolved for steering");
23248
23741
  if (disposed) throw new Error("External steering binding was disposed");
23249
23742
  if (owner === "codex") return originalSteer.apply(manager, args);
23250
23743
  if (!host) throw new Error("Desktop turn submission binding is unavailable");
23251
23744
  const currentRole = manager.getStreamRole?.(threadId);
23252
- if (isRecord5(currentRole) && currentRole.role === "follower")
23745
+ if (isRecord6(currentRole) && currentRole.role === "follower")
23253
23746
  return originalSteer.apply(manager, args);
23254
23747
  if (!Array.isArray(input) || input.length === 0 || input.some(
23255
- (item) => !isRecord5(item) || item.type !== "text" || typeof item.text !== "string"
23256
- ) || !input.some((item) => isRecord5(item) && typeof item.text === "string" && item.text.trim())) {
23748
+ (item) => !isRecord6(item) || item.type !== "text" || typeof item.text !== "string"
23749
+ ) || !input.some((item) => isRecord6(item) && typeof item.text === "string" && item.text.trim())) {
23257
23750
  throw new Error("External steering requires non-empty text input");
23258
23751
  }
23259
23752
  if (toolOutput != null) throw new Error("External steering cannot replace a tool response");
23260
- if (!isRecord5(restoreMessage) || !isRecord5(restoreMessage.context)) {
23753
+ if (!isRecord6(restoreMessage) || !isRecord6(restoreMessage.context)) {
23261
23754
  throw new Error("Desktop steering message context is unavailable");
23262
23755
  }
23263
23756
  const messageId = typeof clientUserMessageId === "string" && clientUserMessageId ? clientUserMessageId : typeof restoreMessage.id === "string" && restoreMessage.id ? restoreMessage.id : crypto.randomUUID();
@@ -23305,7 +23798,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23305
23798
  },
23306
23799
  onMessageAdded
23307
23800
  );
23308
- if (!isRecord5(response) || !isRecord5(response.turn) || typeof response.turn.id !== "string") {
23801
+ if (!isRecord6(response) || !isRecord6(response.turn) || typeof response.turn.id !== "string") {
23309
23802
  throw new Error("Desktop returned no replacement Turn identity");
23310
23803
  }
23311
23804
  try {
@@ -23336,9 +23829,9 @@ Codex Fast mode prioritizes requests and may increase usage.`
23336
23829
  }
23337
23830
 
23338
23831
  // src/renderer-thread-reference-capability.ts
23339
- var record2 = (value) => typeof value === "object" && value !== null;
23832
+ var record3 = (value) => typeof value === "object" && value !== null;
23340
23833
  function restoreThreadReferenceCapability(client, manager, isCurrent) {
23341
- if (!record2(manager) || !record2(manager.storage)) return client;
23834
+ if (!record3(manager) || !record3(manager.storage)) return client;
23342
23835
  const storage = manager.storage;
23343
23836
  const read = storage.readValue;
23344
23837
  const write = storage.writeValue;
@@ -23373,11 +23866,11 @@ Codex Fast mode prioritizes requests and may increase usage.`
23373
23866
  }
23374
23867
 
23375
23868
  // src/renderer-manual-compaction.ts
23376
- function isRecord6(value) {
23869
+ function isRecord7(value) {
23377
23870
  return value !== null && typeof value === "object" && !Array.isArray(value);
23378
23871
  }
23379
23872
  function installRendererManualCompaction(target, hostId, messages) {
23380
- if (!messages || typeof messages.addEventListener !== "function" || typeof messages.removeEventListener !== "function" || !isRecord6(target) || typeof target.registerPendingManualContextCompaction !== "function" || typeof target.getConversation !== "function") {
23873
+ if (!messages || typeof messages.addEventListener !== "function" || typeof messages.removeEventListener !== "function" || !isRecord7(target) || typeof target.registerPendingManualContextCompaction !== "function" || typeof target.getConversation !== "function") {
23381
23874
  return null;
23382
23875
  }
23383
23876
  const register = target.registerPendingManualContextCompaction;
@@ -23386,18 +23879,18 @@ Codex Fast mode prioritizes requests and may increase usage.`
23386
23879
  const listener = (event) => {
23387
23880
  if (event.source != null && event.source !== messages) return;
23388
23881
  const message = event.data;
23389
- if (!isRecord6(message) || message.type !== "mcp-notification" || message.hostId !== hostId || message.method !== THREAD_MANUAL_COMPACTION_STARTED_METHOD) {
23882
+ if (!isRecord7(message) || message.type !== "mcp-notification" || message.hostId !== hostId || message.method !== THREAD_MANUAL_COMPACTION_STARTED_METHOD) {
23390
23883
  return;
23391
23884
  }
23392
23885
  const params = threadManualCompactionStartedSchema.safeParse(message.params);
23393
23886
  if (!params.success) return;
23394
23887
  const { threadId } = params.data;
23395
23888
  const conversation = getConversation.call(target, threadId);
23396
- if (!isRecord6(conversation) || conversation.id !== threadId || conversation.modelProvider !== "codexhost") {
23889
+ if (!isRecord7(conversation) || conversation.id !== threadId || conversation.modelProvider !== "codexhost") {
23397
23890
  return;
23398
23891
  }
23399
23892
  const role = getStreamRole?.call(target, threadId);
23400
- if (isRecord6(role) && role.role === "follower") return;
23893
+ if (isRecord7(role) && role.role === "follower") return;
23401
23894
  register.call(target, threadId);
23402
23895
  };
23403
23896
  messages.addEventListener("message", listener);
@@ -23575,7 +24068,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23575
24068
  var SIDEBAR_THREAD_HOST_ID_ATTRIBUTE = "data-app-action-sidebar-thread-host-id";
23576
24069
  var SIDEBAR_AGENT_ICON_ATTRIBUTE = "data-codexhost-sidebar-agent-icon";
23577
24070
  var OWNERSHIP_RETRY_DELAYS_MS = [100, 300, 800, 1500, 3e3];
23578
- function isRecord7(value) {
24071
+ function isRecord8(value) {
23579
24072
  return typeof value === "object" && value !== null && !Array.isArray(value);
23580
24073
  }
23581
24074
  function sidebarThreadAttributes(element) {
@@ -23602,19 +24095,19 @@ Codex Fast mode prioritizes requests and may increase usage.`
23602
24095
  const fiberName = fiberNames[0];
23603
24096
  if (fiberNames.length !== 1 || !fiberName) return null;
23604
24097
  const firstFiber2 = Object.getOwnPropertyDescriptor(element, fiberName)?.value;
23605
- if (!isRecord7(firstFiber2)) return null;
24098
+ if (!isRecord8(firstFiber2)) return null;
23606
24099
  const candidates = /* @__PURE__ */ new Set();
23607
24100
  let fiber = firstFiber2;
23608
24101
  for (let depth = 0; fiber && depth < 12; depth += 1) {
23609
24102
  const props = fiber.memoizedProps;
23610
- if (isRecord7(props) && isRecord7(props.dataAttributes)) {
24103
+ if (isRecord8(props) && isRecord8(props.dataAttributes)) {
23611
24104
  const dataAttributes = props.dataAttributes;
23612
24105
  const threadId = hostThreadIdSchema.safeParse(props.conversationId);
23613
24106
  if (threadId.success && dataAttributes[SIDEBAR_THREAD_ROW_ATTRIBUTE] === rowMarker && dataAttributes[SIDEBAR_THREAD_ID_ATTRIBUTE] === taskKey && dataAttributes[SIDEBAR_THREAD_HOST_ID_ATTRIBUTE] === hostId) {
23614
24107
  candidates.add(threadId.data);
23615
24108
  }
23616
24109
  }
23617
- fiber = isRecord7(fiber.return) ? fiber.return : null;
24110
+ fiber = isRecord8(fiber.return) ? fiber.return : null;
23618
24111
  }
23619
24112
  return candidates.size === 1 ? candidates.values().next().value ?? null : null;
23620
24113
  }
@@ -23635,6 +24128,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23635
24128
  if (ownership.harnessId === "qoder") return "qoder";
23636
24129
  if (ownership.harnessId === "qoder-cn") return "qoder-cn";
23637
24130
  if (ownership.harnessId === "kimi-code") return "kimi-code";
24131
+ if (ownership.harnessId === "zcode") return "zcode";
23638
24132
  return null;
23639
24133
  }
23640
24134
  function sidebarAgentIconAnchor(titleTrigger, title) {
@@ -23983,7 +24477,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23983
24477
  if (row) finish(row);
23984
24478
  });
23985
24479
  }
23986
- function isRecord8(value) {
24480
+ function isRecord9(value) {
23987
24481
  return typeof value === "object" && value !== null && !Array.isArray(value);
23988
24482
  }
23989
24483
  function firstFiber(element) {
@@ -23993,7 +24487,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23993
24487
  const name = names[0];
23994
24488
  if (names.length !== 1 || !name) return null;
23995
24489
  const value = Object.getOwnPropertyDescriptor(element, name)?.value;
23996
- return isRecord8(value) ? value : null;
24490
+ return isRecord9(value) ? value : null;
23997
24491
  }
23998
24492
  function directForkAncestors(first) {
23999
24493
  const ancestors = [];
@@ -24003,7 +24497,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24003
24497
  if (seen.has(fiber)) return [];
24004
24498
  seen.add(fiber);
24005
24499
  ancestors.push(fiber);
24006
- fiber = isRecord8(fiber.return) ? fiber.return : null;
24500
+ fiber = isRecord9(fiber.return) ? fiber.return : null;
24007
24501
  }
24008
24502
  return ancestors;
24009
24503
  }
@@ -24021,7 +24515,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24021
24515
  let hasForkCallback = false;
24022
24516
  for (const fiber of ancestors) {
24023
24517
  const props = fiber.memoizedProps;
24024
- if (!isRecord8(props)) continue;
24518
+ if (!isRecord9(props)) continue;
24025
24519
  if (!hasForkCallback && typeof props.onFork !== "function" && ownsOtherCallback(fiber, props)) {
24026
24520
  return null;
24027
24521
  }
@@ -24053,7 +24547,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24053
24547
  if (!threadId.success || !turnId.success) return null;
24054
24548
  const fiber = firstFiber(button);
24055
24549
  const buttonProps = fiber?.memoizedProps;
24056
- if (!fiber || !isRecord8(buttonProps) || !Object.hasOwn(buttonProps, "aria-busy")) return null;
24550
+ if (!fiber || !isRecord9(buttonProps) || !Object.hasOwn(buttonProps, "aria-busy")) return null;
24057
24551
  return forkTargetFromAncestors(button, threadId.data, turnId.data, directForkAncestors(fiber)) ?? forkTargetFromAncestors(button, threadId.data, turnId.data, committedReactAncestors(fiber));
24058
24552
  }
24059
24553
  var BrowserRendererForkDom = class {
@@ -24162,12 +24656,12 @@ Codex Fast mode prioritizes requests and may increase usage.`
24162
24656
  if (agent === "kiro-cli") return encodeHarnessPluginRoute({ harnessId: KIRO_CLI_HARNESS_ID });
24163
24657
  if (agent === "codebuddy" || agent === "workbuddy" || agent === "cursor-cli")
24164
24658
  return encodeHarnessPluginRoute({ harnessId: harnessIdSchema.parse(agent) });
24165
- if (agent === "qoder" || agent === "qoder-cn" || agent === "kimi-code") {
24659
+ if (agent === "qoder" || agent === "qoder-cn" || agent === "kimi-code" || agent === "zcode") {
24166
24660
  return encodeHarnessPluginRoute({ harnessId: harnessIdSchema.parse(agent) });
24167
24661
  }
24168
24662
  return null;
24169
24663
  }
24170
- function isRecord9(value) {
24664
+ function isRecord10(value) {
24171
24665
  return typeof value === "object" && value !== null && !Array.isArray(value);
24172
24666
  }
24173
24667
  function piTransportModelId(model, thinkingOptionId) {
@@ -24426,7 +24920,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24426
24920
  let fiber = findComposerFiber(composer);
24427
24921
  for (let depth = 0; fiber && depth < 120; depth += 1) {
24428
24922
  const props = fiber.memoizedProps;
24429
- if (isRecord9(props) && "conversationId" in props && props.conversationId != null) {
24923
+ if (isRecord10(props) && "conversationId" in props && props.conversationId != null) {
24430
24924
  const candidate = hostThreadIdSchema.safeParse(props.conversationId);
24431
24925
  if (!candidate.success || threadId !== void 0 && threadId !== candidate.data) {
24432
24926
  return null;
@@ -24439,12 +24933,12 @@ Codex Fast mode prioritizes requests and may increase usage.`
24439
24933
  return threadId;
24440
24934
  }
24441
24935
  function isLegacySevenSlotDraftWrapper(value) {
24442
- if (!Array.isArray(value) || value.length !== 7 || value[3] !== value[5] || value[3] !== value[6] || !isRecord9(value[3]) || typeof value[3].get !== "function" || typeof value[2] !== "string" && value[2] !== null) {
24936
+ if (!Array.isArray(value) || value.length !== 7 || value[3] !== value[5] || value[3] !== value[6] || !isRecord10(value[3]) || typeof value[3].get !== "function" || typeof value[2] !== "string" && value[2] !== null) {
24443
24937
  return false;
24444
24938
  }
24445
24939
  try {
24446
24940
  const draft = value[3].get();
24447
- return isRecord9(draft) && "modelSettings" in draft && "isManuallyChanged" in draft;
24941
+ return isRecord10(draft) && "modelSettings" in draft && "isManuallyChanged" in draft;
24448
24942
  } catch {
24449
24943
  return false;
24450
24944
  }
@@ -24478,8 +24972,8 @@ Codex Fast mode prioritizes requests and may increase usage.`
24478
24972
  let fiber = findComposerFiber(composer);
24479
24973
  for (let depth = 0; fiber && depth < 120; depth += 1) {
24480
24974
  const updateQueue = fiber.updateQueue;
24481
- const memoCache = isRecord9(updateQueue) ? updateQueue.memoCache : null;
24482
- const data = isRecord9(memoCache) && Array.isArray(memoCache.data) ? memoCache.data : [];
24975
+ const memoCache = isRecord10(updateQueue) ? updateQueue.memoCache : null;
24976
+ const data = isRecord10(memoCache) && Array.isArray(memoCache.data) ? memoCache.data : [];
24483
24977
  for (const value of data) {
24484
24978
  const draftId = draftIdFromMemoValue(value);
24485
24979
  if (draftId) draftIds.add(draftId);
@@ -24506,7 +25000,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24506
25000
  return ["default", draftIds.values().next().value];
24507
25001
  }
24508
25002
  function isDraftPrewarmPolicyReady(value) {
24509
- return isRecord9(value) && value.state === "ready" && typeof value.hostId === "string" && value.hostId.length > 0 && typeof value.select === "function" && typeof value.clear === "function";
25003
+ return isRecord10(value) && value.state === "ready" && typeof value.hostId === "string" && value.hostId.length > 0 && typeof value.select === "function" && typeof value.clear === "function";
24510
25004
  }
24511
25005
  async function waitForRendererDraftPrewarmPolicy(target, composer) {
24512
25006
  const deadline = Date.now() + DRAFT_PREWARM_POLICY_WAIT_TIMEOUT_MS;
@@ -24526,7 +25020,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24526
25020
  ...model ? { model } : {},
24527
25021
  ...thinkingOptionId && agent !== "cursor-cli" ? { thinkingOptionId } : {},
24528
25022
  ...permissionModeId ? { permissionModeId } : {}
24529
- }) : agent === "hermes" ? hermesTransportModelId(model, permissionModeId) : agent === "qoder" || agent === "qoder-cn" || agent === "kimi-code" ? encodeHarnessPluginRoute({
25023
+ }) : agent === "hermes" ? hermesTransportModelId(model, permissionModeId) : agent === "qoder" || agent === "qoder-cn" || agent === "kimi-code" || agent === "zcode" ? encodeHarnessPluginRoute({
24530
25024
  harnessId: harnessIdSchema.parse(agent),
24531
25025
  ...model ? { model } : {},
24532
25026
  ...thinkingOptionId ? { thinkingOptionId } : {},
@@ -24567,13 +25061,15 @@ Codex Fast mode prioritizes requests and may increase usage.`
24567
25061
  const route = disposed ? null : window.__codexhostHostRoutingV1?.forComposer() ?? null;
24568
25062
  const client = clients.forRoute(route);
24569
25063
  usageSubscription.connect(client);
24570
- idleReleaseSync.connect(
24571
- disposed ? null : route?.hostId === "local" ? client : clients.forHost("local")
24572
- );
25064
+ const localClient = disposed ? null : route?.hostId === "local" ? client : clients.forHost("local");
25065
+ idleReleaseSync.connect(localClient);
25066
+ const connected = !disposed && (client !== null || localClient !== null || (window.__codexhostHostRoutingV1?.knownHostIds?.() ?? []).some(
25067
+ (hostId) => window.__codexhostHostRoutingV1?.forHost(hostId) != null
25068
+ ));
24573
25069
  updateStatus(
24574
- route ? "ready" : "installing",
24575
- route ? "ready" : fiberWalkLimited ? "react-fiber-walk-limit-exceeded" : "draft-routing-policy-unavailable",
24576
- route ? "request-bridge" : null
25070
+ connected ? "ready" : "installing",
25071
+ connected ? "ready" : fiberWalkLimited ? "react-fiber-walk-limit-exceeded" : "draft-routing-policy-unavailable",
25072
+ connected ? "request-bridge" : null
24577
25073
  );
24578
25074
  return route;
24579
25075
  };
@@ -24588,10 +25084,14 @@ Codex Fast mode prioritizes requests and may increase usage.`
24588
25084
  return client;
24589
25085
  };
24590
25086
  const modelControl = Object.freeze({
24591
- currentHostId: () => {
25087
+ currentHostId: (composer) => {
25088
+ if (composer) {
25089
+ return disposed ? null : window.__codexhostHostRoutingV1?.hostIdForComposer(composer) ?? null;
25090
+ }
24592
25091
  const route = currentRequestRoute();
24593
25092
  return disposed ? null : route?.hostId ?? window.__codexhostHostRoutingV1?.hostIdForComposer() ?? null;
24594
25093
  },
25094
+ knownHostIds: () => disposed ? [] : window.__codexhostHostRoutingV1?.knownHostIds?.() ?? [],
24595
25095
  clientForHost: (hostId) => disposed ? null : clients.forHost(hostId),
24596
25096
  listHarnessPlugins: async () => {
24597
25097
  const client = settingsModelClient();
@@ -24768,11 +25268,11 @@ Codex Fast mode prioritizes requests and may increase usage.`
24768
25268
  return null;
24769
25269
  }
24770
25270
  }
24771
- function isRecord10(value) {
25271
+ function isRecord11(value) {
24772
25272
  return typeof value === "object" && value !== null && !Array.isArray(value);
24773
25273
  }
24774
25274
  function parseExternalConfiguration(value) {
24775
- if (!isRecord10(value)) return void 0;
25275
+ if (!isRecord11(value)) return void 0;
24776
25276
  const model = harnessModelRefSchema.safeParse(value.model);
24777
25277
  if (!model.success) return void 0;
24778
25278
  const thinkingOptionId = harnessThinkingOptionIdSchema.safeParse(value.thinkingOptionId);
@@ -24789,9 +25289,9 @@ Codex Fast mode prioritizes requests and may increase usage.`
24789
25289
  const raw = storage.getItem(RENDERER_NEW_THREAD_PREFERENCE_KEY);
24790
25290
  if (!raw) return void 0;
24791
25291
  const parsed = JSON.parse(raw);
24792
- if (!isRecord10(parsed) || parsed.version !== 1) return void 0;
25292
+ if (!isRecord11(parsed) || parsed.version !== 1) return void 0;
24793
25293
  if (!KNOWN_RENDERER_AGENTS.some((agent) => agent === parsed.lastAgent)) return void 0;
24794
- const externalByAgent = isRecord10(parsed.externalByAgent) ? parsed.externalByAgent : {};
25294
+ const externalByAgent = isRecord11(parsed.externalByAgent) ? parsed.externalByAgent : {};
24795
25295
  const parsedExternal = Object.fromEntries(
24796
25296
  KNOWN_RENDERER_AGENTS.filter(
24797
25297
  (agent) => agent !== "codex"
@@ -24919,7 +25419,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24919
25419
  var GET_LOCALE_OVERRIDE_URL = "vscode://codex/get-setting";
24920
25420
  var GET_LOCALE_INFO_URL = "vscode://codex/locale-info";
24921
25421
  var DEFAULT_LOCALE_REQUEST_TIMEOUT_MS = 750;
24922
- function isRecord11(value) {
25422
+ function isRecord12(value) {
24923
25423
  return typeof value === "object" && value !== null && !Array.isArray(value);
24924
25424
  }
24925
25425
  function canonicalLocale(value) {
@@ -24941,7 +25441,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24941
25441
  return new DOMException("The operation was aborted", "AbortError");
24942
25442
  }
24943
25443
  function parseLocaleOverride(value) {
24944
- if (!isRecord11(value) || !("value" in value)) {
25444
+ if (!isRecord12(value) || !("value" in value)) {
24945
25445
  throw new Error("Codex locale override response is malformed");
24946
25446
  }
24947
25447
  if (value.value === null) return { value: null };
@@ -24956,7 +25456,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24956
25456
  return locale;
24957
25457
  }
24958
25458
  function parseLocaleInfo(value) {
24959
- if (!isRecord11(value)) throw new Error("Codex locale info response is malformed");
25459
+ if (!isRecord12(value)) throw new Error("Codex locale info response is malformed");
24960
25460
  return {
24961
25461
  ideLocale: optionalLocale(value.ideLocale, "IDE locale"),
24962
25462
  systemLocale: optionalLocale(value.systemLocale, "system locale")
@@ -24985,7 +25485,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
24985
25485
  const onAbort = () => settle(() => reject(abortError2()));
24986
25486
  const onMessage = (event) => {
24987
25487
  const message2 = event.data;
24988
- if (!isRecord11(message2) || message2.type !== "fetch-response") return;
25488
+ if (!isRecord12(message2) || message2.type !== "fetch-response") return;
24989
25489
  if (message2.requestId !== requestId) return;
24990
25490
  if (message2.responseType !== "success" || typeof message2.status !== "number" || message2.status < 200 || message2.status >= 300 || typeof message2.bodyJsonString !== "string") {
24991
25491
  settle(() => reject(new Error("Codex locale request failed")));
@@ -25094,6 +25594,1061 @@ Codex Fast mode prioritizes requests and may increase usage.`
25094
25594
  });
25095
25595
  }
25096
25596
 
25597
+ // src/settings/remote-failure-messages.ts
25598
+ var REMOTE_FAILURE_CHINESE = Object.freeze({
25599
+ // SSH helper: installing, updating and repairing over SSH.
25600
+ "Invalid SSH address or port": "SSH \u5730\u5740\u6216\u7AEF\u53E3\u65E0\u6548",
25601
+ "Install requires a published stable version": "\u53EA\u80FD\u5B89\u88C5\u5DF2\u53D1\u5E03\u7684\u6B63\u5F0F\u7248\u672C",
25602
+ "SSH operation timed out; check the remote computer before retrying": "SSH \u64CD\u4F5C\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5\u8FDC\u7A0B\u7535\u8111\u540E\u91CD\u8BD5",
25603
+ "Remote service is already installed. Connect and use its update button": "\u8FDC\u7A0B\u670D\u52A1\u5DF2\u7ECF\u5B89\u88C5\uFF0C\u8BF7\u8FDE\u63A5\u540E\u4F7F\u7528\u66F4\u65B0\u6309\u94AE",
25604
+ "Remote service is not installed. Use Install and connect instead": "\u8FDC\u7A0B\u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF0C\u8BF7\u4F7F\u7528\u300C\u5B89\u88C5\u5E76\u8FDE\u63A5\u300D",
25605
+ "This release is not published on npm, or the remote computer cannot reach the registry": "\u8BE5\u7248\u672C\u5C1A\u672A\u53D1\u5E03\u5230 npm\uFF0C\u6216\u8FDC\u7A0B\u7535\u8111\u65E0\u6CD5\u8BBF\u95EE npm \u4ED3\u5E93",
25606
+ "codexhost was installed but is not on the remote PATH. Update this remote manually": "codexhost \u5DF2\u5B89\u88C5\uFF0C\u4F46\u4E0D\u5728\u8FDC\u7A0B\u7535\u8111\u7684 PATH \u4E2D\uFF0C\u8BF7\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u624B\u52A8\u66F4\u65B0",
25607
+ "Only Mac and Linux remote computers are supported": "\u53EA\u652F\u6301 Mac \u548C Linux \u8FDC\u7A0B\u7535\u8111",
25608
+ "Node.js, npm or Codex CLI was not found over SSH. Install them on the remote computer and make sure non-interactive SSH sessions have them on PATH": "\u901A\u8FC7 SSH \u627E\u4E0D\u5230 Node.js\u3001npm \u6216 Codex CLI\u3002\u8BF7\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u5B89\u88C5\u5B83\u4EEC\uFF0C\u5E76\u786E\u4FDD\u975E\u4EA4\u4E92\u5F0F SSH \u4F1A\u8BDD\u7684 PATH \u4E2D\u5305\u542B\u5B83\u4EEC",
25609
+ "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",
25610
+ "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",
25611
+ "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",
25612
+ "Remote connection configuration could not be removed. Check file permissions": "\u65E0\u6CD5\u79FB\u9664\u8FDC\u7A0B\u8FDE\u63A5\u914D\u7F6E\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u6743\u9650",
25613
+ "Another remote installation is in progress. Wait and check again": "\u53E6\u4E00\u9879\u8FDC\u7A0B\u5B89\u88C5\u6B63\u5728\u8FDB\u884C\uFF0C\u8BF7\u7A0D\u540E\u91CD\u65B0\u68C0\u6D4B",
25614
+ "Remote service was installed but could not start. Check the remote service and retry connecting": "\u8FDC\u7A0B\u670D\u52A1\u5DF2\u5B89\u88C5\u4F46\u65E0\u6CD5\u542F\u52A8\uFF0C\u8BF7\u68C0\u67E5\u8FDC\u7A0B\u670D\u52A1\u540E\u91CD\u65B0\u8FDE\u63A5",
25615
+ "SSH failed. Check the address, key authentication and trusted host key": "SSH \u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u3001\u5BC6\u94A5\u8BA4\u8BC1\u4EE5\u53CA\u4E3B\u673A\u5BC6\u94A5\u662F\u5426\u5DF2\u4FE1\u4EFB",
25616
+ "Unexpected SSH inspection response": "SSH \u68C0\u6D4B\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u7ED3\u679C",
25617
+ "SSH installation failed; check the remote computer and retry": "SSH \u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u8FDC\u7A0B\u7535\u8111\u540E\u91CD\u8BD5",
25618
+ "SSH installation helper is unavailable. Update local codexhost and restart.": "SSH \u5B89\u88C5\u7EC4\u4EF6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u66F4\u65B0\u672C\u673A codexhost \u540E\u91CD\u542F",
25619
+ "SSH installation must run from the local computer": "SSH \u5B89\u88C5\u53EA\u80FD\u4ECE\u672C\u673A\u53D1\u8D77",
25620
+ "An SSH operation is already running for this connection": "\u8FD9\u4E2A\u8FDE\u63A5\u5DF2\u6709 SSH \u64CD\u4F5C\u6B63\u5728\u8FDB\u884C",
25621
+ // Remote service: updating or restarting itself.
25622
+ "Another codexhost update is active; finish it before retrying": "\u53E6\u4E00\u9879 codexhost \u66F4\u65B0\u6B63\u5728\u8FDB\u884C\uFF0C\u8BF7\u7B49\u5B83\u5B8C\u6210\u540E\u91CD\u8BD5",
25623
+ "The remote installation changed before the update acquired its lock; refresh and retry": "\u66F4\u65B0\u5F00\u59CB\u524D\u8FDC\u7A0B\u5B89\u88C5\u53D1\u751F\u4E86\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5",
25624
+ "Could not identify the global npm installation": "\u65E0\u6CD5\u8BC6\u522B\u8FDC\u7A0B\u7535\u8111\u7684\u5168\u5C40 npm \u5B89\u88C5",
25625
+ "npm uses a different global installation; update this remote manually": "\u8FDC\u7A0B\u7535\u8111\u7684 npm \u4F7F\u7528\u4E86\u53E6\u4E00\u5904\u5168\u5C40\u5B89\u88C5\uFF0C\u8BF7\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u624B\u52A8\u66F4\u65B0",
25626
+ "The requested release is unavailable or npm cannot reach the registry": "\u76EE\u6807\u7248\u672C\u4E0D\u5B58\u5728\uFF0C\u6216\u8FDC\u7A0B\u7535\u8111\u65E0\u6CD5\u8BBF\u95EE npm \u4ED3\u5E93",
25627
+ "npm installation failed; check remote network access and write permissions": "npm \u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u8FDC\u7A0B\u7535\u8111\u7684\u7F51\u7EDC\u548C\u5199\u5165\u6743\u9650",
25628
+ "Installed remote version does not match the requested version": "\u8FDC\u7A0B\u5B89\u88C5\u7684\u7248\u672C\u4E0E\u8BF7\u6C42\u7684\u7248\u672C\u4E0D\u4E00\u81F4",
25629
+ "Remote stop failed": "\u66F4\u65B0\u5DF2\u5B89\u88C5\uFF0C\u4F46\u505C\u6B62\u65E7\u670D\u52A1\u5931\u8D25",
25630
+ "Remote install failed": "\u66F4\u65B0\u5DF2\u5B89\u88C5\uFF0C\u4F46\u91CD\u65B0\u914D\u7F6E\u8FDC\u7A0B\u670D\u52A1\u5931\u8D25",
25631
+ "Remote start failed": "\u66F4\u65B0\u5DF2\u5B89\u88C5\uFF0C\u4F46\u542F\u52A8\u8FDC\u7A0B\u670D\u52A1\u5931\u8D25",
25632
+ "The previous remote update was interrupted; retry the update": "\u4E0A\u4E00\u6B21\u8FDC\u7A0B\u66F4\u65B0\u88AB\u4E2D\u65AD\uFF0C\u8BF7\u91CD\u8BD5",
25633
+ "Remote updater did not start": "\u8FDC\u7A0B\u66F4\u65B0\u7A0B\u5E8F\u672A\u80FD\u542F\u52A8",
25634
+ "Remote updater could not be started": "\u65E0\u6CD5\u542F\u52A8\u8FDC\u7A0B\u66F4\u65B0\u7A0B\u5E8F",
25635
+ "Remote updater is unavailable": "\u8FDC\u7A0B\u66F4\u65B0\u7A0B\u5E8F\u4E0D\u53EF\u7528",
25636
+ "Another remote update is already pending": "\u5DF2\u6709\u53E6\u4E00\u9879\u8FDC\u7A0B\u66F4\u65B0\u5728\u7B49\u5F85\u6267\u884C",
25637
+ "Remote update requires an npm installation with the current updater": "\u8FDC\u7A0B\u66F4\u65B0\u9700\u8981\u901A\u8FC7 npm \u5B89\u88C5\u5E76\u5E26\u6709\u5F53\u524D\u7248\u672C\u7684\u66F4\u65B0\u7A0B\u5E8F",
25638
+ "Remote downgrades are not automatic; update this computer first": "\u4E0D\u4F1A\u81EA\u52A8\u964D\u4F4E\u8FDC\u7A0B\u7248\u672C\uFF0C\u8BF7\u5148\u66F4\u65B0\u672C\u673A",
25639
+ "The remote installation changed; refresh versions before retrying": "\u8FDC\u7A0B\u5B89\u88C5\u53D1\u751F\u4E86\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u7248\u672C\u540E\u91CD\u8BD5",
25640
+ "Remote service is updating; reconnect shortly": "\u8FDC\u7A0B\u670D\u52A1\u6B63\u5728\u66F4\u65B0\uFF0C\u8BF7\u7A0D\u540E\u91CD\u65B0\u8FDE\u63A5",
25641
+ "Runtime version management is unavailable in this build": "\u5F53\u524D\u6784\u5EFA\u4E0D\u652F\u6301\u7248\u672C\u7BA1\u7406",
25642
+ // This window's bridge to the remote service.
25643
+ "Remote service did not respond; reconnect and retry": "\u8FDC\u7A0B\u670D\u52A1\u6CA1\u6709\u54CD\u5E94\uFF0C\u8BF7\u91CD\u65B0\u8FDE\u63A5\u540E\u91CD\u8BD5",
25644
+ "SSH installation is unavailable; update local codexhost and restart": "SSH \u5B89\u88C5\u4E0D\u53EF\u7528\uFF0C\u8BF7\u66F4\u65B0\u672C\u673A codexhost \u540E\u91CD\u542F",
25645
+ "Version management is unavailable; update codexhost on this host and reconnect": "\u7248\u672C\u7BA1\u7406\u4E0D\u53EF\u7528\uFF0C\u8BF7\u66F4\u65B0\u8FD9\u53F0\u7535\u8111\u4E0A\u7684 codexhost \u540E\u91CD\u65B0\u8FDE\u63A5",
25646
+ "Remote update is unavailable; update this remote once and reconnect": "\u8FDC\u7A0B\u66F4\u65B0\u4E0D\u53EF\u7528\uFF0C\u8BF7\u5148\u624B\u52A8\u66F4\u65B0\u4E00\u6B21\u8FD9\u53F0\u8FDC\u7A0B\u7535\u8111\u518D\u91CD\u65B0\u8FDE\u63A5",
25647
+ // Codex SSH bridge: reading and saving connections.
25648
+ "Codex SSH settings are unavailable in this window": "\u6B64\u7A97\u53E3\u65E0\u6CD5\u4F7F\u7528 Codex SSH \u8BBE\u7F6E",
25649
+ "Codex SSH request failed; check the native Connections settings": "Codex SSH \u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u539F\u751F\u7684\u8FDE\u63A5\u8BBE\u7F6E",
25650
+ "Invalid Codex SSH response": "Codex SSH \u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u7ED3\u679C",
25651
+ "Codex SSH request timed out; refresh before retrying": "Codex SSH \u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5",
25652
+ "Invalid Codex SSH connection list": "Codex \u8FD4\u56DE\u7684 SSH \u8FDE\u63A5\u5217\u8868\u65E0\u6CD5\u8BC6\u522B",
25653
+ "This Codex version uses an unsupported SSH configuration; open native settings": "\u5F53\u524D Codex \u7248\u672C\u7684 SSH \u914D\u7F6E\u683C\u5F0F\u4E0D\u53D7\u652F\u6301\uFF0C\u8BF7\u5728\u539F\u751F\u8BBE\u7F6E\u4E2D\u7BA1\u7406",
25654
+ "This connection changed in Codex. Refresh and edit it again.": "\u8FD9\u4E2A\u8FDE\u63A5\u5DF2\u5728 Codex \u4E2D\u88AB\u4FEE\u6539\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u65B0\u7F16\u8F91",
25655
+ "Enter a name, SSH hostname (or alias), and a port between 1 and 65535": "\u8BF7\u586B\u5199\u540D\u79F0\u3001SSH \u4E3B\u673A\u540D\uFF08\u6216\u522B\u540D\uFF09\uFF0C\u7AEF\u53E3\u9700\u5728 1 \u5230 65535 \u4E4B\u95F4",
25656
+ "A connection with this name already exists": "\u5DF2\u5B58\u5728\u540C\u540D\u7684\u8FDE\u63A5",
25657
+ "Edit SSH-config connection details in native Codex settings": "\u6765\u81EA SSH \u914D\u7F6E\u7684\u8FDE\u63A5\uFF0C\u8BF7\u5728 Codex \u539F\u751F\u8BBE\u7F6E\u4E2D\u4FEE\u6539\u8BE6\u7EC6\u4FE1\u606F",
25658
+ "Invalid Codex connection state": "Codex \u8FD4\u56DE\u7684\u8FDE\u63A5\u72B6\u6001\u65E0\u6CD5\u8BC6\u522B"
25659
+ });
25660
+ function localizeRemoteFailure(message, t) {
25661
+ const text = message.replace(/^Error:\s*/u, "");
25662
+ const detail = text.indexOf(": ");
25663
+ const chinese = REMOTE_FAILURE_CHINESE[text] ?? (detail > 0 ? REMOTE_FAILURE_CHINESE[text.slice(0, detail)] : void 0);
25664
+ return chinese ? t(chinese, text) : text;
25665
+ }
25666
+
25667
+ // src/settings/remote-connections-card.ts
25668
+ var STABLE_VERSION = /^\d+\.\d+\.\d+$/u;
25669
+ function remoteErrorMessage(error51) {
25670
+ return error51 instanceof Error && error51.message ? error51.message : String(error51);
25671
+ }
25672
+ function remoteVersionIssue(error51) {
25673
+ const outdated = typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === -32601;
25674
+ return { outdated, message: remoteErrorMessage(error51) };
25675
+ }
25676
+ function remoteElement(document2, tag, className = "", text = "") {
25677
+ const result = document2.createElement(tag);
25678
+ if (className) result.className = className;
25679
+ if (text) result.textContent = text;
25680
+ return result;
25681
+ }
25682
+ function remoteInstallVersion(local) {
25683
+ const version2 = local?.runningVersion;
25684
+ return version2 && STABLE_VERSION.test(version2) ? version2 : null;
25685
+ }
25686
+ function remoteMaintenanceTarget(local, remote) {
25687
+ if (!remote.updateSupported) return null;
25688
+ return newerLocalVersion(local, remote) ?? (remote.restartRequired ? remote.installedVersion : null);
25689
+ }
25690
+ function newerLocalVersion(local, remote) {
25691
+ return remoteUpdateTarget(local, { ...remote, update: { ...remote.update, phase: "idle" } });
25692
+ }
25693
+ function remoteBusy(remote) {
25694
+ return !!remote && ["installing", "restarting"].includes(remote.update.phase);
25695
+ }
25696
+ function remoteTroubled(row) {
25697
+ if (row.remote) return row.remote.update.phase === "failed";
25698
+ if (row.state === "connecting") return false;
25699
+ return !(row.state === "disconnected" && !row.connection.autoConnect);
25700
+ }
25701
+ function connectionState(row, t) {
25702
+ switch (row.state) {
25703
+ case null:
25704
+ return ["progress", t("\u68C0\u6D4B\u4E2D\u2026", "Checking\u2026")];
25705
+ case "connected":
25706
+ return ["success", t("\u5DF2\u8FDE\u63A5", "Connected")];
25707
+ case "connecting":
25708
+ return ["progress", t("\u8FDE\u63A5\u4E2D\u2026", "Connecting\u2026")];
25709
+ case "disconnected":
25710
+ return ["neutral", t("\u672A\u8FDE\u63A5", "Disconnected")];
25711
+ case "error":
25712
+ return ["danger", t("\u8FDE\u63A5\u5931\u8D25", "Connection failed")];
25713
+ default:
25714
+ return ["neutral", t("\u72B6\u6001\u672A\u77E5", "Unknown state")];
25715
+ }
25716
+ }
25717
+ function serviceSummary(row, local, t) {
25718
+ const { remote } = row;
25719
+ const hints = [];
25720
+ if (!remote) {
25721
+ if (row.probing) return { title: t("\u6B63\u5728\u68C0\u6D4B\u8FDC\u7A0B\u670D\u52A1\u2026", "Checking remote service\u2026"), hints };
25722
+ if (row.installation === "not-installed") {
25723
+ const version2 = remoteInstallVersion(local);
25724
+ hints.push(
25725
+ version2 ? [
25726
+ "neutral",
25727
+ t(
25728
+ `\u5C06\u5B89\u88C5\u4E0E\u672C\u673A\u76F8\u540C\u7684\u7248\u672C ${version2}`,
25729
+ `Installs version ${version2}, matching this computer`
25730
+ )
25731
+ ] : [
25732
+ "warning",
25733
+ t(
25734
+ "\u672C\u673A\u7248\u672C\u4E0D\u53EF\u7528\u4E8E\u5B89\u88C5\uFF0C\u8BF7\u4F7F\u7528\u6B63\u5F0F\u53D1\u5E03\u7248\u672C\u3002",
25735
+ "Install from a published local release."
25736
+ )
25737
+ ]
25738
+ );
25739
+ return { title: t("\u9700\u8981\u5B89\u88C5\u8FDC\u7A0B\u670D\u52A1", "Remote service needs installation"), hints };
25740
+ }
25741
+ if (row.remoteIssue?.outdated) {
25742
+ const version2 = remoteInstallVersion(local);
25743
+ hints.push(
25744
+ version2 ? [
25745
+ "neutral",
25746
+ t(
25747
+ `\u66F4\u65B0\u5230\u672C\u673A\u7248\u672C ${version2} \u540E\u53EF\u5728\u8FD9\u91CC\u67E5\u770B\u548C\u7BA1\u7406\u7248\u672C\u3002`,
25748
+ `Update to this computer's version ${version2} to manage versions here.`
25749
+ )
25750
+ ] : [
25751
+ "warning",
25752
+ t(
25753
+ "\u672C\u673A\u4E0D\u662F\u6B63\u5F0F\u53D1\u5E03\u7248\u672C\uFF0C\u65E0\u6CD5\u4ECE\u8FD9\u91CC\u66F4\u65B0\uFF1B\u8BF7\u5728\u8FDC\u7A0B\u7535\u8111\u4E0A\u624B\u52A8\u66F4\u65B0 codexhost\u3002",
25754
+ "This computer is not running a published release, so it cannot update the remote. Update codexhost on the remote computer manually."
25755
+ )
25756
+ ]
25757
+ );
25758
+ return { title: t("\u8FDC\u7A0B\u670D\u52A1\u9700\u8981\u66F4\u65B0", "Remote service needs an update"), hints };
25759
+ }
25760
+ hints.push([
25761
+ "neutral",
25762
+ row.state === "connecting" || row.state === "disconnected" && !row.connection.autoConnect ? t("\u8FDE\u63A5\u540E\u663E\u793A\u8FDC\u7A0B\u670D\u52A1\u7248\u672C\u3002", "Connect to see the remote service version.") : t(
25763
+ "\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6\u8FDC\u7A0B\u7248\u672C\uFF0C\u53EF\u5237\u65B0\u6216\u91CD\u65B0\u8FDE\u63A5\u540E\u518D\u8BD5\u3002",
25764
+ "Remote version information is temporarily unavailable. Refresh or reconnect to retry."
25765
+ )
25766
+ ]);
25767
+ if (row.remoteIssue)
25768
+ hints.push([
25769
+ "danger",
25770
+ `${t("\u539F\u56E0", "Reason")}: ${localizeRemoteFailure(row.remoteIssue.message, t)}`
25771
+ ]);
25772
+ return { title: t("\u8FDC\u7A0B\u670D\u52A1", "Remote service"), hints };
25773
+ }
25774
+ const busy = remoteBusy(remote);
25775
+ const installed = (busy ? remote.runningVersion : remote.installedVersion) ?? remote.runningVersion ?? "\u2014";
25776
+ const title = !busy && remote.runningVersion && remote.runningVersion !== remote.installedVersion ? t(
25777
+ `\u8FDC\u7A0B\u670D\u52A1 ${installed} \xB7 \u8FD0\u884C\u4E2D ${remote.runningVersion}`,
25778
+ `Remote service ${installed} \xB7 running ${remote.runningVersion}`
25779
+ ) : t(`\u8FDC\u7A0B\u670D\u52A1 ${installed}`, `Remote service ${installed}`);
25780
+ const phases = {
25781
+ idle: null,
25782
+ installing: ["progress", t("\u6B63\u5728\u66F4\u65B0\u8FDC\u7A0B\u670D\u52A1\u2026", "Updating remote service\u2026")],
25783
+ restarting: ["progress", t("\u6B63\u5728\u91CD\u542F\uFF0C\u8BF7\u7A0D\u5019\u91CD\u8FDE\u2026", "Restarting; reconnecting shortly\u2026")],
25784
+ // The record outlives the update; the version line already shows the result.
25785
+ succeeded: null,
25786
+ failed: ["danger", t("\u66F4\u65B0\u5931\u8D25\uFF0C\u53EF\u4EE5\u91CD\u8BD5", "Update failed; you can retry")]
25787
+ };
25788
+ const phase = phases[remote.update.phase];
25789
+ if (phase) hints.push(phase);
25790
+ if (remote.update.error) hints.push(["danger", localizeRemoteFailure(remote.update.error, t)]);
25791
+ if (remote.restartRequired && !busy)
25792
+ hints.push([
25793
+ "warning",
25794
+ t("\u9700\u8981\u91CD\u542F\u4EE5\u4F7F\u7528\u5DF2\u5B89\u88C5\u7684\u7248\u672C", "Restart needed to use the installed version")
25795
+ ]);
25796
+ const newer = newerLocalVersion(local, remote);
25797
+ const localVersion = local?.runningVersion;
25798
+ if (remoteBusy(remote)) {
25799
+ } else if (newer && remote.installedVersion !== localVersion)
25800
+ hints.push([
25801
+ "warning",
25802
+ t(`\u6709\u66F4\u65B0\u53EF\u7528 \xB7 \u672C\u673A\u4E3A ${newer}`, `Update available \xB7 this computer runs ${newer}`)
25803
+ ]);
25804
+ else if (localVersion && remote.installedVersion === localVersion && !remote.restartRequired)
25805
+ hints.push(["success", t("\u4E0E\u672C\u673A\u7248\u672C\u4E00\u81F4", "Matches this computer")]);
25806
+ if (localVersion && remote.installedVersion && localVersion !== remote.installedVersion && !newer)
25807
+ hints.push([
25808
+ "warning",
25809
+ t(
25810
+ "\u7248\u672C\u4E0D\u540C\uFF0C\u8BF7\u5148\u68C0\u67E5\u672C\u673A\u66F4\u65B0\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u964D\u4F4E\u8FDC\u7A0B\u7248\u672C\u3002",
25811
+ "Versions differ. Check for a local update; the remote version will not be downgraded."
25812
+ )
25813
+ ]);
25814
+ return { title, hints };
25815
+ }
25816
+ function renderRemoteCard(document2, row, local, t, onAction) {
25817
+ const { connection, remote } = row;
25818
+ const locked = row.pending !== null;
25819
+ const command = (action, label, primary = false) => {
25820
+ const button = remoteElement(
25821
+ document2,
25822
+ "button",
25823
+ primary ? "settings-command-button" : "settings-command-button settings-command-button--secondary",
25824
+ label
25825
+ );
25826
+ button.type = "button";
25827
+ button.dataset.action = action;
25828
+ button.disabled = locked;
25829
+ button.addEventListener("click", () => onAction(action));
25830
+ return button;
25831
+ };
25832
+ const card = remoteElement(document2, "section", "settings-remote-card");
25833
+ card.dataset.hostId = connection.hostId;
25834
+ card.setAttribute("aria-label", connection.displayName);
25835
+ card.setAttribute("aria-busy", String(locked));
25836
+ const head = remoteElement(document2, "div", "settings-remote-card__head");
25837
+ const identity = remoteElement(document2, "div", "settings-remote-card__identity");
25838
+ const titleLine = remoteElement(document2, "div", "settings-remote-card__title");
25839
+ const [tone, stateLabel] = connectionState(row, t);
25840
+ const state = remoteElement(document2, "span", "settings-remote-card__state", stateLabel);
25841
+ state.dataset.tone = tone;
25842
+ titleLine.append(remoteElement(document2, "h3", "", connection.displayName), state);
25843
+ const address = `${connection.sshAlias ?? connection.sshHost}${connection.sshPort ? `:${connection.sshPort}` : ""}`;
25844
+ const addressLine = remoteElement(document2, "div", "settings-remote-card__address", address);
25845
+ addressLine.title = address;
25846
+ if (connection.source === "discovered")
25847
+ addressLine.append(
25848
+ remoteElement(document2, "span", "settings-status-badge", t("SSH \u914D\u7F6E", "SSH config"))
25849
+ );
25850
+ identity.append(titleLine, addressLine);
25851
+ const headActions = remoteElement(document2, "div", "settings-remote-card__actions");
25852
+ if (row.installation !== "not-installed" && !row.probing) {
25853
+ if (row.state === "error" && connection.autoConnect)
25854
+ headActions.append(command("reconnect", t("\u91CD\u65B0\u8FDE\u63A5", "Reconnect")));
25855
+ headActions.append(
25856
+ command("toggle", connection.autoConnect ? t("\u65AD\u5F00", "Disconnect") : t("\u8FDE\u63A5", "Connect"))
25857
+ );
25858
+ }
25859
+ headActions.append(command("edit", t("\u7F16\u8F91", "Edit")));
25860
+ const remove = remoteElement(
25861
+ document2,
25862
+ "button",
25863
+ "settings-icon-button settings-remote-card__remove"
25864
+ );
25865
+ remove.type = "button";
25866
+ remove.dataset.action = "remove";
25867
+ remove.disabled = locked;
25868
+ remove.title = t("\u79FB\u9664", "Remove");
25869
+ remove.setAttribute("aria-label", t("\u79FB\u9664", "Remove"));
25870
+ remove.append(createRendererSettingsIcon("trash", 15));
25871
+ remove.addEventListener("click", () => onAction("remove"));
25872
+ headActions.append(remove);
25873
+ head.append(identity, headActions);
25874
+ card.append(head);
25875
+ if (row.stateError) {
25876
+ const detail = remoteElement(
25877
+ document2,
25878
+ "p",
25879
+ "settings-remote-card__detail",
25880
+ localizeRemoteFailure(row.stateError, t)
25881
+ );
25882
+ detail.dataset.tone = "danger";
25883
+ card.append(detail);
25884
+ }
25885
+ const service = remoteElement(document2, "div", "settings-remote-card__service");
25886
+ const copy = remoteElement(document2, "div", "settings-remote-card__service-copy");
25887
+ const hint = (hintTone, text, live = false) => {
25888
+ const line = remoteElement(document2, "p", "settings-remote-card__hint");
25889
+ line.dataset.tone = hintTone;
25890
+ if (hintTone === "progress")
25891
+ line.append(remoteElement(document2, "span", "settings-remote-spinner"));
25892
+ line.append(text);
25893
+ if (live) line.setAttribute("role", hintTone === "danger" ? "alert" : "status");
25894
+ return line;
25895
+ };
25896
+ const summary = serviceSummary(row, local, t);
25897
+ copy.append(remoteElement(document2, "div", "settings-remote-card__service-title", summary.title));
25898
+ if (row.pending) copy.append(hint("progress", row.pending, true));
25899
+ else {
25900
+ for (const [hintTone, text] of summary.hints) copy.append(hint(hintTone, text));
25901
+ if (row.notice)
25902
+ copy.append(hint(row.notice.tone, localizeRemoteFailure(row.notice.text, t), true));
25903
+ }
25904
+ const serviceActions = remoteElement(document2, "div", "settings-remote-card__actions");
25905
+ if (!row.probing) {
25906
+ const outdated = !remote && !!row.remoteIssue?.outdated && row.installation !== "not-installed";
25907
+ if (outdated) {
25908
+ const upgrade = command("upgrade", t("\u66F4\u65B0\u8FDC\u7A0B\u670D\u52A1", "Update remote service"), true);
25909
+ upgrade.disabled ||= !remoteInstallVersion(local);
25910
+ serviceActions.append(upgrade);
25911
+ } else if (remoteTroubled(row)) {
25912
+ if (row.installation !== "not-installed") {
25913
+ const repair = command("repair", t("\u4FEE\u590D\u8FDC\u7A0B\u670D\u52A1", "Repair remote service"));
25914
+ repair.title = t(
25915
+ "\u505C\u6B62\u5E76\u91CD\u65B0\u914D\u7F6E\u8FDC\u7A0B\u670D\u52A1\uFF0C\u4E0D\u4F1A\u66F4\u65B0\u7248\u672C\u3002",
25916
+ "Stops and reconfigures the remote service. It does not change the version."
25917
+ );
25918
+ serviceActions.append(repair);
25919
+ }
25920
+ if (!remote) serviceActions.append(command("recheck", t("\u91CD\u65B0\u68C0\u6D4B", "Check again")));
25921
+ }
25922
+ const target = remote ? remoteMaintenanceTarget(local, remote) : null;
25923
+ if (row.installation === "not-installed") {
25924
+ const install2 = command("install", t("\u5B89\u88C5\u5E76\u8FDE\u63A5", "Install and connect"), true);
25925
+ install2.disabled ||= !remoteInstallVersion(local);
25926
+ serviceActions.append(install2);
25927
+ } else if (remote && target && !remoteBusy(remote)) {
25928
+ const update = command(
25929
+ "update",
25930
+ target === remote.installedVersion ? t("\u91CD\u542F\u5E76\u8FDE\u63A5", "Restart and connect") : t("\u66F4\u65B0\u5230\u672C\u673A\u7248\u672C", "Match local version"),
25931
+ true
25932
+ );
25933
+ serviceActions.append(update);
25934
+ }
25935
+ }
25936
+ service.append(copy, serviceActions);
25937
+ card.append(service);
25938
+ return card;
25939
+ }
25940
+
25941
+ // src/settings/preference-ui.ts
25942
+ var nextPreferenceId = 0;
25943
+ function preferenceId(prefix) {
25944
+ nextPreferenceId += 1;
25945
+ return `codexhost-${prefix}-${nextPreferenceId}`;
25946
+ }
25947
+ function createPreferenceGroup(document2, title) {
25948
+ const group = document2.createElement("section");
25949
+ group.className = "mt-7 first-of-type:mt-0";
25950
+ const header = document2.createElement("div");
25951
+ header.className = "mb-2 flex min-h-6 items-center gap-2 px-1";
25952
+ const heading = document2.createElement("h3");
25953
+ heading.id = preferenceId("preference-group");
25954
+ heading.className = "m-0 text-[13px] leading-5 font-semibold text-settings-text";
25955
+ heading.textContent = title;
25956
+ header.append(heading);
25957
+ const card = document2.createElement("div");
25958
+ card.className = "rounded-[10px] border border-settings-border bg-settings-panel";
25959
+ group.setAttribute("aria-labelledby", heading.id);
25960
+ group.append(header, card);
25961
+ return { group, header, card };
25962
+ }
25963
+ function createPreferenceItem(document2, input) {
25964
+ const item = document2.createElement("div");
25965
+ item.className = "flex min-h-[60px] items-center justify-between gap-6 border-t border-settings-divider px-4 py-3 first:border-t-0";
25966
+ const copy = document2.createElement("div");
25967
+ copy.className = "flex min-w-0 flex-col gap-0.5";
25968
+ const heading = document2.createElement("div");
25969
+ heading.className = "flex items-center gap-1.5";
25970
+ const title = document2.createElement("label");
25971
+ title.htmlFor = input.controlId;
25972
+ title.className = "cursor-pointer text-[13px] leading-5 font-medium text-settings-text";
25973
+ title.textContent = input.title;
25974
+ heading.append(title);
25975
+ if (input.help) heading.append(createHelpTooltip(document2, input.help.label, input.help.lines));
25976
+ const description = document2.createElement("div");
25977
+ description.id = preferenceId("preference-description");
25978
+ description.className = "text-xs leading-[18px] text-settings-muted data-[invalid=true]:text-settings-danger";
25979
+ description.textContent = input.description;
25980
+ copy.append(heading, description);
25981
+ item.append(copy);
25982
+ return { item, description };
25983
+ }
25984
+ function createHelpTooltip(document2, label, lines) {
25985
+ const wrapper = document2.createElement("span");
25986
+ wrapper.className = "group relative inline-flex";
25987
+ const button = document2.createElement("button");
25988
+ button.type = "button";
25989
+ button.className = "inline-flex size-4 cursor-help items-center justify-center rounded-full border-0 bg-transparent p-0 text-settings-subtle transition-colors hover:text-settings-text focus-visible:text-settings-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-settings-focus";
25990
+ button.setAttribute("aria-label", label);
25991
+ button.append(createRendererSettingsIcon("help", 14));
25992
+ const tooltip = document2.createElement("div");
25993
+ tooltip.id = preferenceId("preference-tooltip");
25994
+ tooltip.setAttribute("role", "tooltip");
25995
+ tooltip.className = "pointer-events-none invisible absolute top-full left-0 z-20 mt-2 w-80 rounded-lg border border-settings-border bg-settings-panel px-3 py-2.5 text-xs leading-[18px] font-normal text-settings-text opacity-0 shadow-[0_10px_30px_rgb(0_0_0/0.22)] transition-opacity duration-150 group-hover:visible group-hover:opacity-100 group-focus-within:visible group-focus-within:opacity-100";
25996
+ const list = document2.createElement("ul");
25997
+ list.className = "m-0 grid list-disc gap-1 pl-4 marker:text-settings-subtle";
25998
+ for (const line of lines) {
25999
+ const item = document2.createElement("li");
26000
+ item.textContent = line;
26001
+ list.append(item);
26002
+ }
26003
+ tooltip.append(list);
26004
+ button.setAttribute("aria-describedby", tooltip.id);
26005
+ button.addEventListener("keydown", (event) => {
26006
+ if (event.key === "Escape") button.blur();
26007
+ });
26008
+ wrapper.append(button, tooltip);
26009
+ return wrapper;
26010
+ }
26011
+ function createPreferenceSwitch(document2, id, describedBy) {
26012
+ const input = document2.createElement("input");
26013
+ input.type = "checkbox";
26014
+ input.id = id;
26015
+ input.setAttribute("role", "switch");
26016
+ input.setAttribute("aria-describedby", describedBy);
26017
+ input.className = "relative m-0 h-5 w-9 shrink-0 cursor-pointer appearance-none rounded-full bg-settings-surface-hover ring-1 ring-settings-border transition-colors ring-inset before:absolute before:top-0.5 before:left-0.5 before:size-4 before:rounded-full before:bg-white before:shadow-[0_1px_2px_rgb(0_0_0/0.3)] before:transition-transform checked:bg-settings-focus checked:ring-transparent checked:before:translate-x-4 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-settings-focus";
26018
+ return input;
26019
+ }
26020
+ function createNumberField(document2, input) {
26021
+ const field = document2.createElement("div");
26022
+ field.className = "flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-settings-border bg-settings-surface px-2.5 transition-colors focus-within:border-settings-focus data-[invalid=true]:border-settings-danger";
26023
+ const control = document2.createElement("input");
26024
+ control.type = "number";
26025
+ control.id = input.id;
26026
+ control.min = String(input.min);
26027
+ control.max = String(input.max);
26028
+ control.step = "1";
26029
+ control.setAttribute("aria-describedby", input.describedBy);
26030
+ control.className = "w-14 border-0 bg-transparent p-0 text-right text-[13px] leading-5 text-settings-text tabular-nums outline-none";
26031
+ const unit = document2.createElement("span");
26032
+ unit.className = "text-xs text-settings-muted";
26033
+ unit.setAttribute("aria-hidden", "true");
26034
+ unit.textContent = input.unit;
26035
+ field.append(control, unit);
26036
+ return { field, control };
26037
+ }
26038
+
26039
+ // src/settings/remote-connections-dialogs.ts
26040
+ function createRemoteDialog({ container, signal, t }, title) {
26041
+ if (signal.aborted || container.querySelector("dialog")) return null;
26042
+ const document2 = container.ownerDocument;
26043
+ const modal = remoteElement(
26044
+ document2,
26045
+ "dialog",
26046
+ "settings-account-dialog settings-credential-dialog settings-remote-dialog"
26047
+ );
26048
+ const form = remoteElement(document2, "form");
26049
+ const heading = remoteElement(document2, "h2", "", title);
26050
+ heading.id = preferenceId("remote-dialog-title");
26051
+ modal.setAttribute("aria-labelledby", heading.id);
26052
+ const body = remoteElement(document2, "div", "settings-credential-dialog__body");
26053
+ const error51 = remoteElement(document2, "p", "settings-credential-dialog__error");
26054
+ error51.setAttribute("role", "alert");
26055
+ const actions = remoteElement(document2, "div", "settings-credential-dialog__actions");
26056
+ const cancel = remoteElement(
26057
+ document2,
26058
+ "button",
26059
+ "settings-command-button settings-command-button--secondary",
26060
+ t("\u53D6\u6D88", "Cancel")
26061
+ );
26062
+ cancel.type = "button";
26063
+ const confirm = remoteElement(document2, "button", "settings-command-button");
26064
+ confirm.type = "submit";
26065
+ actions.append(cancel, confirm);
26066
+ form.append(heading, body, error51, actions);
26067
+ modal.append(form);
26068
+ let busy = false;
26069
+ let labels = { idle: "", busy: "" };
26070
+ const setBusy = (next) => {
26071
+ busy = next;
26072
+ cancel.disabled = confirm.disabled = next;
26073
+ confirm.textContent = next ? labels.busy : labels.idle;
26074
+ modal.setAttribute("aria-busy", String(next));
26075
+ };
26076
+ const dispose = () => modal.remove();
26077
+ cancel.addEventListener("click", () => modal.close());
26078
+ modal.addEventListener("cancel", (event) => {
26079
+ if (busy) event.preventDefault();
26080
+ });
26081
+ modal.addEventListener("close", () => {
26082
+ signal.removeEventListener("abort", dispose);
26083
+ dispose();
26084
+ });
26085
+ signal.addEventListener("abort", dispose, { once: true });
26086
+ return {
26087
+ form,
26088
+ body,
26089
+ submit(operation) {
26090
+ if (busy) return;
26091
+ error51.textContent = "";
26092
+ setBusy(true);
26093
+ operation().then(
26094
+ () => modal.close(),
26095
+ (failure) => {
26096
+ if (signal.aborted) return;
26097
+ setBusy(false);
26098
+ error51.textContent = localizeRemoteFailure(remoteErrorMessage(failure), t);
26099
+ }
26100
+ );
26101
+ },
26102
+ show(confirmLabel, busyLabel, danger = false) {
26103
+ labels = { idle: confirmLabel, busy: busyLabel };
26104
+ confirm.textContent = confirmLabel;
26105
+ if (danger)
26106
+ confirm.className = "settings-command-button settings-command-button--secondary settings-command-button--danger";
26107
+ container.append(modal);
26108
+ modal.showModal();
26109
+ }
26110
+ };
26111
+ }
26112
+ function openRemoteConnectionEditor(options) {
26113
+ const { t, previous } = options;
26114
+ const dialog = createRemoteDialog(
26115
+ options,
26116
+ previous ? t("\u7F16\u8F91\u8FDE\u63A5", "Edit connection") : t("\u6DFB\u52A0 SSH \u8FDE\u63A5", "Add SSH connection")
26117
+ );
26118
+ if (!dialog) return;
26119
+ const document2 = options.container.ownerDocument;
26120
+ const field = (label, value, placeholder = "", hint = "") => {
26121
+ const wrapper = remoteElement(document2, "div", "settings-credential-dialog__field");
26122
+ const input = remoteElement(document2, "input");
26123
+ input.id = preferenceId("remote-field");
26124
+ input.value = value;
26125
+ input.placeholder = placeholder;
26126
+ input.autocomplete = "off";
26127
+ input.spellcheck = false;
26128
+ const caption = remoteElement(document2, "label", "", label);
26129
+ caption.htmlFor = input.id;
26130
+ wrapper.append(caption, input);
26131
+ if (hint) {
26132
+ const description = remoteElement(document2, "small", "", hint);
26133
+ description.id = `${input.id}-hint`;
26134
+ input.setAttribute("aria-describedby", description.id);
26135
+ wrapper.append(description);
26136
+ }
26137
+ dialog.body.append(wrapper);
26138
+ return input;
26139
+ };
26140
+ const name = field(t("\u540D\u79F0", "Name"), previous?.displayName ?? "");
26141
+ name.required = true;
26142
+ const addressHint = t(
26143
+ "user@hostname\uFF0C\u6216 ~/.ssh/config \u4E2D\u7684\u4E3B\u673A\u522B\u540D",
26144
+ "user@hostname, or a host alias from ~/.ssh/config"
26145
+ );
26146
+ const hostname3 = field(
26147
+ t("SSH \u5730\u5740", "SSH address"),
26148
+ previous?.sshHost ?? "",
26149
+ "user@hostname",
26150
+ addressHint
26151
+ );
26152
+ hostname3.required = true;
26153
+ hostname3.pattern = "[^\\s\\-]\\S*";
26154
+ hostname3.title = addressHint;
26155
+ const port = field(
26156
+ t("\u7AEF\u53E3\uFF08\u53EF\u9009\uFF09", "Port (optional)"),
26157
+ previous?.sshPort?.toString() ?? "",
26158
+ "22"
26159
+ );
26160
+ port.type = "number";
26161
+ port.min = "1";
26162
+ port.max = "65535";
26163
+ port.step = "1";
26164
+ const identity = field(
26165
+ t("\u79C1\u94A5\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09", "Identity file (optional)"),
26166
+ previous?.identity ?? "",
26167
+ "~/.ssh/id_ed25519"
26168
+ );
26169
+ if (previous?.source === "discovered") {
26170
+ hostname3.disabled = port.disabled = identity.disabled = true;
26171
+ dialog.body.append(
26172
+ remoteElement(
26173
+ document2,
26174
+ "p",
26175
+ "",
26176
+ t(
26177
+ "\u6B64\u8FDE\u63A5\u6765\u81EA SSH \u914D\u7F6E\uFF0C\u8FD9\u91CC\u53EA\u80FD\u4FEE\u6539\u540D\u79F0\uFF1B\u5176\u4F59\u914D\u7F6E\u8BF7\u5728\u539F\u751F\u8BBE\u7F6E\u4E2D\u7F16\u8F91\u3002",
26178
+ "This connection comes from SSH config, so only its name can change here. Edit other details in native settings."
26179
+ )
26180
+ )
26181
+ );
26182
+ }
26183
+ dialog.form.addEventListener("submit", (event) => {
26184
+ event.preventDefault();
26185
+ dialog.submit(
26186
+ () => options.save({
26187
+ displayName: name.value,
26188
+ hostname: hostname3.value,
26189
+ sshPort: port.value ? Number(port.value) : null,
26190
+ identity: identity.value.trim() || null
26191
+ })
26192
+ );
26193
+ });
26194
+ dialog.show(t("\u4FDD\u5B58", "Save"), t("\u4FDD\u5B58\u4E2D\u2026", "Saving\u2026"));
26195
+ name.focus();
26196
+ name.select();
26197
+ }
26198
+ function openRemoteConnectionRemoval(options) {
26199
+ const { t, connection } = options;
26200
+ const dialog = createRemoteDialog(
26201
+ options,
26202
+ t(`\u79FB\u9664\u201C${connection.displayName}\u201D\uFF1F`, `Remove "${connection.displayName}"?`)
26203
+ );
26204
+ if (!dialog) return;
26205
+ dialog.body.append(
26206
+ remoteElement(
26207
+ options.container.ownerDocument,
26208
+ "p",
26209
+ "",
26210
+ t(
26211
+ "\u79FB\u9664\u8FDE\u63A5\u4E0D\u4F1A\u5220\u9664\u8FDC\u7A0B\u6587\u4EF6\u6216\u4F1A\u8BDD\u3002",
26212
+ "Removing a connection does not delete remote files or chats."
26213
+ )
26214
+ )
26215
+ );
26216
+ dialog.form.addEventListener("submit", (event) => {
26217
+ event.preventDefault();
26218
+ dialog.submit(options.remove);
26219
+ });
26220
+ dialog.show(t("\u79FB\u9664", "Remove"), t("\u79FB\u9664\u4E2D\u2026", "Removing\u2026"), true);
26221
+ }
26222
+
26223
+ // src/settings/remote-connections-page.ts
26224
+ var POLL_INTERVAL_MS = 5e3;
26225
+ var SUCCESS_NOTICE_MS = 6e3;
26226
+ var INSPECT_RETRY_MS = 6e4;
26227
+ var RESTART_GRACE_MS = 9e4;
26228
+ var CONNECT_REQUEST_WAIT_MS = 3e3;
26229
+ function createRemoteConnectionsPage(messages, getControl) {
26230
+ const zh = messages.locale === "zh-CN";
26231
+ const t = (cn, en) => zh ? cn : en;
26232
+ return Object.freeze({
26233
+ id: "remote-connections",
26234
+ label: messages.pageLabels["remote-connections"],
26235
+ icon: "gateway",
26236
+ mount({ content, signal }) {
26237
+ const document2 = content.ownerDocument;
26238
+ const el = (tag, className = "", text = "") => remoteElement(document2, tag, className, text);
26239
+ const emptyPanel = (title, detail) => {
26240
+ const panel = el("div", "settings-remote__empty");
26241
+ const copy = el("div");
26242
+ copy.append(el("strong", "", title), el("span", "", detail));
26243
+ panel.append(copy);
26244
+ return panel;
26245
+ };
26246
+ const root = el("div", "settings-remote");
26247
+ const header = el("div", "settings-remote__header");
26248
+ const headerCopy = el("div");
26249
+ const localVersion = el("p", "settings-remote__meta");
26250
+ headerCopy.append(
26251
+ el("h1", "settings-section-label", messages.pageLabels["remote-connections"]),
26252
+ el(
26253
+ "p",
26254
+ "settings-page-description",
26255
+ t(
26256
+ "\u901A\u8FC7 SSH \u8FDE\u63A5\u4F60\u7684 Mac \u6216 Linux \u7535\u8111\u3002\u8FDE\u63A5\u914D\u7F6E\u4E0E Codex \u5171\u7528\u3002",
26257
+ "Connect to a Mac or Linux computer over SSH. Connection settings are shared with Codex."
26258
+ )
26259
+ ),
26260
+ localVersion
26261
+ );
26262
+ header.append(headerCopy);
26263
+ root.append(header);
26264
+ content.append(root);
26265
+ const availableControl = getControl();
26266
+ if (!availableControl) {
26267
+ root.append(
26268
+ emptyPanel(
26269
+ t("\u6B64\u7A97\u53E3\u65E0\u6CD5\u7BA1\u7406 SSH \u8FDE\u63A5", "SSH connections are unavailable in this window"),
26270
+ t("\u8BF7\u5728 Codex \u684C\u9762\u7A0B\u5E8F\u4E2D\u7BA1\u7406 SSH \u8FDE\u63A5\u3002", "Manage SSH connections in Codex Desktop.")
26271
+ )
26272
+ );
26273
+ return;
26274
+ }
26275
+ const control = availableControl;
26276
+ const toolbar = el("div", "settings-remote__toolbar");
26277
+ const refreshButton = el("button", "settings-icon-button settings-remote__refresh");
26278
+ refreshButton.type = "button";
26279
+ refreshButton.title = t("\u5237\u65B0", "Refresh");
26280
+ refreshButton.setAttribute("aria-label", t("\u5237\u65B0", "Refresh"));
26281
+ refreshButton.append(createRendererSettingsIcon("refresh", 16));
26282
+ const addButton = el("button", "settings-command-button");
26283
+ addButton.type = "button";
26284
+ addButton.append(createRendererSettingsIcon("add", 14), t("\u6DFB\u52A0\u8FDE\u63A5", "Add connection"));
26285
+ toolbar.append(refreshButton, addButton);
26286
+ header.append(toolbar);
26287
+ const banner = el("div", "settings-remote__banner");
26288
+ banner.setAttribute("role", "alert");
26289
+ banner.hidden = true;
26290
+ const bannerText = el("span");
26291
+ const retry = el(
26292
+ "button",
26293
+ "settings-command-button settings-command-button--secondary",
26294
+ t("\u91CD\u8BD5", "Retry")
26295
+ );
26296
+ retry.type = "button";
26297
+ banner.append(bannerText, retry);
26298
+ const placeholder = el("div", "settings-remote__placeholder");
26299
+ const list = el("div", "settings-remote__list");
26300
+ root.append(banner, placeholder, list);
26301
+ let local = null;
26302
+ let localLoaded = false;
26303
+ let loaded = false;
26304
+ let loadError = null;
26305
+ let order = [];
26306
+ let polling = false;
26307
+ const rows = /* @__PURE__ */ new Map();
26308
+ const cards = /* @__PURE__ */ new Map();
26309
+ const installations = /* @__PURE__ */ new Map();
26310
+ const probeTokens = /* @__PURE__ */ new Map();
26311
+ const inspectFailures = /* @__PURE__ */ new Map();
26312
+ const restarting = /* @__PURE__ */ new Map();
26313
+ const focusedElement = () => {
26314
+ const active = root.getRootNode().activeElement;
26315
+ return active instanceof HTMLElement ? active : null;
26316
+ };
26317
+ function renderPlaceholder() {
26318
+ if (!loaded) {
26319
+ const loading = el("p", "settings-remote__loading");
26320
+ loading.append(
26321
+ el("span", "settings-remote-spinner"),
26322
+ t("\u6B63\u5728\u8BFB\u53D6\u8FDE\u63A5\u2026", "Loading connections\u2026")
26323
+ );
26324
+ placeholder.replaceChildren(...loadError ? [] : [loading]);
26325
+ } else if (!order.length) {
26326
+ const empty = emptyPanel(
26327
+ t("\u8FD8\u6CA1\u6709 SSH \u8FDE\u63A5", "No SSH connections yet"),
26328
+ t(
26329
+ "\u6DFB\u52A0\u4E00\u53F0 Mac \u6216 Linux \u7535\u8111\uFF0C\u5728\u4E0A\u9762\u8FD0\u884C\u4F1A\u8BDD\u3002",
26330
+ "Add a Mac or Linux computer to run conversations on it."
26331
+ )
26332
+ );
26333
+ const add = el(
26334
+ "button",
26335
+ "settings-command-button settings-command-button--secondary",
26336
+ t("\u6DFB\u52A0\u8FDE\u63A5", "Add connection")
26337
+ );
26338
+ add.type = "button";
26339
+ add.addEventListener("click", () => edit(null));
26340
+ empty.append(add);
26341
+ placeholder.replaceChildren(empty);
26342
+ } else placeholder.replaceChildren();
26343
+ }
26344
+ function render() {
26345
+ if (signal.aborted) return;
26346
+ localVersion.textContent = localLoaded ? `${t("\u672C\u673A codexhost", "Local codexhost")} ${local?.runningVersion ?? t("\u7248\u672C\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6", "version is temporarily unavailable")}` : "";
26347
+ banner.hidden = !loadError;
26348
+ bannerText.textContent = loadError ? `${t("\u8BFB\u53D6\u8FDE\u63A5\u5931\u8D25", "Could not load connections")}: ${localizeRemoteFailure(loadError, t)}` : "";
26349
+ renderPlaceholder();
26350
+ for (const [hostId, card] of cards) {
26351
+ if (rows.has(hostId)) continue;
26352
+ card.element.remove();
26353
+ cards.delete(hostId);
26354
+ }
26355
+ const active = focusedElement();
26356
+ order.forEach((hostId, index) => {
26357
+ const row = rows.get(hostId);
26358
+ if (!row) return;
26359
+ const signature = JSON.stringify([row, local]);
26360
+ let card = cards.get(hostId);
26361
+ if (card?.signature !== signature) {
26362
+ const element = renderRemoteCard(
26363
+ document2,
26364
+ row,
26365
+ local,
26366
+ t,
26367
+ (action) => handle(row, action)
26368
+ );
26369
+ const focusedAction = active && card?.element.contains(active) ? active.dataset.action : void 0;
26370
+ if (card) card.element.replaceWith(element);
26371
+ card = { element, signature };
26372
+ cards.set(hostId, card);
26373
+ if (focusedAction) focusAction(element, focusedAction);
26374
+ }
26375
+ if (list.children[index] !== card.element)
26376
+ list.insertBefore(card.element, list.children[index] ?? null);
26377
+ });
26378
+ }
26379
+ function focusAction(card, action) {
26380
+ const target = card.querySelector(`[data-action="${action}"]:not(:disabled)`) ?? card.querySelector("button:not(:disabled)");
26381
+ target?.focus();
26382
+ }
26383
+ async function probe(row) {
26384
+ const { hostId } = row.connection;
26385
+ const token = (probeTokens.get(hostId) ?? 0) + 1;
26386
+ probeTokens.set(hostId, token);
26387
+ const state = await control.ssh.state(hostId, signal).catch((error51) => ({ state: "unknown", error: remoteErrorMessage(error51) }));
26388
+ let remote = null;
26389
+ let remoteIssue = null;
26390
+ if (state.state === "connected")
26391
+ remote = await control.runtime(hostId).catch((error51) => {
26392
+ remoteIssue = remoteVersionIssue(error51);
26393
+ return null;
26394
+ });
26395
+ let expectedGap = false;
26396
+ if (remote && remoteBusy(remote)) restarting.set(hostId, Date.now() + RESTART_GRACE_MS);
26397
+ else if (remote || (restarting.get(hostId) ?? 0) <= Date.now()) restarting.delete(hostId);
26398
+ else if (row.remote) {
26399
+ expectedGap = true;
26400
+ remote = { ...row.remote, update: { ...row.remote.update, phase: "restarting" } };
26401
+ remoteIssue = null;
26402
+ }
26403
+ if (remote) installations.set(hostId, "installed");
26404
+ else if (!installations.has(hostId) && Date.now() - (inspectFailures.get(hostId) ?? -INSPECT_RETRY_MS) >= INSPECT_RETRY_MS) {
26405
+ const inspected = await control.setup(row.connection, "inspect").catch(() => null);
26406
+ if (inspected) installations.set(hostId, inspected.state);
26407
+ else inspectFailures.set(hostId, Date.now());
26408
+ }
26409
+ if (signal.aborted || rows.get(hostId) !== row || probeTokens.get(hostId) !== token) return;
26410
+ row.probing = false;
26411
+ row.state = expectedGap ? "connecting" : state.state;
26412
+ row.stateError = expectedGap ? null : state.error;
26413
+ row.remote = remote;
26414
+ row.remoteIssue = remoteIssue;
26415
+ row.installation = installations.get(hostId) ?? "unknown";
26416
+ render();
26417
+ }
26418
+ async function refresh(actingHostId, wait = "acting") {
26419
+ if (signal.aborted) return;
26420
+ const localTask = control.runtime("local").catch(() => null).then((status) => {
26421
+ if (signal.aborted) return;
26422
+ local = status;
26423
+ localLoaded = true;
26424
+ render();
26425
+ });
26426
+ let connections;
26427
+ try {
26428
+ connections = await control.ssh.list(signal);
26429
+ } catch (error51) {
26430
+ if (signal.aborted) return;
26431
+ loadError = remoteErrorMessage(error51);
26432
+ render();
26433
+ return;
26434
+ }
26435
+ if (signal.aborted) return;
26436
+ loadError = null;
26437
+ loaded = true;
26438
+ order = connections.map((connection) => connection.hostId);
26439
+ for (const connection of connections) {
26440
+ const row = rows.get(connection.hostId);
26441
+ if (row) row.connection = connection;
26442
+ else
26443
+ rows.set(connection.hostId, {
26444
+ connection,
26445
+ probing: true,
26446
+ state: null,
26447
+ stateError: null,
26448
+ remote: null,
26449
+ remoteIssue: null,
26450
+ installation: null,
26451
+ pending: null,
26452
+ notice: null
26453
+ });
26454
+ }
26455
+ for (const hostId of [...rows.keys()]) {
26456
+ if (order.includes(hostId)) continue;
26457
+ rows.delete(hostId);
26458
+ installations.delete(hostId);
26459
+ probeTokens.delete(hostId);
26460
+ inspectFailures.delete(hostId);
26461
+ restarting.delete(hostId);
26462
+ }
26463
+ render();
26464
+ const probes = [...rows.values()].filter((row) => !row.pending || row.connection.hostId === actingHostId).map((row) => ({ hostId: row.connection.hostId, done: probe(row) }));
26465
+ const awaited = wait === "all" ? probes : probes.filter((p) => p.hostId === actingHostId);
26466
+ await Promise.all([localTask, ...awaited.map((p) => p.done)]);
26467
+ }
26468
+ async function act(row, action, label, operation) {
26469
+ const { hostId } = row.connection;
26470
+ row.pending = label;
26471
+ row.notice = null;
26472
+ render();
26473
+ try {
26474
+ const done = await operation();
26475
+ await refresh(hostId);
26476
+ if (typeof done === "string") {
26477
+ const notice = { tone: "success", text: done };
26478
+ row.notice = notice;
26479
+ setTimeout(() => {
26480
+ if (row.notice !== notice) return;
26481
+ row.notice = null;
26482
+ render();
26483
+ }, SUCCESS_NOTICE_MS);
26484
+ }
26485
+ } catch (error51) {
26486
+ row.notice = { tone: "danger", text: remoteErrorMessage(error51) };
26487
+ installations.delete(hostId);
26488
+ inspectFailures.delete(hostId);
26489
+ void refresh(hostId);
26490
+ } finally {
26491
+ row.pending = null;
26492
+ if (!signal.aborted) {
26493
+ render();
26494
+ const card = cards.get(hostId)?.element;
26495
+ if (card && !focusedElement()) focusAction(card, action);
26496
+ }
26497
+ }
26498
+ }
26499
+ function edit(previous) {
26500
+ openRemoteConnectionEditor({
26501
+ container: root,
26502
+ signal,
26503
+ t,
26504
+ previous,
26505
+ async save(draft) {
26506
+ await control.ssh.save(draft, previous, signal);
26507
+ if (previous) {
26508
+ installations.delete(previous.hostId);
26509
+ inspectFailures.delete(previous.hostId);
26510
+ restarting.delete(previous.hostId);
26511
+ }
26512
+ await refresh();
26513
+ }
26514
+ });
26515
+ }
26516
+ function handle(row, action) {
26517
+ if (row.pending) return;
26518
+ const { connection } = row;
26519
+ const { hostId } = connection;
26520
+ const setConnected = async (enabled) => {
26521
+ const request = control.ssh.connect(hostId, enabled, signal);
26522
+ request.catch(() => void 0);
26523
+ await Promise.race([
26524
+ request,
26525
+ new Promise((resolve) => setTimeout(resolve, CONNECT_REQUEST_WAIT_MS))
26526
+ ]);
26527
+ };
26528
+ const reconnect = async () => {
26529
+ installations.delete(hostId);
26530
+ inspectFailures.delete(hostId);
26531
+ await setConnected(true);
26532
+ };
26533
+ switch (action) {
26534
+ case "edit":
26535
+ edit(connection);
26536
+ break;
26537
+ case "remove":
26538
+ openRemoteConnectionRemoval({
26539
+ container: root,
26540
+ signal,
26541
+ t,
26542
+ connection,
26543
+ async remove() {
26544
+ await control.ssh.remove(connection, signal);
26545
+ await refresh();
26546
+ }
26547
+ });
26548
+ break;
26549
+ case "toggle":
26550
+ void act(
26551
+ row,
26552
+ action,
26553
+ connection.autoConnect ? t("\u6B63\u5728\u65AD\u5F00\u2026", "Disconnecting\u2026") : t("\u6B63\u5728\u8FDE\u63A5\u2026", "Connecting\u2026"),
26554
+ () => setConnected(!connection.autoConnect)
26555
+ );
26556
+ break;
26557
+ case "reconnect":
26558
+ void act(row, action, t("\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5\u2026", "Reconnecting\u2026"), async () => {
26559
+ await setConnected(false);
26560
+ await setConnected(true);
26561
+ });
26562
+ break;
26563
+ case "recheck":
26564
+ void act(row, action, t("\u6B63\u5728\u91CD\u65B0\u68C0\u6D4B\u2026", "Checking again\u2026"), async () => {
26565
+ installations.delete(hostId);
26566
+ inspectFailures.delete(hostId);
26567
+ });
26568
+ break;
26569
+ case "install": {
26570
+ const version2 = remoteInstallVersion(local);
26571
+ if (!version2) break;
26572
+ void act(
26573
+ row,
26574
+ action,
26575
+ t("\u6B63\u5728\u5B89\u88C5\u8FDC\u7A0B\u670D\u52A1\uFF0C\u8BF7\u7A0D\u5019\u2026", "Installing remote service, please wait\u2026"),
26576
+ async () => {
26577
+ await control.setup(connection, "install", version2);
26578
+ await reconnect();
26579
+ return t("\u8FDC\u7A0B\u670D\u52A1\u5DF2\u5B89\u88C5\uFF0C\u6B63\u5728\u8FDE\u63A5", "Remote service installed; connecting");
26580
+ }
26581
+ );
26582
+ break;
26583
+ }
26584
+ case "upgrade": {
26585
+ const version2 = remoteInstallVersion(local);
26586
+ if (!version2) break;
26587
+ void act(
26588
+ row,
26589
+ action,
26590
+ t("\u6B63\u5728\u66F4\u65B0\u8FDC\u7A0B\u670D\u52A1\uFF0C\u8BF7\u7A0D\u5019\u2026", "Updating remote service, please wait\u2026"),
26591
+ async () => {
26592
+ await control.setup(connection, "update", version2);
26593
+ await reconnect();
26594
+ return t("\u8FDC\u7A0B\u670D\u52A1\u5DF2\u66F4\u65B0\uFF0C\u6B63\u5728\u8FDE\u63A5", "Remote service updated; connecting");
26595
+ }
26596
+ );
26597
+ break;
26598
+ }
26599
+ case "repair":
26600
+ void act(
26601
+ row,
26602
+ action,
26603
+ t("\u6B63\u5728\u91CD\u65B0\u914D\u7F6E\u8FDC\u7A0B\u670D\u52A1\u2026", "Reconfiguring remote service\u2026"),
26604
+ async () => {
26605
+ await control.setup(connection, "repair");
26606
+ await reconnect();
26607
+ return t("\u8FDC\u7A0B\u670D\u52A1\u5DF2\u4FEE\u590D\uFF0C\u6B63\u5728\u8FDE\u63A5", "Remote service repaired; connecting");
26608
+ }
26609
+ );
26610
+ break;
26611
+ case "update": {
26612
+ const target = row.remote ? remoteMaintenanceTarget(local, row.remote) : null;
26613
+ if (!target) break;
26614
+ const restart = target === row.remote?.installedVersion;
26615
+ void act(
26616
+ row,
26617
+ action,
26618
+ restart ? t("\u6B63\u5728\u91CD\u542F\u8FDC\u7A0B\u670D\u52A1\u2026", "Restarting remote service\u2026") : t("\u6B63\u5728\u63D0\u4EA4\u66F4\u65B0\u2026", "Starting update\u2026"),
26619
+ async () => {
26620
+ await control.update(hostId, target);
26621
+ await setConnected(true);
26622
+ }
26623
+ );
26624
+ break;
26625
+ }
26626
+ }
26627
+ }
26628
+ const reload = () => {
26629
+ if (refreshButton.getAttribute("aria-busy") === "true") return;
26630
+ refreshButton.setAttribute("aria-busy", "true");
26631
+ installations.clear();
26632
+ inspectFailures.clear();
26633
+ void refresh(void 0, "all").finally(() => refreshButton.removeAttribute("aria-busy"));
26634
+ };
26635
+ refreshButton.addEventListener("click", reload);
26636
+ retry.addEventListener("click", reload);
26637
+ addButton.addEventListener("click", () => edit(null));
26638
+ render();
26639
+ void refresh();
26640
+ const timer = setInterval(() => {
26641
+ if (polling) return;
26642
+ polling = true;
26643
+ void refresh(void 0, "all").finally(() => {
26644
+ polling = false;
26645
+ });
26646
+ }, POLL_INTERVAL_MS);
26647
+ return () => clearInterval(timer);
26648
+ }
26649
+ });
26650
+ }
26651
+
25097
26652
  // src/settings/core.ts
25098
26653
  var SETTINGS_PAGE_ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
25099
26654
  var MAX_SETTINGS_PAGE_ID_LENGTH = 48;
@@ -25197,6 +26752,74 @@ Codex Fast mode prioritizes requests and may increase usage.`
25197
26752
  }
25198
26753
  };
25199
26754
 
26755
+ // src/settings/horizontal-scroll-hint.ts
26756
+ function createHorizontalScrollHint(document2, scroller, className) {
26757
+ const element = document2.createElement("div");
26758
+ element.className = className;
26759
+ element.setAttribute("aria-hidden", "true");
26760
+ const thumb = document2.createElement("span");
26761
+ element.append(thumb);
26762
+ const limit = () => Math.max(0, scroller.scrollWidth - scroller.clientWidth);
26763
+ const scrollTo = (left) => {
26764
+ scroller.scrollLeft = Math.min(limit(), Math.max(0, left));
26765
+ update();
26766
+ };
26767
+ const update = () => {
26768
+ const { clientWidth, scrollLeft, scrollWidth } = scroller;
26769
+ const overflowing = scrollWidth > clientWidth + 1;
26770
+ element.dataset.overflowing = String(overflowing);
26771
+ if (!overflowing) return;
26772
+ thumb.style.width = `${clientWidth / scrollWidth * 100}%`;
26773
+ thumb.style.left = `${scrollLeft / scrollWidth * 100}%`;
26774
+ };
26775
+ scroller.addEventListener("scroll", update, { passive: true });
26776
+ scroller.addEventListener(
26777
+ "wheel",
26778
+ (event) => {
26779
+ if (event.deltaX !== 0 || event.deltaY === 0 || event.ctrlKey) return;
26780
+ const next = Math.min(limit(), Math.max(0, scroller.scrollLeft + event.deltaY));
26781
+ if (Math.abs(next - scroller.scrollLeft) < 1) return;
26782
+ event.preventDefault();
26783
+ scrollTo(next);
26784
+ },
26785
+ { passive: false }
26786
+ );
26787
+ let drag = null;
26788
+ element.addEventListener("pointerdown", (event) => {
26789
+ if (event.button !== 0 || limit() <= 1) return;
26790
+ const track = element.getBoundingClientRect();
26791
+ if (track.width <= 0) return;
26792
+ if (event.target !== thumb) {
26793
+ const pointed = (event.clientX - track.left) / track.width * scroller.scrollWidth;
26794
+ scrollTo(pointed - scroller.clientWidth / 2);
26795
+ }
26796
+ drag = {
26797
+ pointerId: event.pointerId,
26798
+ startX: event.clientX,
26799
+ startLeft: scroller.scrollLeft,
26800
+ // One pixel along the line is this many pixels of row content.
26801
+ scale: scroller.scrollWidth / track.width
26802
+ };
26803
+ element.dataset.dragging = "true";
26804
+ element.setPointerCapture(event.pointerId);
26805
+ event.preventDefault();
26806
+ });
26807
+ element.addEventListener("pointermove", (event) => {
26808
+ if (drag?.pointerId !== event.pointerId) return;
26809
+ scrollTo(drag.startLeft + (event.clientX - drag.startX) * drag.scale);
26810
+ });
26811
+ const release = (event) => {
26812
+ if (drag?.pointerId !== event.pointerId) return;
26813
+ drag = null;
26814
+ delete element.dataset.dragging;
26815
+ };
26816
+ element.addEventListener("pointerup", release);
26817
+ element.addEventListener("pointercancel", release);
26818
+ const ResizeObserverClass = document2.defaultView?.ResizeObserver;
26819
+ if (ResizeObserverClass) new ResizeObserverClass(update).observe(scroller);
26820
+ return { element, update };
26821
+ }
26822
+
25200
26823
  // src/settings/session-import-list-controls.ts
25201
26824
  function createSessionImportListControls(document2, messages, onChange) {
25202
26825
  let query = "";
@@ -25330,7 +26953,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
25330
26953
  text.textContent = message;
25331
26954
  return text;
25332
26955
  }
25333
- function createSessionImportSettingsPage(messages, getClient, openImportedThread) {
26956
+ function createSessionImportSettingsPage(messages, getClient, openImportedThread, getDiagnostics = () => null) {
25334
26957
  return Object.freeze({
25335
26958
  id: "session-import",
25336
26959
  label: messages.pageLabels["session-import"],
@@ -25363,10 +26986,19 @@ Codex Fast mode prioritizes requests and may increase usage.`
25363
26986
  harnessOptions.dataset.sessionImportHarness = "selector";
25364
26987
  harnessOptions.setAttribute("role", "group");
25365
26988
  harnessOptions.setAttribute("aria-label", messages.sessionImportHarness);
26989
+ const harnessScroller = document2.createElement("div");
26990
+ harnessScroller.className = "settings-session-import-harness__scroller";
26991
+ const harnessHint = createHorizontalScrollHint(
26992
+ document2,
26993
+ harnessOptions,
26994
+ "settings-session-import-harness__indicator"
26995
+ );
26996
+ harnessScroller.append(harnessOptions, harnessHint.element);
25366
26997
  let selectedHarness = null;
25367
26998
  let sources = [];
25368
26999
  const sourceButtons = [];
25369
27000
  const renderHarnessOptions = () => {
27001
+ const scrolled = harnessOptions.scrollLeft;
25370
27002
  harnessOptions.replaceChildren();
25371
27003
  sourceButtons.length = 0;
25372
27004
  for (const source of sources) {
@@ -25387,8 +27019,11 @@ Codex Fast mode prioritizes requests and may increase usage.`
25387
27019
  sourceButtons.push(option);
25388
27020
  harnessOptions.append(option);
25389
27021
  }
27022
+ harnessOptions.scrollLeft = scrolled;
27023
+ harnessOptions.querySelector('[aria-pressed="true"]')?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
27024
+ harnessHint.update();
25390
27025
  };
25391
- harness.append(harnessLabel, harnessOptions);
27026
+ harness.append(harnessLabel, harnessScroller);
25392
27027
  const description = document2.createElement("p");
25393
27028
  description.className = "settings-page-description";
25394
27029
  description.textContent = messages.sessionImportDescription;
@@ -25677,10 +27312,15 @@ Codex Fast mode prioritizes requests and may increase usage.`
25677
27312
  const pageParams = listControls.params();
25678
27313
  void context.runLatest(
25679
27314
  async (signal) => {
25680
- const result = await client.listSessionImportSources();
25681
- const selected = result.harnesses.find(({ harnessId }) => harnessId === requestedHarness)?.harnessId ?? result.harnesses[0]?.harnessId ?? null;
27315
+ const missing = new Set(
27316
+ getDiagnostics()?.snapshot().hosts.find(({ hostId }) => hostId === "local")?.agents.filter(({ availability }) => availability === "notInstalled").map(({ agent }) => agent)
27317
+ );
27318
+ const harnesses = (await client.listSessionImportSources()).harnesses.filter(
27319
+ ({ harnessId }) => !missing.has(harnessId)
27320
+ );
27321
+ const selected = harnesses.find(({ harnessId }) => harnessId === requestedHarness)?.harnessId ?? harnesses[0]?.harnessId ?? null;
25682
27322
  if (signal.aborted) throw new Error("Session import selection changed");
25683
- sources = result.harnesses;
27323
+ sources = harnesses;
25684
27324
  selectedHarness = selected;
25685
27325
  if (selected !== requestedHarness) {
25686
27326
  listControls.reset();
@@ -25821,104 +27461,6 @@ Codex Fast mode prioritizes requests and may increase usage.`
25821
27461
  };
25822
27462
  }
25823
27463
 
25824
- // src/settings/preference-ui.ts
25825
- var nextPreferenceId = 0;
25826
- function preferenceId(prefix) {
25827
- nextPreferenceId += 1;
25828
- return `codexhost-${prefix}-${nextPreferenceId}`;
25829
- }
25830
- function createPreferenceGroup(document2, title) {
25831
- const group = document2.createElement("section");
25832
- group.className = "mt-7 first-of-type:mt-0";
25833
- const header = document2.createElement("div");
25834
- header.className = "mb-2 flex min-h-6 items-center gap-2 px-1";
25835
- const heading = document2.createElement("h3");
25836
- heading.id = preferenceId("preference-group");
25837
- heading.className = "m-0 text-[13px] leading-5 font-semibold text-settings-text";
25838
- heading.textContent = title;
25839
- header.append(heading);
25840
- const card = document2.createElement("div");
25841
- card.className = "rounded-[10px] border border-settings-border bg-settings-panel";
25842
- group.setAttribute("aria-labelledby", heading.id);
25843
- group.append(header, card);
25844
- return { group, header, card };
25845
- }
25846
- function createPreferenceItem(document2, input) {
25847
- const item = document2.createElement("div");
25848
- item.className = "flex min-h-[60px] items-center justify-between gap-6 border-t border-settings-divider px-4 py-3 first:border-t-0";
25849
- const copy = document2.createElement("div");
25850
- copy.className = "flex min-w-0 flex-col gap-0.5";
25851
- const heading = document2.createElement("div");
25852
- heading.className = "flex items-center gap-1.5";
25853
- const title = document2.createElement("label");
25854
- title.htmlFor = input.controlId;
25855
- title.className = "cursor-pointer text-[13px] leading-5 font-medium text-settings-text";
25856
- title.textContent = input.title;
25857
- heading.append(title);
25858
- if (input.help) heading.append(createHelpTooltip(document2, input.help.label, input.help.lines));
25859
- const description = document2.createElement("div");
25860
- description.id = preferenceId("preference-description");
25861
- description.className = "text-xs leading-[18px] text-settings-muted data-[invalid=true]:text-settings-danger";
25862
- description.textContent = input.description;
25863
- copy.append(heading, description);
25864
- item.append(copy);
25865
- return { item, description };
25866
- }
25867
- function createHelpTooltip(document2, label, lines) {
25868
- const wrapper = document2.createElement("span");
25869
- wrapper.className = "group relative inline-flex";
25870
- const button = document2.createElement("button");
25871
- button.type = "button";
25872
- button.className = "inline-flex size-4 cursor-help items-center justify-center rounded-full border-0 bg-transparent p-0 text-settings-subtle transition-colors hover:text-settings-text focus-visible:text-settings-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-settings-focus";
25873
- button.setAttribute("aria-label", label);
25874
- button.append(createRendererSettingsIcon("help", 14));
25875
- const tooltip = document2.createElement("div");
25876
- tooltip.id = preferenceId("preference-tooltip");
25877
- tooltip.setAttribute("role", "tooltip");
25878
- tooltip.className = "pointer-events-none invisible absolute top-full left-0 z-20 mt-2 w-80 rounded-lg border border-settings-border bg-settings-panel px-3 py-2.5 text-xs leading-[18px] font-normal text-settings-text opacity-0 shadow-[0_10px_30px_rgb(0_0_0/0.22)] transition-opacity duration-150 group-hover:visible group-hover:opacity-100 group-focus-within:visible group-focus-within:opacity-100";
25879
- const list = document2.createElement("ul");
25880
- list.className = "m-0 grid list-disc gap-1 pl-4 marker:text-settings-subtle";
25881
- for (const line of lines) {
25882
- const item = document2.createElement("li");
25883
- item.textContent = line;
25884
- list.append(item);
25885
- }
25886
- tooltip.append(list);
25887
- button.setAttribute("aria-describedby", tooltip.id);
25888
- button.addEventListener("keydown", (event) => {
25889
- if (event.key === "Escape") button.blur();
25890
- });
25891
- wrapper.append(button, tooltip);
25892
- return wrapper;
25893
- }
25894
- function createPreferenceSwitch(document2, id, describedBy) {
25895
- const input = document2.createElement("input");
25896
- input.type = "checkbox";
25897
- input.id = id;
25898
- input.setAttribute("role", "switch");
25899
- input.setAttribute("aria-describedby", describedBy);
25900
- input.className = "relative m-0 h-5 w-9 shrink-0 cursor-pointer appearance-none rounded-full bg-settings-surface-hover ring-1 ring-settings-border transition-colors ring-inset before:absolute before:top-0.5 before:left-0.5 before:size-4 before:rounded-full before:bg-white before:shadow-[0_1px_2px_rgb(0_0_0/0.3)] before:transition-transform checked:bg-settings-focus checked:ring-transparent checked:before:translate-x-4 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-settings-focus";
25901
- return input;
25902
- }
25903
- function createNumberField(document2, input) {
25904
- const field = document2.createElement("div");
25905
- field.className = "flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-settings-border bg-settings-surface px-2.5 transition-colors focus-within:border-settings-focus data-[invalid=true]:border-settings-danger";
25906
- const control = document2.createElement("input");
25907
- control.type = "number";
25908
- control.id = input.id;
25909
- control.min = String(input.min);
25910
- control.max = String(input.max);
25911
- control.step = "1";
25912
- control.setAttribute("aria-describedby", input.describedBy);
25913
- control.className = "w-14 border-0 bg-transparent p-0 text-right text-[13px] leading-5 text-settings-text tabular-nums outline-none";
25914
- const unit = document2.createElement("span");
25915
- unit.className = "text-xs text-settings-muted";
25916
- unit.setAttribute("aria-hidden", "true");
25917
- unit.textContent = input.unit;
25918
- field.append(control, unit);
25919
- return { field, control };
25920
- }
25921
-
25922
27464
  // src/settings/idle-release-controls.ts
25923
27465
  function mountIdleReleaseControls(context, messages, getLoadedSessionsClient) {
25924
27466
  const { content } = context;
@@ -26283,7 +27825,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
26283
27825
  }
26284
27826
  function defaultImportName(source, existing) {
26285
27827
  const base = source.provider === "openai-codex" ? "codex" : "grok";
26286
- const used = new Set(existing.map((record3) => record3.name));
27828
+ const used = new Set(existing.map((record4) => record4.name));
26287
27829
  const valid = (name) => credentialImportNameSchema.safeParse(name).success;
26288
27830
  if (!used.has(base)) return base;
26289
27831
  const local = source.label.includes("@") ? source.label.split("@")[0] ?? "" : "";
@@ -26303,7 +27845,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
26303
27845
  let othersExpanded = false;
26304
27846
  const targetButtons = /* @__PURE__ */ new Map();
26305
27847
  const importedRecords = () => snapshot.targets.find((target) => target.harnessId === TARGET_HARNESS_ID)?.imports ?? [];
26306
- const sourceOf = (record3) => snapshot.sources.find((source) => source.id === record3.source.id);
27848
+ const sourceOf = (record4) => snapshot.sources.find((source) => source.id === record4.source.id);
26307
27849
  const section = document2.createElement("section");
26308
27850
  section.className = "settings-pi-accounts";
26309
27851
  section.hidden = true;
@@ -26352,33 +27894,33 @@ Codex Fast mode prioritizes requests and may increase usage.`
26352
27894
  mark.append(createRendererAgentIcon(agent ?? "pi", 26, document2));
26353
27895
  return mark;
26354
27896
  };
26355
- const renderRow = (record3) => {
27897
+ const renderRow = (record4) => {
26356
27898
  const row = document2.createElement("div");
26357
27899
  row.className = "settings-pi-accounts__row";
26358
27900
  row.tabIndex = -1;
26359
- row.dataset.importName = record3.name;
26360
- row.setAttribute("aria-label", record3.source.label);
26361
- const agent = KNOWN_RENDERER_AGENTS.find((candidate) => candidate === record3.source.harnessId);
27901
+ row.dataset.importName = record4.name;
27902
+ row.setAttribute("aria-label", record4.source.label);
27903
+ const agent = KNOWN_RENDERER_AGENTS.find((candidate) => candidate === record4.source.harnessId);
26362
27904
  const mark = createMark(agent);
26363
27905
  const identity = document2.createElement("div");
26364
27906
  identity.className = "settings-pi-accounts__identity";
26365
27907
  const label = document2.createElement("strong");
26366
27908
  label.className = "settings-account-email";
26367
- label.textContent = record3.source.label;
26368
- label.title = record3.source.label;
27909
+ label.textContent = record4.source.label;
27910
+ label.title = record4.source.label;
26369
27911
  label.translate = false;
26370
27912
  const meta3 = document2.createElement("div");
26371
27913
  meta3.className = "settings-account-metadata";
26372
- const source = sourceOf(record3);
27914
+ const source = sourceOf(record4);
26373
27915
  const parts = [
26374
27916
  metaPart(
26375
- agent && RENDERER_AGENT_LABELS[agent] || record3.source.harnessId,
27917
+ agent && RENDERER_AGENT_LABELS[agent] || record4.source.harnessId,
26376
27918
  "settings-pi-accounts__agent"
26377
27919
  ),
26378
- metaPart(`${record3.name}/\u2026`, void 0, true),
27920
+ metaPart(`${record4.name}/\u2026`, void 0, true),
26379
27921
  metaPart(messages.copied)
26380
27922
  ];
26381
- const time3 = formatImportedAt(record3.importedAt);
27923
+ const time3 = formatImportedAt(record4.importedAt);
26382
27924
  if (time3) parts.push(metaPart(time3));
26383
27925
  parts.forEach((part, index) => {
26384
27926
  if (index > 0) {
@@ -26395,23 +27937,23 @@ Codex Fast mode prioritizes requests and may increase usage.`
26395
27937
  reimport.type = "button";
26396
27938
  reimport.className = "settings-account-action";
26397
27939
  reimport.textContent = messages.rowReimport;
26398
- reimport.setAttribute("aria-label", `${messages.reimport}: ${record3.source.label}`);
27940
+ reimport.setAttribute("aria-label", `${messages.reimport}: ${record4.source.label}`);
26399
27941
  reimport.disabled = busy || !source;
26400
27942
  reimport.title = messages.reimport;
26401
27943
  reimport.addEventListener("click", () => {
26402
- if (source) openDialog("reimport", { source, record: record3 }, `row:${record3.name}`);
27944
+ if (source) openDialog("reimport", { source, record: record4 }, `row:${record4.name}`);
26403
27945
  });
26404
27946
  const remove = document2.createElement("button");
26405
27947
  remove.type = "button";
26406
27948
  remove.className = "settings-account-action settings-pi-accounts__remove";
26407
27949
  remove.textContent = messages.rowRemove;
26408
- remove.setAttribute("aria-label", `${messages.remove}: ${record3.source.label}`);
27950
+ remove.setAttribute("aria-label", `${messages.remove}: ${record4.source.label}`);
26409
27951
  remove.disabled = busy;
26410
- remove.addEventListener("click", () => openDialog("remove", { record: record3 }, `row:${record3.name}`));
27952
+ remove.addEventListener("click", () => openDialog("remove", { record: record4 }, `row:${record4.name}`));
26411
27953
  if (source) actions.append(reimport);
26412
27954
  actions.append(remove);
26413
27955
  row.append(mark, identity, actions);
26414
- rows.set(record3.name, row);
27956
+ rows.set(record4.name, row);
26415
27957
  return row;
26416
27958
  };
26417
27959
  const loginTypeLabel = (type) => type === "oauth" ? messages.typeOauth : type === "api_key" ? messages.typeApiKey : messages.typeOther;
@@ -26558,7 +28100,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
26558
28100
  const name = request.action === "list" ? void 0 : request.name;
26559
28101
  state = {
26560
28102
  source: state.source,
26561
- record: importedRecords().find((record3) => record3.name === name) ?? state.record
28103
+ record: importedRecords().find((record4) => record4.name === name) ?? state.record
26562
28104
  };
26563
28105
  show(next);
26564
28106
  } else {
@@ -26571,20 +28113,20 @@ Codex Fast mode prioritizes requests and may increase usage.`
26571
28113
  nameInput = void 0;
26572
28114
  body.replaceChildren();
26573
28115
  controls.replaceChildren();
26574
- const { source, record: record3 } = state;
28116
+ const { source, record: record4 } = state;
26575
28117
  const close = () => modal.close();
26576
28118
  if (mode === "add" || mode === "reimport") {
26577
28119
  title.textContent = mode === "add" ? messages.add : messages.reimport;
26578
28120
  modal.setAttribute("aria-label", title.textContent);
26579
- const active = source ?? record3?.source;
28121
+ const active = source ?? record4?.source;
26580
28122
  if (active) body.append(line(messages.source, active.label));
26581
28123
  const input = document2.createElement("input");
26582
28124
  input.type = "text";
26583
28125
  input.maxLength = 48;
26584
28126
  input.autocomplete = "off";
26585
28127
  input.spellcheck = false;
26586
- input.value = record3?.name ?? (source ? defaultImportName(source, importedRecords()) : "codex");
26587
- input.readOnly = Boolean(record3);
28128
+ input.value = record4?.name ?? (source ? defaultImportName(source, importedRecords()) : "codex");
28129
+ input.readOnly = Boolean(record4);
26588
28130
  nameInput = input;
26589
28131
  const label = document2.createElement("label");
26590
28132
  label.className = "settings-credential-dialog__field";
@@ -26613,7 +28155,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
26613
28155
  {
26614
28156
  action: mode === "reimport" ? "reimport" : "import",
26615
28157
  sourceId: source.id,
26616
- name: record3?.name ?? input.value,
28158
+ name: record4?.name ?? input.value,
26617
28159
  confirmed: true
26618
28160
  },
26619
28161
  "done"
@@ -26623,16 +28165,16 @@ Codex Fast mode prioritizes requests and may increase usage.`
26623
28165
  )
26624
28166
  );
26625
28167
  (mode === "add" ? input : controls.children[0]).focus();
26626
- } else if (mode === "remove" && record3) {
28168
+ } else if (mode === "remove" && record4) {
26627
28169
  title.textContent = messages.remove;
26628
28170
  modal.setAttribute("aria-label", title.textContent);
26629
- body.append(note(messages.removeWarning.replace("{name}", record3.name)));
28171
+ body.append(note(messages.removeWarning.replace("{name}", record4.name)));
26630
28172
  const cancel = command(messages.cancel, close);
26631
28173
  controls.append(
26632
28174
  cancel,
26633
28175
  command(
26634
28176
  messages.remove,
26635
- () => submit({ action: "remove", name: record3.name, confirmed: true }, null),
28177
+ () => submit({ action: "remove", name: record4.name, confirmed: true }, null),
26636
28178
  "danger"
26637
28179
  )
26638
28180
  );
@@ -26640,7 +28182,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
26640
28182
  } else {
26641
28183
  title.textContent = messages.doneTitle;
26642
28184
  modal.setAttribute("aria-label", title.textContent);
26643
- const name = record3?.name;
28185
+ const name = record4?.name;
26644
28186
  if (name) body.append(line(messages.entry, `${name}/\u2026`, true));
26645
28187
  body.append(note(messages.done));
26646
28188
  const done = command(messages.close, close, "primary");
@@ -26681,25 +28223,25 @@ Codex Fast mode prioritizes requests and may increase usage.`
26681
28223
  (candidate) => candidate.harnessId === TARGET_HARNESS_ID && source && candidate.providers.includes(source.provider)
26682
28224
  );
26683
28225
  if (!source || !target) return null;
26684
- const record3 = target.imports.find((candidate) => candidate.source.id === source.id);
28226
+ const record4 = target.imports.find((candidate) => candidate.source.id === source.id);
26685
28227
  const key = `${harnessId}:${accountLabel}:import`;
26686
28228
  const button = document2.createElement("button");
26687
28229
  button.type = "button";
26688
28230
  button.className = "settings-icon-button settings-account-harness-target";
26689
28231
  button.dataset.accountFocus = key;
26690
28232
  button.disabled = busy;
26691
- if (record3) button.dataset.state = "imported";
26692
- const hint = record3 ? messages.importedHint.replace("{name}", record3.name) : messages.add;
28233
+ if (record4) button.dataset.state = "imported";
28234
+ const hint = record4 ? messages.importedHint.replace("{name}", record4.name) : messages.add;
26693
28235
  button.title = hint;
26694
28236
  button.setAttribute("aria-label", hint);
26695
28237
  button.setAttribute("aria-haspopup", "dialog");
26696
28238
  button.append(createRendererAgentIcon("pi", 16, document2));
26697
28239
  button.addEventListener("click", () => {
26698
- if (!record3) {
28240
+ if (!record4) {
26699
28241
  openDialog("add", { source }, key);
26700
28242
  return;
26701
28243
  }
26702
- const row = rows.get(record3.name);
28244
+ const row = rows.get(record4.name);
26703
28245
  row?.scrollIntoView({ block: "nearest", behavior: "smooth" });
26704
28246
  row?.focus();
26705
28247
  });
@@ -27425,7 +28967,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
27425
28967
  }
27426
28968
 
27427
28969
  // src/settings/accounts-page.ts
27428
- function errorMessage(error51, fallback) {
28970
+ function errorMessage2(error51, fallback) {
27429
28971
  return error51 instanceof Error && error51.message ? error51.message : fallback;
27430
28972
  }
27431
28973
  function createAccountsSettingsPage(messages, getClient) {
@@ -27667,7 +29209,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
27667
29209
  refreshInBackground();
27668
29210
  },
27669
29211
  failure(error51) {
27670
- loadMessage = errorMessage(error51, messages.accountLoadFailed);
29212
+ loadMessage = errorMessage2(error51, messages.accountLoadFailed);
27671
29213
  render();
27672
29214
  }
27673
29215
  });
@@ -28242,11 +29784,17 @@ Codex Fast mode prioritizes requests and may increase usage.`
28242
29784
  }
28243
29785
  });
28244
29786
  }
28245
- function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread = () => Promise.reject(new Error("Imported Thread navigation is unavailable")), getLoadedSessionsClient = () => null) {
29787
+ function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread = () => Promise.reject(new Error("Imported Thread navigation is unavailable")), getLoadedSessionsClient = () => null, getRemoteConnections = () => null) {
28246
29788
  return Object.freeze([
28247
29789
  createConnectionsSettingsPage(messages, getDiagnostics),
29790
+ createRemoteConnectionsPage(messages, getRemoteConnections),
28248
29791
  createAccountsSettingsPage(messages, getAccountClient),
28249
- createSessionImportSettingsPage(messages, getSessionImportClient, openImportedThread),
29792
+ createSessionImportSettingsPage(
29793
+ messages,
29794
+ getSessionImportClient,
29795
+ openImportedThread,
29796
+ getDiagnostics
29797
+ ),
28250
29798
  createAppearanceSettingsPage(messages, getLoadedSessionsClient),
28251
29799
  updatesPage(messages, getUpdateClient),
28252
29800
  aboutPage(messages, getUpdateClient)
@@ -28691,26 +30239,32 @@ Codex Fast mode prioritizes requests and may increase usage.`
28691
30239
 
28692
30240
  .settings-session-import-harness {
28693
30241
  display: flex;
28694
- align-items: center;
30242
+ /* The label lines up with the options whether or not the scroll indicator is shown. */
30243
+ align-items: flex-start;
28695
30244
  min-width: 0;
28696
30245
  gap: 12px;
28697
- margin: -12px 0 16px;
30246
+ margin: -12px 0 10px;
28698
30247
  color: var(--settings-muted);
28699
30248
  font-size: 13px;
28700
30249
  }
28701
30250
 
28702
30251
  .settings-session-import-harness > span {
28703
30252
  flex: none;
30253
+ line-height: 32px;
30254
+ }
30255
+
30256
+ .settings-session-import-harness__scroller {
30257
+ min-width: 0;
30258
+ flex: 1;
28704
30259
  }
28705
30260
 
28706
30261
  .settings-session-import-harness__options {
28707
30262
  display: flex;
28708
30263
  align-items: center;
28709
- min-width: 0;
28710
- flex: 1;
28711
30264
  gap: 6px;
28712
30265
  overflow-x: auto;
28713
30266
  overflow-y: hidden;
30267
+ /* Replaced by the hairline below: a native overlay scrollbar covers the options. */
28714
30268
  scrollbar-width: none;
28715
30269
  }
28716
30270
 
@@ -28718,6 +30272,59 @@ Codex Fast mode prioritizes requests and may increase usage.`
28718
30272
  display: none;
28719
30273
  }
28720
30274
 
30275
+ /*
30276
+ * Always occupies 6px so the page does not shift; drawn only while the options overflow. The box
30277
+ * is taller than the 2px line it draws so the line can be grabbed with a mouse.
30278
+ */
30279
+ .settings-session-import-harness__indicator {
30280
+ position: relative;
30281
+ height: 12px;
30282
+ margin-bottom: -6px;
30283
+ cursor: grab;
30284
+ touch-action: none;
30285
+ visibility: hidden;
30286
+ }
30287
+
30288
+ .settings-session-import-harness__indicator[data-overflowing="true"] {
30289
+ visibility: visible;
30290
+ }
30291
+
30292
+ .settings-session-import-harness__indicator[data-dragging="true"] {
30293
+ cursor: grabbing;
30294
+ }
30295
+
30296
+ .settings-session-import-harness__indicator::before,
30297
+ .settings-session-import-harness__indicator > span {
30298
+ position: absolute;
30299
+ top: 4px;
30300
+ height: 2px;
30301
+ border-radius: 1px;
30302
+ }
30303
+
30304
+ .settings-session-import-harness__indicator::before {
30305
+ right: 0;
30306
+ left: 0;
30307
+ background: var(--settings-divider);
30308
+ content: "";
30309
+ }
30310
+
30311
+ .settings-session-import-harness__indicator > span {
30312
+ /* Quieter than text: a hint that there is more, not a control competing with the options. */
30313
+ background: light-dark(rgb(13 13 13 / 24%), rgb(255 255 255 / 26%));
30314
+ }
30315
+
30316
+ /* The thumb takes the press across the whole grab area, not only on its 2px line. */
30317
+ .settings-session-import-harness__indicator > span::before {
30318
+ position: absolute;
30319
+ inset: -4px 0 -6px;
30320
+ content: "";
30321
+ }
30322
+
30323
+ .settings-session-import-harness__indicator:hover > span,
30324
+ .settings-session-import-harness__indicator[data-dragging="true"] > span {
30325
+ background: light-dark(rgb(13 13 13 / 42%), rgb(255 255 255 / 46%));
30326
+ }
30327
+
28721
30328
  .settings-session-import-harness__option {
28722
30329
  min-height: 32px;
28723
30330
  flex: none;
@@ -29354,6 +30961,12 @@ Codex Fast mode prioritizes requests and may increase usage.`
29354
30961
  border-radius: 10px;
29355
30962
  }
29356
30963
 
30964
+ .settings-harness-installation-actions {
30965
+ display: flex;
30966
+ flex-wrap: wrap;
30967
+ gap: 8px;
30968
+ }
30969
+
29357
30970
  .settings-harness-installation-command pre {
29358
30971
  white-space: pre-wrap;
29359
30972
  overflow-wrap: anywhere;
@@ -29361,6 +30974,27 @@ Codex Fast mode prioritizes requests and may increase usage.`
29361
30974
  font-size: 12px;
29362
30975
  }
29363
30976
 
30977
+ .settings-connection-website {
30978
+ display: inline-flex;
30979
+ align-items: center;
30980
+ justify-content: center;
30981
+ flex: none;
30982
+ width: 22px;
30983
+ height: 22px;
30984
+ color: var(--settings-muted);
30985
+ border-radius: 4px;
30986
+ }
30987
+
30988
+ .settings-connection-website:hover {
30989
+ color: var(--settings-focus);
30990
+ background: var(--settings-hover);
30991
+ }
30992
+
30993
+ .settings-connection-website:focus-visible {
30994
+ outline: 2px solid var(--settings-focus);
30995
+ outline-offset: 1px;
30996
+ }
30997
+
29364
30998
  .settings-connection-install-link {
29365
30999
  display: inline-flex;
29366
31000
  align-items: center;
@@ -29372,13 +31006,11 @@ Codex Fast mode prioritizes requests and may increase usage.`
29372
31006
  background: transparent;
29373
31007
  border: 1px solid transparent;
29374
31008
  border-radius: 7px;
29375
- opacity: 0;
29376
- transition: opacity 120ms ease;
29377
31009
  }
29378
31010
 
29379
- .settings-connection-row:hover .settings-connection-install-link,
29380
- .settings-connection-install-link:focus-visible {
29381
- opacity: 1;
31011
+ .settings-connection-install-link:disabled {
31012
+ opacity: 0.45;
31013
+ cursor: wait;
29382
31014
  }
29383
31015
 
29384
31016
  .settings-connection-install-link:hover {
@@ -29427,6 +31059,10 @@ Codex Fast mode prioritizes requests and may increase usage.`
29427
31059
  padding: 12px;
29428
31060
  }
29429
31061
 
31062
+ .settings-connection-inspector__body:empty {
31063
+ display: none;
31064
+ }
31065
+
29430
31066
  .settings-harness-version {
29431
31067
  display: grid;
29432
31068
  gap: 10px;
@@ -29551,33 +31187,6 @@ Codex Fast mode prioritizes requests and may increase usage.`
29551
31187
  border-radius: 10px;
29552
31188
  }
29553
31189
 
29554
- .settings-connection-error-metadata {
29555
- display: grid;
29556
- gap: 7px;
29557
- padding: 12px;
29558
- background: var(--settings-inset);
29559
- border: 1px solid var(--settings-border);
29560
- border-radius: 9px;
29561
- }
29562
-
29563
- .settings-connection-detail-line {
29564
- display: grid;
29565
- grid-template-columns: 92px minmax(0, 1fr);
29566
- gap: 10px;
29567
- min-width: 0;
29568
- color: var(--settings-muted);
29569
- font-size: 10px;
29570
- line-height: 16px;
29571
- }
29572
-
29573
- .settings-connection-detail-line code {
29574
- min-width: 0;
29575
- overflow-wrap: anywhere;
29576
- color: var(--settings-text);
29577
- font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;
29578
- white-space: pre-wrap;
29579
- }
29580
-
29581
31190
  .settings-connection-error-log-header {
29582
31191
  display: flex;
29583
31192
  align-items: center;
@@ -30394,6 +32003,336 @@ Codex Fast mode prioritizes requests and may increase usage.`
30394
32003
  transition-duration: 0s !important;
30395
32004
  }
30396
32005
  }
32006
+
32007
+ .settings-remote {
32008
+ display: grid;
32009
+ gap: 14px;
32010
+ min-width: 0;
32011
+ }
32012
+
32013
+ .settings-remote__header {
32014
+ display: flex;
32015
+ align-items: flex-start;
32016
+ justify-content: space-between;
32017
+ gap: 18px;
32018
+ margin-bottom: 4px;
32019
+ }
32020
+
32021
+ .settings-remote__header .settings-section-label {
32022
+ padding-bottom: 0;
32023
+ }
32024
+
32025
+ .settings-remote__header .settings-page-description {
32026
+ margin: 5px 0 0;
32027
+ }
32028
+
32029
+ .settings-remote__meta {
32030
+ margin: 2px 0 0;
32031
+ color: var(--settings-subtle);
32032
+ font-size: 12px;
32033
+ line-height: 18px;
32034
+ font-variant-numeric: tabular-nums;
32035
+ }
32036
+
32037
+ .settings-remote__meta:empty {
32038
+ display: none;
32039
+ }
32040
+
32041
+ .settings-remote__toolbar,
32042
+ .settings-remote-card__actions {
32043
+ display: flex;
32044
+ align-items: center;
32045
+ flex: none;
32046
+ gap: 6px;
32047
+ }
32048
+
32049
+ .settings-remote__toolbar {
32050
+ gap: 8px;
32051
+ }
32052
+
32053
+ .settings-remote__banner {
32054
+ display: flex;
32055
+ align-items: center;
32056
+ justify-content: space-between;
32057
+ gap: 12px;
32058
+ padding: 8px 8px 8px 12px;
32059
+ color: var(--settings-danger);
32060
+ font-size: 12px;
32061
+ line-height: 18px;
32062
+ overflow-wrap: anywhere;
32063
+ background: var(--settings-danger-bg);
32064
+ border: 1px solid var(--settings-danger-border);
32065
+ border-radius: 10px;
32066
+ }
32067
+
32068
+ .settings-remote__loading {
32069
+ display: flex;
32070
+ align-items: center;
32071
+ gap: 8px;
32072
+ margin: 0;
32073
+ padding: 16px 2px;
32074
+ color: var(--settings-muted);
32075
+ font-size: 12px;
32076
+ }
32077
+
32078
+ .settings-remote__empty {
32079
+ display: flex;
32080
+ align-items: center;
32081
+ justify-content: space-between;
32082
+ gap: 16px;
32083
+ padding: 16px;
32084
+ background: var(--settings-panel);
32085
+ border: 1px dashed var(--settings-border);
32086
+ border-radius: 10px;
32087
+ }
32088
+
32089
+ .settings-remote__empty > div {
32090
+ display: grid;
32091
+ min-width: 0;
32092
+ gap: 2px;
32093
+ }
32094
+
32095
+ .settings-remote__empty strong {
32096
+ color: var(--settings-text);
32097
+ font-size: 13px;
32098
+ font-weight: 500;
32099
+ line-height: 19px;
32100
+ }
32101
+
32102
+ .settings-remote__empty span {
32103
+ color: var(--settings-muted);
32104
+ font-size: 12px;
32105
+ line-height: 18px;
32106
+ }
32107
+
32108
+ .settings-remote__list {
32109
+ display: grid;
32110
+ gap: 12px;
32111
+ }
32112
+
32113
+ .settings-remote__placeholder:empty,
32114
+ .settings-remote__list:empty {
32115
+ display: none;
32116
+ }
32117
+
32118
+ .settings-remote-card {
32119
+ min-width: 0;
32120
+ overflow: hidden;
32121
+ background: var(--settings-panel);
32122
+ border: 1px solid var(--settings-border);
32123
+ border-radius: 10px;
32124
+ }
32125
+
32126
+ .settings-remote-card__head {
32127
+ display: flex;
32128
+ align-items: center;
32129
+ gap: 12px;
32130
+ padding: 12px 10px 12px 14px;
32131
+ }
32132
+
32133
+ .settings-remote-card__identity {
32134
+ display: grid;
32135
+ flex: 1;
32136
+ min-width: 0;
32137
+ gap: 2px;
32138
+ }
32139
+
32140
+ .settings-remote-card__title {
32141
+ display: flex;
32142
+ align-items: center;
32143
+ min-width: 0;
32144
+ gap: 10px;
32145
+ }
32146
+
32147
+ .settings-remote-card h3 {
32148
+ min-width: 0;
32149
+ margin: 0;
32150
+ overflow: hidden;
32151
+ color: var(--settings-text);
32152
+ font-size: 13px;
32153
+ font-weight: 600;
32154
+ line-height: 20px;
32155
+ text-overflow: ellipsis;
32156
+ white-space: nowrap;
32157
+ }
32158
+
32159
+ .settings-remote-card__state {
32160
+ display: inline-flex;
32161
+ align-items: center;
32162
+ flex: none;
32163
+ gap: 6px;
32164
+ color: var(--settings-muted);
32165
+ font-size: 12px;
32166
+ line-height: 18px;
32167
+ }
32168
+
32169
+ .settings-remote-card__state::before {
32170
+ width: 7px;
32171
+ height: 7px;
32172
+ content: "";
32173
+ background: var(--settings-subtle);
32174
+ border-radius: 50%;
32175
+ }
32176
+
32177
+ .settings-remote-card__state[data-tone="success"]::before {
32178
+ background: var(--settings-success);
32179
+ }
32180
+
32181
+ .settings-remote-card__state[data-tone="danger"]::before {
32182
+ background: var(--settings-danger);
32183
+ }
32184
+
32185
+ .settings-remote-card__state[data-tone="progress"]::before {
32186
+ animation: settings-remote-pulse 1.2s ease-in-out infinite;
32187
+ }
32188
+
32189
+ .settings-remote-card__address {
32190
+ display: flex;
32191
+ align-items: center;
32192
+ min-width: 0;
32193
+ gap: 8px;
32194
+ overflow: hidden;
32195
+ color: var(--settings-muted);
32196
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
32197
+ font-size: 12px;
32198
+ line-height: 18px;
32199
+ text-overflow: ellipsis;
32200
+ white-space: nowrap;
32201
+ }
32202
+
32203
+ .settings-remote-card__address .settings-status-badge {
32204
+ padding: 0 6px;
32205
+ font-family: system-ui, sans-serif;
32206
+ }
32207
+
32208
+ .settings-remote-card__actions {
32209
+ flex-wrap: wrap;
32210
+ justify-content: flex-end;
32211
+ }
32212
+
32213
+ .settings-remote-card__remove:hover:not(:disabled) {
32214
+ color: var(--settings-danger);
32215
+ background: var(--settings-danger-bg);
32216
+ }
32217
+
32218
+ .settings-remote .settings-icon-button:disabled {
32219
+ cursor: not-allowed;
32220
+ opacity: 0.55;
32221
+ }
32222
+
32223
+ .settings-remote-card__detail {
32224
+ margin: -4px 14px 12px;
32225
+ font-size: 12px;
32226
+ line-height: 18px;
32227
+ overflow-wrap: anywhere;
32228
+ }
32229
+
32230
+ .settings-remote-card__service {
32231
+ display: flex;
32232
+ align-items: center;
32233
+ justify-content: space-between;
32234
+ gap: 12px;
32235
+ padding: 10px 10px 10px 14px;
32236
+ background: var(--settings-surface);
32237
+ border-top: 1px solid var(--settings-divider);
32238
+ }
32239
+
32240
+ .settings-remote-card__service-copy {
32241
+ display: grid;
32242
+ min-width: 0;
32243
+ gap: 2px;
32244
+ font-size: 12px;
32245
+ line-height: 18px;
32246
+ }
32247
+
32248
+ .settings-remote-card__service-title {
32249
+ color: var(--settings-text);
32250
+ font-weight: 500;
32251
+ font-variant-numeric: tabular-nums;
32252
+ }
32253
+
32254
+ .settings-remote-card__hint {
32255
+ display: flex;
32256
+ align-items: center;
32257
+ gap: 6px;
32258
+ margin: 0;
32259
+ color: var(--settings-muted);
32260
+ overflow-wrap: anywhere;
32261
+ }
32262
+
32263
+ .settings-remote [data-tone="success"] {
32264
+ color: var(--settings-success);
32265
+ }
32266
+
32267
+ .settings-remote [data-tone="warning"] {
32268
+ color: var(--settings-warning);
32269
+ }
32270
+
32271
+ .settings-remote [data-tone="danger"] {
32272
+ color: var(--settings-danger);
32273
+ }
32274
+
32275
+ .settings-remote-spinner {
32276
+ width: 12px;
32277
+ height: 12px;
32278
+ flex: none;
32279
+ border: 1.5px solid var(--settings-border);
32280
+ border-top-color: var(--settings-muted);
32281
+ border-radius: 50%;
32282
+ animation: settings-remote-spin 0.8s linear infinite;
32283
+ }
32284
+
32285
+ .settings-remote__refresh[aria-busy="true"] svg {
32286
+ animation: settings-remote-spin 0.8s linear infinite;
32287
+ }
32288
+
32289
+ .settings-remote-dialog .settings-credential-dialog__field small {
32290
+ color: var(--settings-subtle);
32291
+ font-size: 11px;
32292
+ line-height: 16px;
32293
+ }
32294
+
32295
+ .settings-remote-dialog input:disabled {
32296
+ color: var(--settings-muted);
32297
+ cursor: not-allowed;
32298
+ }
32299
+
32300
+ .settings-remote-dialog input:focus-visible {
32301
+ border-color: var(--settings-focus);
32302
+ outline-offset: -1px;
32303
+ }
32304
+
32305
+ @keyframes settings-remote-spin {
32306
+ to {
32307
+ transform: rotate(360deg);
32308
+ }
32309
+ }
32310
+
32311
+ @keyframes settings-remote-pulse {
32312
+ 50% {
32313
+ opacity: 0.3;
32314
+ }
32315
+ }
32316
+
32317
+ @container (max-width: 620px) {
32318
+ .settings-remote-card__head,
32319
+ .settings-remote-card__service {
32320
+ align-items: stretch;
32321
+ flex-direction: column;
32322
+ }
32323
+
32324
+ .settings-remote-card__actions {
32325
+ justify-content: flex-start;
32326
+ }
32327
+ }
32328
+
32329
+ @media (prefers-reduced-motion: reduce) {
32330
+ .settings-remote-spinner,
32331
+ .settings-remote__refresh svg,
32332
+ .settings-remote-card__state::before {
32333
+ animation: none !important;
32334
+ }
32335
+ }
30397
32336
  `;
30398
32337
 
30399
32338
  // src/settings/accounts.css
@@ -31139,7 +33078,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
31139
33078
  `;
31140
33079
 
31141
33080
  // src/settings/tailwind.css
31142
- var tailwind_default = '@layer properties, theme, base, components, utilities;\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-content:"";--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0}}}@layer theme{:root,:host{--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1)}}@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.relative{position:relative}.top-full{top:100%}.left-0{left:0}.z-20{z-index:20}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:0}.mx-2{margin-inline:calc(var(--spacing) * 2)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-7{margin-top:calc(var(--spacing) * 7)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-16{margin-top:calc(var(--spacing) * 16)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.ml-1{margin-left:var(--spacing)}.ml-\\[45px\\]{margin-left:45px}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.list-item{display:list-item}.table{display:table}.size-1\\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-8{height:calc(var(--spacing) * 8)}.min-h-6{min-height:calc(var(--spacing) * 6)}.min-h-\\[60px\\]{min-height:60px}.w-9{width:calc(var(--spacing) * 9)}.w-14{width:calc(var(--spacing) * 14)}.w-80{width:calc(var(--spacing) * 80)}.w-full{width:100%}.w-px{width:1px}.max-w-64{max-width:calc(var(--spacing) * 64)}.min-w-0{min-width:0}.flex-shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-help{cursor:help}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-disc{list-style-type:disc}.appearance-none{appearance:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.overflow-x-auto{overflow-x:auto}.rounded-\\[10px\\]{border-radius:10px}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-settings-border{border-color:var(--settings-border)}.border-settings-divider{border-color:var(--settings-divider)}.bg-settings-panel{background-color:var(--settings-panel)}.bg-settings-subtle{background-color:var(--settings-subtle)}.bg-settings-surface{background-color:var(--settings-surface)}.bg-settings-surface-hover{background-color:var(--settings-surface-hover)}.bg-transparent{background-color:#0000}.p-0{padding:0}.p-4{padding:calc(var(--spacing) * 4)}.\\!px-0{padding-inline:0!important}.px-1{padding-inline:var(--spacing)}.px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pl-4{padding-left:calc(var(--spacing) * 4)}.\\!text-right{text-align:right!important}.text-left{text-align:left}.text-right{text-align:right}.align-top{vertical-align:top}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\\[11px\\]{font-size:11px}.text-\\[13px\\]{font-size:13px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-7{--tw-leading:calc(var(--spacing) * 7);line-height:calc(var(--spacing) * 7)}.leading-\\[18px\\]{--tw-leading:18px;line-height:18px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.text-wrap{text-wrap:wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-pre-wrap{white-space:pre-wrap}.text-settings-muted{color:var(--settings-muted)}.text-settings-subtle{color:var(--settings-subtle)}.text-settings-text{color:var(--settings-text)}.lowercase{text-transform:lowercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.opacity-0{opacity:0}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\\[0_10px_30px_rgb\\(0_0_0\\/0\\.22\\)\\]{--tw-shadow:0 10px 30px var(--tw-shadow-color,#00000038);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring,.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-settings-border{--tw-ring-color:var(--settings-border)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.outline-none{--tw-outline-style:none;outline-style:none}.ring-inset{--tw-ring-inset:inset}.group-focus-within\\:visible:is(:where(.group):focus-within *){visibility:visible}.group-focus-within\\:opacity-100:is(:where(.group):focus-within *){opacity:1}@media (hover:hover){.group-hover\\:visible:is(:where(.group):hover *){visibility:visible}.group-hover\\:opacity-100:is(:where(.group):hover *){opacity:1}}.group-data-\\[state\\=failed\\]\\/status\\:bg-settings-danger:is(:where(.group\\/status)[data-state=failed] *){background-color:var(--settings-danger)}.group-data-\\[state\\=unavailable\\]\\/status\\:bg-settings-warning:is(:where(.group\\/status)[data-state=unavailable] *){background-color:var(--settings-warning)}.marker\\:text-settings-subtle ::marker{color:var(--settings-subtle)}.marker\\:text-settings-subtle::marker{color:var(--settings-subtle)}.marker\\:text-settings-subtle ::-webkit-details-marker{color:var(--settings-subtle)}.marker\\:text-settings-subtle::-webkit-details-marker{color:var(--settings-subtle)}.before\\:absolute:before{content:var(--tw-content);position:absolute}.before\\:top-0\\.5:before{content:var(--tw-content);top:calc(var(--spacing) * .5)}.before\\:left-0\\.5:before{content:var(--tw-content);left:calc(var(--spacing) * .5)}.before\\:size-4:before{content:var(--tw-content);width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.before\\:rounded-full:before{content:var(--tw-content);border-radius:3.40282e38px}.before\\:bg-white:before{content:var(--tw-content);background-color:#fff}.before\\:shadow-\\[0_1px_2px_rgb\\(0_0_0\\/0\\.3\\)\\]:before{content:var(--tw-content);--tw-shadow:0 1px 2px var(--tw-shadow-color,#0000004d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.before\\:transition-transform:before{content:var(--tw-content);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.first\\:border-t-0:first-child{border-top-style:var(--tw-border-style);border-top-width:0}.first-of-type\\:mt-0:first-of-type{margin-top:0}.checked\\:bg-settings-focus:checked{background-color:var(--settings-focus)}.checked\\:ring-transparent:checked{--tw-ring-color:transparent}.checked\\:before\\:translate-x-4:checked:before{content:var(--tw-content);--tw-translate-x:calc(var(--spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.focus-within\\:border-settings-focus:focus-within{border-color:var(--settings-focus)}@media (hover:hover){.hover\\:text-settings-text:hover{color:var(--settings-text)}}.focus-visible\\:text-settings-text:focus-visible{color:var(--settings-text)}.focus-visible\\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\\:outline-settings-focus:focus-visible{outline-color:var(--settings-focus)}.data-\\[invalid\\=true\\]\\:border-settings-danger[data-invalid=true]{border-color:var(--settings-danger)}.data-\\[invalid\\=true\\]\\:text-settings-danger[data-invalid=true],.data-\\[state\\=failed\\]\\:text-settings-danger[data-state=failed]{color:var(--settings-danger)}.data-\\[state\\=unavailable\\]\\:text-settings-warning[data-state=unavailable]{color:var(--settings-warning)}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@layer properties{\n *, ::before, ::after, ::backdrop{--tw-border-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-alpha:100%;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-offset-width:0;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-drop-shadow-alpha:100%;--tw-content:"";--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0}}\n';
33081
+ var tailwind_default = '@layer properties, theme, base, components, utilities;\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-content:"";--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0}}}@layer theme{:root,:host{--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1)}}@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.top-full{top:100%}.left-0{left:0}.z-20{z-index:20}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:0}.mx-2{margin-inline:calc(var(--spacing) * 2)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-7{margin-top:calc(var(--spacing) * 7)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-16{margin-top:calc(var(--spacing) * 16)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.ml-1{margin-left:var(--spacing)}.ml-\\[45px\\]{margin-left:45px}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.list-item{display:list-item}.table{display:table}.size-1\\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-8{height:calc(var(--spacing) * 8)}.min-h-6{min-height:calc(var(--spacing) * 6)}.min-h-\\[60px\\]{min-height:60px}.w-9{width:calc(var(--spacing) * 9)}.w-14{width:calc(var(--spacing) * 14)}.w-80{width:calc(var(--spacing) * 80)}.w-full{width:100%}.w-px{width:1px}.max-w-64{max-width:calc(var(--spacing) * 64)}.min-w-0{min-width:0}.flex-shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-help{cursor:help}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-disc{list-style-type:disc}.appearance-none{appearance:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.overflow-x-auto{overflow-x:auto}.rounded-\\[10px\\]{border-radius:10px}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-settings-border{border-color:var(--settings-border)}.border-settings-divider{border-color:var(--settings-divider)}.bg-settings-panel{background-color:var(--settings-panel)}.bg-settings-subtle{background-color:var(--settings-subtle)}.bg-settings-surface{background-color:var(--settings-surface)}.bg-settings-surface-hover{background-color:var(--settings-surface-hover)}.bg-transparent{background-color:#0000}.p-0{padding:0}.p-4{padding:calc(var(--spacing) * 4)}.\\!px-0{padding-inline:0!important}.px-1{padding-inline:var(--spacing)}.px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pl-4{padding-left:calc(var(--spacing) * 4)}.\\!text-right{text-align:right!important}.text-left{text-align:left}.text-right{text-align:right}.align-top{vertical-align:top}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\\[11px\\]{font-size:11px}.text-\\[13px\\]{font-size:13px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-7{--tw-leading:calc(var(--spacing) * 7);line-height:calc(var(--spacing) * 7)}.leading-\\[18px\\]{--tw-leading:18px;line-height:18px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.text-wrap{text-wrap:wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-pre-wrap{white-space:pre-wrap}.text-settings-muted{color:var(--settings-muted)}.text-settings-subtle{color:var(--settings-subtle)}.text-settings-text{color:var(--settings-text)}.lowercase{text-transform:lowercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.opacity-0{opacity:0}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\\[0_10px_30px_rgb\\(0_0_0\\/0\\.22\\)\\]{--tw-shadow:0 10px 30px var(--tw-shadow-color,#00000038);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring,.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-settings-border{--tw-ring-color:var(--settings-border)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.outline-none{--tw-outline-style:none;outline-style:none}.ring-inset{--tw-ring-inset:inset}.group-focus-within\\:visible:is(:where(.group):focus-within *){visibility:visible}.group-focus-within\\:opacity-100:is(:where(.group):focus-within *){opacity:1}@media (hover:hover){.group-hover\\:visible:is(:where(.group):hover *){visibility:visible}.group-hover\\:opacity-100:is(:where(.group):hover *){opacity:1}}.group-data-\\[state\\=failed\\]\\/status\\:bg-settings-danger:is(:where(.group\\/status)[data-state=failed] *){background-color:var(--settings-danger)}.group-data-\\[state\\=unavailable\\]\\/status\\:bg-settings-warning:is(:where(.group\\/status)[data-state=unavailable] *){background-color:var(--settings-warning)}.marker\\:text-settings-subtle ::marker{color:var(--settings-subtle)}.marker\\:text-settings-subtle::marker{color:var(--settings-subtle)}.marker\\:text-settings-subtle ::-webkit-details-marker{color:var(--settings-subtle)}.marker\\:text-settings-subtle::-webkit-details-marker{color:var(--settings-subtle)}.before\\:absolute:before{content:var(--tw-content);position:absolute}.before\\:top-0\\.5:before{content:var(--tw-content);top:calc(var(--spacing) * .5)}.before\\:left-0\\.5:before{content:var(--tw-content);left:calc(var(--spacing) * .5)}.before\\:size-4:before{content:var(--tw-content);width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.before\\:rounded-full:before{content:var(--tw-content);border-radius:3.40282e38px}.before\\:bg-white:before{content:var(--tw-content);background-color:#fff}.before\\:shadow-\\[0_1px_2px_rgb\\(0_0_0\\/0\\.3\\)\\]:before{content:var(--tw-content);--tw-shadow:0 1px 2px var(--tw-shadow-color,#0000004d);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.before\\:transition-transform:before{content:var(--tw-content);transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.first\\:border-t-0:first-child{border-top-style:var(--tw-border-style);border-top-width:0}.first-of-type\\:mt-0:first-of-type{margin-top:0}.checked\\:bg-settings-focus:checked{background-color:var(--settings-focus)}.checked\\:ring-transparent:checked{--tw-ring-color:transparent}.checked\\:before\\:translate-x-4:checked:before{content:var(--tw-content);--tw-translate-x:calc(var(--spacing) * 4);translate:var(--tw-translate-x) var(--tw-translate-y)}.focus-within\\:border-settings-focus:focus-within{border-color:var(--settings-focus)}@media (hover:hover){.hover\\:text-settings-text:hover{color:var(--settings-text)}}.focus-visible\\:text-settings-text:focus-visible{color:var(--settings-text)}.focus-visible\\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\\:outline-settings-focus:focus-visible{outline-color:var(--settings-focus)}.data-\\[invalid\\=true\\]\\:border-settings-danger[data-invalid=true]{border-color:var(--settings-danger)}.data-\\[invalid\\=true\\]\\:text-settings-danger[data-invalid=true],.data-\\[state\\=failed\\]\\:text-settings-danger[data-state=failed]{color:var(--settings-danger)}.data-\\[state\\=unavailable\\]\\:text-settings-warning[data-state=unavailable]{color:var(--settings-warning)}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@layer properties{\n *, ::before, ::after, ::backdrop{--tw-border-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-alpha:100%;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-offset-width:0;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-drop-shadow-alpha:100%;--tw-content:"";--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0}}\n';
31143
33082
 
31144
33083
  // src/settings/trigger.ts
31145
33084
  var SETTINGS_TRIGGER_ATTRIBUTE = "data-codexhost-settings-trigger";
@@ -31717,7 +33656,8 @@ ${accounts_default}`;
31717
33656
  await options.openImportedThread(threadId, signal);
31718
33657
  if (!disposed && !signal.aborted) shell?.close();
31719
33658
  },
31720
- options.getLoadedSessionsClient ?? (() => null)
33659
+ options.getLoadedSessionsClient ?? (() => null),
33660
+ options.getRemoteConnections ?? (() => null)
31721
33661
  );
31722
33662
  const nextShell = installRendererSettingsShell(definitions, messages, ownerWindow.document, {
31723
33663
  onOpenChange(open) {
@@ -32260,7 +34200,7 @@ ${accounts_default}`;
32260
34200
  openedFor = { node: trigger.node, start: trigger.start };
32261
34201
  options.onOpen?.(trigger.editor);
32262
34202
  }
32263
- const allTargets = options.readTargets();
34203
+ const allTargets = options.readTargets(trigger.editor);
32264
34204
  syncChipStyle(ownerDocument, allTargets, iconCache);
32265
34205
  const targets = filterDelegationTargets(allTargets, trigger.query);
32266
34206
  const source = options.readCommands(trigger.editor);
@@ -32438,7 +34378,8 @@ ${accounts_default}`;
32438
34378
  hermes: harnessIdSchema.parse("hermes"),
32439
34379
  qoder: harnessIdSchema.parse("qoder"),
32440
34380
  "qoder-cn": harnessIdSchema.parse("qoder-cn"),
32441
- "kimi-code": harnessIdSchema.parse("kimi-code")
34381
+ "kimi-code": harnessIdSchema.parse("kimi-code"),
34382
+ zcode: harnessIdSchema.parse("zcode")
32442
34383
  };
32443
34384
  var externalAgents = [
32444
34385
  "pi",
@@ -32455,7 +34396,8 @@ ${accounts_default}`;
32455
34396
  "hermes",
32456
34397
  "qoder",
32457
34398
  "qoder-cn",
32458
- "kimi-code"
34399
+ "kimi-code",
34400
+ "zcode"
32459
34401
  ];
32460
34402
  function isRetryableHarnessAvailability(availability, error51) {
32461
34403
  return availability !== void 0 && availability !== "ready" && availability !== "notInstalled" && error51?.retryable === true;
@@ -32477,8 +34419,13 @@ ${accounts_default}`;
32477
34419
  return state === "ready";
32478
34420
  }
32479
34421
  var rendererUsageRefreshDelays = [250, 500, 1e3, 2e3, 4e3, 8e3];
32480
- function refreshConnectionHosts(hostIds, refreshHost) {
32481
- return Promise.all([...hostIds].map((hostId) => refreshHost(hostId))).then(() => void 0);
34422
+ function refreshConnectionHosts(hostIds, refreshHost, pendingHost) {
34423
+ return Promise.all(
34424
+ [...hostIds].map(async (hostId) => {
34425
+ if (pendingHost) await pendingHost(hostId)?.catch(() => void 0);
34426
+ await refreshHost(hostId);
34427
+ })
34428
+ ).then(() => void 0);
32482
34429
  }
32483
34430
  function rendererUsageRefreshDelay(attempt) {
32484
34431
  const index = Math.max(0, Math.min(Math.trunc(attempt), rendererUsageRefreshDelays.length - 1));
@@ -32663,10 +34610,10 @@ ${accounts_default}`;
32663
34610
  ...permissionModeId ? { permissionModeId } : {}
32664
34611
  };
32665
34612
  }
32666
- if (inspection.harnessId === "kimi-code") {
34613
+ if (inspection.harnessId === "kimi-code" || inspection.harnessId === "zcode") {
32667
34614
  const route = decodeHarnessPluginRoute(inspection.transportModelId);
32668
34615
  if (!route || route.harnessId !== inspection.harnessId) {
32669
- throw new Error("Kimi Code Thread reported an incompatible transport Model");
34616
+ throw new Error(`${inspection.harnessId} Thread reported an incompatible transport Model`);
32670
34617
  }
32671
34618
  const model = inspection.effectiveModel ?? route.model;
32672
34619
  const thinkingOptionId = inspection.availableThinkingOptions !== void 0 ? selectableThinkingOptionId(inspection) : inspection.effectiveThinkingOptionId ?? route.thinkingOptionId;
@@ -32772,19 +34719,23 @@ ${accounts_default}`;
32772
34719
  let adapterDispose = null;
32773
34720
  let applyAdapterAgent = null;
32774
34721
  let modelControl = null;
32775
- const activeModelHostId = () => {
34722
+ const activeModelHostId = (composer) => {
32776
34723
  if (!modelControl) return null;
32777
- return modelControl.currentHostId ? modelControl.currentHostId() : "local";
34724
+ return modelControl.currentHostId ? modelControl.currentHostId(composer) : "local";
32778
34725
  };
32779
34726
  const controllerTarget = (target, hostId) => scopedComposerTarget(target, hostId);
32780
- let usageNotificationDispose = null;
34727
+ const usageSubscriptions = /* @__PURE__ */ new Map();
34728
+ const usageNotificationDispose = () => {
34729
+ for (const subscription of usageSubscriptions.values()) subscription.dispose();
34730
+ usageSubscriptions.clear();
34731
+ };
32781
34732
  const localAgentForSidebarThread = (input) => {
32782
34733
  for (const mounted of mountedByComposer.values()) {
32783
34734
  const target = mounted.modelTarget;
32784
34735
  const matchesDraft = target?.[0] === "default" && input.draftId !== null && target[1] === input.draftId;
32785
34736
  const matchesConversation = target?.[0] === "conversation" && input.threadId !== null && target[1] === input.threadId && mounted.ownershipStatus === "ready";
32786
34737
  if (!matchesDraft && !matchesConversation) continue;
32787
- const mountedHostId = modelControl?.currentHostId?.() ?? "local";
34738
+ const mountedHostId = activeModelHostId(mounted.composer);
32788
34739
  if (input.hostId !== mountedHostId) return null;
32789
34740
  return controller.get(mounted.composer).agent;
32790
34741
  }
@@ -32795,7 +34746,9 @@ ${accounts_default}`;
32795
34746
  getLocalAgent: localAgentForSidebarThread
32796
34747
  });
32797
34748
  let connectionDiagnostics = null;
34749
+ const remoteConnections = createRemoteConnectionsControl(window, modelClientForHost);
32798
34750
  const settingsLifecycle = installRendererSettingsLifecycle(window, {
34751
+ getRemoteConnections: () => remoteConnections,
32799
34752
  getUpdateClient: () => modelControl,
32800
34753
  getAccountClient: () => modelControl,
32801
34754
  getConnectionDiagnostics: () => connectionDiagnostics,
@@ -32843,7 +34796,8 @@ ${accounts_default}`;
32843
34796
  hermes: void 0,
32844
34797
  qoder: void 0,
32845
34798
  "qoder-cn": void 0,
32846
- "kimi-code": void 0
34799
+ "kimi-code": void 0,
34800
+ zcode: void 0
32847
34801
  },
32848
34802
  webUi: Object.fromEntries(
32849
34803
  externalAgents.map((agent) => [agent, false])
@@ -32924,6 +34878,9 @@ ${accounts_default}`;
32924
34878
  const title = status === "unsupported" ? rendererHarnessMessages(settingsLifecycle.locale).codexUsageGateUnavailable : "";
32925
34879
  if (mounted.control.root.title !== title) mounted.control.root.title = title;
32926
34880
  };
34881
+ const composerClient = (mounted) => mounted.hostId ? modelClientForHost(mounted.hostId) : null;
34882
+ const composerAvailability = (mounted) => mounted.hostId ? hostHarnessAvailabilityState(mounted.hostId).availability : {};
34883
+ const composerAdapterState = (mounted) => composerClient(mounted) ? "ready" : "installing";
32927
34884
  const renderMounted = (mounted) => {
32928
34885
  const accounts = composerCodexAccounts(mounted.composer);
32929
34886
  const currentCodexAccount = accounts?.accounts.find(
@@ -32932,9 +34889,9 @@ ${accounts_default}`;
32932
34889
  const externalSubmissionReady = renderComposerAgentControl(
32933
34890
  mounted.control,
32934
34891
  controller.get(mounted.composer),
32935
- adapterStatus.state,
34892
+ composerAdapterState(mounted),
32936
34893
  controller.isSwitching(mounted.composer) || mounted.ownershipStatus === "loading",
32937
- activeHarnessAvailabilityState().availability,
34894
+ composerAvailability(mounted),
32938
34895
  mounted.modelView,
32939
34896
  mounted.permissionModeView,
32940
34897
  mounted.usage,
@@ -32967,7 +34924,7 @@ ${accounts_default}`;
32967
34924
  const generation = ++mounted.commandRequestGeneration;
32968
34925
  const agent = controller.get(mounted.composer).agent;
32969
34926
  const threadId = threadIdFromComposerModelTarget(mounted.modelTarget);
32970
- const hostId = threadId ? mounted.hostId : activeModelHostId();
34927
+ const hostId = threadId ? mounted.hostId : activeModelHostId(mounted.composer);
32971
34928
  const requestControl = modelControl;
32972
34929
  const client = modelClientForHostFrom(requestControl, hostId);
32973
34930
  if (!keepCurrent || agent === "codex") mounted.control.harnessCommands.setCommands([]);
@@ -32977,7 +34934,7 @@ ${accounts_default}`;
32977
34934
  harnessId: externalHarnessIds[agent],
32978
34935
  ...hostId && draftWorkspaces.has(hostId) ? { cwd: draftWorkspaces.get(hostId) } : {}
32979
34936
  });
32980
- if (disposed || mountedByComposer.get(mounted.composer) !== mounted || mounted.commandRequestGeneration !== generation || requestControl !== modelControl || (threadIdFromComposerModelTarget(mounted.modelTarget) ? mounted.hostId : activeModelHostId()) !== hostId || controller.get(mounted.composer).agent !== agent)
34937
+ if (disposed || mountedByComposer.get(mounted.composer) !== mounted || mounted.commandRequestGeneration !== generation || requestControl !== modelControl || (threadIdFromComposerModelTarget(mounted.modelTarget) ? mounted.hostId : activeModelHostId(mounted.composer)) !== hostId || controller.get(mounted.composer).agent !== agent)
32981
34938
  return;
32982
34939
  mounted.control.harnessCommands.setCommands(
32983
34940
  catalog.commands,
@@ -32991,9 +34948,11 @@ ${accounts_default}`;
32991
34948
  const executeCommand = async (mounted, command) => {
32992
34949
  const threadId = threadIdFromComposerModelTarget(mounted.modelTarget);
32993
34950
  if (!threadId || !modelControl || controller.get(mounted.composer).agent === "codex") return;
34951
+ const client = composerClient(mounted);
34952
+ if (!client) return;
32994
34953
  mounted.control.harnessCommands.setExecuting(command.id);
32995
34954
  try {
32996
- await modelControl.executeThreadCommand({ threadId, commandId: command.id });
34955
+ await client.executeThreadCommand({ threadId, commandId: command.id });
32997
34956
  } catch (error51) {
32998
34957
  console.error(
32999
34958
  "codexhost Harness command failed",
@@ -33015,9 +34974,10 @@ ${accounts_default}`;
33015
34974
  }
33016
34975
  console.error("codexhost Harness command could not claim the current Composer editor");
33017
34976
  };
33018
- const applyThreadUsageUpdate = (update) => {
34977
+ const applyThreadUsageUpdate = (hostId, update) => {
33019
34978
  for (const mounted of mountedByComposer.values()) {
33020
- if (threadIdFromComposerModelTarget(mounted.modelTarget) !== update.threadId) continue;
34979
+ if (mounted.hostId !== hostId || threadIdFromComposerModelTarget(mounted.modelTarget) !== update.threadId)
34980
+ continue;
33021
34981
  mounted.usageRequestGeneration += 1;
33022
34982
  mounted.usage = update.usage;
33023
34983
  mounted.accountCredits = update.accountCredits ?? null;
@@ -33061,13 +35021,16 @@ ${accounts_default}`;
33061
35021
  renderMounted(mounted);
33062
35022
  return;
33063
35023
  }
35024
+ const hostId = mounted.hostId;
35025
+ const client = composerClient(mounted);
35026
+ if (!client) return;
33064
35027
  const generation = ++mounted.usageRequestGeneration;
33065
35028
  try {
33066
- const result = await modelControl.inspectThreadUsage({
35029
+ const result = await client.inspectThreadUsage({
33067
35030
  threadId,
33068
35031
  ...refresh ? { refresh } : {}
33069
35032
  });
33070
- if (disposed || mountedByComposer.get(mounted.composer) !== mounted || !mounted.composer.isConnected || mounted.usageRequestGeneration !== generation || threadIdFromComposerModelTarget(mounted.modelTarget) !== threadId || result.threadId !== threadId) {
35033
+ if (disposed || mountedByComposer.get(mounted.composer) !== mounted || !mounted.composer.isConnected || mounted.usageRequestGeneration !== generation || mounted.hostId !== hostId || composerClient(mounted) !== client || threadIdFromComposerModelTarget(mounted.modelTarget) !== threadId || result.threadId !== threadId) {
33071
35034
  return;
33072
35035
  }
33073
35036
  mounted.usage = result.usage;
@@ -33136,7 +35099,7 @@ ${accounts_default}`;
33136
35099
  return;
33137
35100
  }
33138
35101
  const requestModelControl = modelControl;
33139
- const requestHostId = activeModelHostId();
35102
+ const requestHostId = activeModelHostId(mounted.composer);
33140
35103
  const client = modelClientForHostFrom(requestModelControl, requestHostId);
33141
35104
  const generation = controller.beginOwnershipRequest(mounted.composer);
33142
35105
  const usageGeneration = mounted.usageRequestGeneration;
@@ -33146,7 +35109,7 @@ ${accounts_default}`;
33146
35109
  if (!client || !requestHostId) throw new Error("Thread ownership control is unavailable");
33147
35110
  mounted.hostId = requestHostId;
33148
35111
  const inspection = await client.inspectThread({ threadId });
33149
- if (!isCurrentOwnershipRequest(mounted, generation) || mountedByComposer.get(mounted.composer) !== mounted || threadIdFromComposerModelTarget(mounted.modelTarget) !== threadId || mounted.hostId !== requestHostId || modelControl !== requestModelControl || activeModelHostId() !== requestHostId) {
35112
+ if (!isCurrentOwnershipRequest(mounted, generation) || mountedByComposer.get(mounted.composer) !== mounted || threadIdFromComposerModelTarget(mounted.modelTarget) !== threadId || mounted.hostId !== requestHostId || modelControl !== requestModelControl || activeModelHostId(mounted.composer) !== requestHostId) {
33150
35113
  return;
33151
35114
  }
33152
35115
  const { agent, model, thinkingOptionId, permissionModeId } = restoredThreadOwnership(inspection);
@@ -33212,7 +35175,7 @@ ${accounts_default}`;
33212
35175
  );
33213
35176
  if (resolution === "none") return false;
33214
35177
  const previousTarget = mounted.modelTarget;
33215
- const nextHostId = activeModelHostId() ?? mounted.hostId;
35178
+ const nextHostId = activeModelHostId(mounted.composer);
33216
35179
  const nextControllerTarget = controllerTarget(currentTarget, nextHostId);
33217
35180
  mounted.modelTarget = currentTarget;
33218
35181
  mounted.hostId = nextHostId;
@@ -33251,7 +35214,7 @@ ${accounts_default}`;
33251
35214
  const agent = state.agent;
33252
35215
  const requestModelControl = modelControl;
33253
35216
  const isDraft = !threadIdFromComposerModelTarget(mounted.modelTarget);
33254
- const requestHostId = isDraft ? activeModelHostId() : mounted.hostId;
35217
+ const requestHostId = isDraft ? activeModelHostId(mounted.composer) : mounted.hostId;
33255
35218
  if (!requestHostId) {
33256
35219
  mounted.modelView = {
33257
35220
  status: "waitingForAdapter",
@@ -33265,7 +35228,7 @@ ${accounts_default}`;
33265
35228
  const availability = hostHarnessAvailabilityState(requestHostId).availability[agent];
33266
35229
  if (availability !== "ready" && availability !== "checking") {
33267
35230
  mounted.modelView = {
33268
- status: adapterStatus.state === "ready" ? "error" : "waitingForAdapter",
35231
+ status: composerAdapterState(mounted) === "ready" ? "error" : "waitingForAdapter",
33269
35232
  thinkingSelectionSupported: false,
33270
35233
  ...availability ? { error: `${agent} runtime is ${availability}` } : {}
33271
35234
  };
@@ -33274,12 +35237,12 @@ ${accounts_default}`;
33274
35237
  return;
33275
35238
  }
33276
35239
  mounted.modelView = {
33277
- status: adapterStatus.state === "ready" ? "loading" : "waitingForAdapter",
35240
+ status: composerAdapterState(mounted) === "ready" ? "loading" : "waitingForAdapter",
33278
35241
  thinkingSelectionSupported: false
33279
35242
  };
33280
35243
  mounted.permissionModeView = { status: "idle" };
33281
35244
  renderMounted(mounted);
33282
- if (adapterStatus.state !== "ready") return;
35245
+ if (!composerClient(mounted)) return;
33283
35246
  const generation = controller.beginModelRequest(mounted.composer);
33284
35247
  let catalogRequest;
33285
35248
  try {
@@ -33295,7 +35258,7 @@ ${accounts_default}`;
33295
35258
  const inspection = await client.inspectHarness({
33296
35259
  harnessId: externalHarnessIds[agent]
33297
35260
  });
33298
- if (!isCurrentModelRequest(mounted, generation) || controller.get(mounted.composer).agent !== agent || mounted.hostId !== requestHostId || modelControl !== requestModelControl || modelClientForHostFrom(requestModelControl, requestHostId) !== client || activeModelHostId() !== requestHostId) {
35261
+ if (!isCurrentModelRequest(mounted, generation) || controller.get(mounted.composer).agent !== agent || mounted.hostId !== requestHostId || modelControl !== requestModelControl || modelClientForHostFrom(requestModelControl, requestHostId) !== client || activeModelHostId(mounted.composer) !== requestHostId) {
33299
35262
  return;
33300
35263
  }
33301
35264
  if (inspection.status !== "ready") throw new Error(inspection.error.message);
@@ -33524,7 +35487,10 @@ ${accounts_default}`;
33524
35487
  if (!threadId) {
33525
35488
  throw new Error("External Thread identity is unavailable for Model selection");
33526
35489
  }
33527
- const state = await modelControl.selectThreadModel({ threadId, model: selected });
35490
+ const client = composerClient(mounted);
35491
+ if (!client) throw new Error("Thread Host is unavailable");
35492
+ const state = await client.selectThreadModel({ threadId, model: selected });
35493
+ if (composerClient(mounted) !== client) return;
33528
35494
  if (!isCurrentModelRequest(mounted, generation) || controller.get(mounted.composer).agent !== agent) {
33529
35495
  return;
33530
35496
  }
@@ -33648,10 +35614,13 @@ ${accounts_default}`;
33648
35614
  if (!threadId) {
33649
35615
  throw new Error("External Thread identity is unavailable for Permission Mode selection");
33650
35616
  }
33651
- const state = await modelControl.selectThreadPermissionMode({
35617
+ const client = composerClient(mounted);
35618
+ if (!client) throw new Error("Thread Host is unavailable");
35619
+ const state = await client.selectThreadPermissionMode({
33652
35620
  threadId,
33653
35621
  permissionModeId: selectedPermissionModeId
33654
35622
  });
35623
+ if (composerClient(mounted) !== client) return;
33655
35624
  if (!isCurrentModelRequest(mounted, generation) || controller.get(mounted.composer).agent !== agent) {
33656
35625
  return;
33657
35626
  }
@@ -33762,10 +35731,13 @@ ${accounts_default}`;
33762
35731
  if (!threadId || !modelControl) {
33763
35732
  throw new Error("External Thread identity is unavailable for Thinking selection");
33764
35733
  }
33765
- const state = await modelControl.selectThreadThinking({
35734
+ const client = composerClient(mounted);
35735
+ if (!client) throw new Error("Thread Host is unavailable");
35736
+ const state = await client.selectThreadThinking({
33766
35737
  threadId,
33767
35738
  thinkingOptionId: selectedThinkingOptionId
33768
35739
  });
35740
+ if (composerClient(mounted) !== client) return;
33769
35741
  if (!isCurrentModelRequest(mounted, generation) || controller.get(mounted.composer).agent !== agent) {
33770
35742
  return;
33771
35743
  }
@@ -33825,7 +35797,7 @@ ${accounts_default}`;
33825
35797
  }
33826
35798
  };
33827
35799
  const switchComposerAgent = async (mounted, agent) => {
33828
- if (agent !== "codex" && activeHarnessAvailabilityState().availability[agent] !== "ready") {
35800
+ if (agent !== "codex" && composerAvailability(mounted)[agent] !== "ready") {
33829
35801
  return false;
33830
35802
  }
33831
35803
  controller.clearPendingSubmission(mounted.composer);
@@ -33862,16 +35834,20 @@ ${accounts_default}`;
33862
35834
  }
33863
35835
  };
33864
35836
  const loadCodexAccounts = async () => {
33865
- const hostId = activeModelHostId();
33866
- const accounts = codexAccountsForHost(hostId);
33867
- if (!accounts) return;
33868
- await accounts.refresh();
33869
- if (disposed || codexAccountsForHost(hostId) !== accounts) return;
33870
- for (const mounted of mountedByComposer.values()) {
33871
- if (mounted.hostId !== hostId) continue;
33872
- renderMounted(mounted);
33873
- void refreshDraftCodexUsage(mounted);
33874
- }
35837
+ const hostIds = new Set([...mountedByComposer.values()].map((mounted) => mounted.hostId));
35838
+ await Promise.all(
35839
+ [...hostIds].map(async (hostId) => {
35840
+ const accounts = codexAccountsForHost(hostId);
35841
+ if (!accounts) return;
35842
+ await accounts.refresh();
35843
+ if (disposed || codexAccountsForHost(hostId) !== accounts) return;
35844
+ for (const mounted of mountedByComposer.values()) {
35845
+ if (mounted.hostId !== hostId) continue;
35846
+ renderMounted(mounted);
35847
+ void refreshDraftCodexUsage(mounted);
35848
+ }
35849
+ })
35850
+ );
33875
35851
  };
33876
35852
  const openInstallPage = (agent) => {
33877
35853
  const url2 = RENDERER_AGENT_INSTALL_URLS[agent];
@@ -33915,7 +35891,7 @@ ${accounts_default}`;
33915
35891
  state.requestGeneration += 1;
33916
35892
  state.request = null;
33917
35893
  }
33918
- if (force || activeModelHostId() === hostId) {
35894
+ if (force || [...mountedByComposer.values()].some((mounted) => mounted.hostId === hostId)) {
33919
35895
  for (const agent of externalAgents) {
33920
35896
  state.availability[agent] = "error";
33921
35897
  state.errors[agent] = {
@@ -33989,17 +35965,15 @@ ${accounts_default}`;
33989
35965
  state.errors[agent] = nextError;
33990
35966
  state.availability = { ...state.availability, [agent]: status };
33991
35967
  state.webUi = { ...state.webUi, [agent]: webUiAvailable };
33992
- if (hostId !== activeAvailabilityHostId) {
33993
- publishConnectionStatus();
33994
- return;
33995
- }
33996
35968
  for (const mounted of mountedByComposer.values()) {
35969
+ if (mounted.hostId !== hostId) continue;
33997
35970
  const composerState = controller.get(mounted.composer);
33998
- if (adapterStatus.state === "ready" && composerState.phase === "draft" && composerState.agent === agent && status !== "ready") {
35971
+ if (composerAdapterState(mounted) === "ready" && composerState.phase === "draft" && composerState.agent === agent && status !== "ready") {
33999
35972
  await switchComposerAgent(mounted, "codex");
34000
35973
  }
34001
35974
  }
34002
35975
  for (const mounted of mountedByComposer.values()) {
35976
+ if (mounted.hostId !== hostId) continue;
34003
35977
  const composerState = controller.get(mounted.composer);
34004
35978
  const pendingCatalog = catalogRequests.get(mounted);
34005
35979
  const loadingCurrentCatalog = pendingCatalog?.client === client && pendingCatalog.hostId === hostId && pendingCatalog.agent === agent && isCurrentModelRequest(mounted, pendingCatalog.generation);
@@ -34035,9 +36009,10 @@ ${accounts_default}`;
34035
36009
  );
34036
36010
  return promise2;
34037
36011
  }
34038
- const reloadMountedOwnershipForHost = (hostId) => {
36012
+ const reloadMountedOwnershipForHost = () => {
34039
36013
  for (const mounted of mountedByComposer.values()) {
34040
- if (mounted.hostId === hostId) continue;
36014
+ const hostId = activeModelHostId(mounted.composer);
36015
+ if (!hostId || mounted.hostId === hostId) continue;
34041
36016
  if (!threadIdFromComposerModelTarget(mounted.modelTarget)) {
34042
36017
  controller.clearPendingSubmission(mounted.composer);
34043
36018
  controller.beginModelRequest(mounted.composer);
@@ -34081,19 +36056,46 @@ ${accounts_default}`;
34081
36056
  sidebarAgentIcons.refresh();
34082
36057
  };
34083
36058
  function reconcileHarnessAvailabilityHost() {
36059
+ for (const hostId2 of modelControl?.knownHostIds?.() ?? []) {
36060
+ hostHarnessAvailabilityState(hostId2);
36061
+ }
36062
+ reloadMountedOwnershipForHost();
36063
+ for (const target of new Set([...mountedByComposer.values()].map((m) => m.hostId))) {
36064
+ if (!target) continue;
36065
+ const client = modelClientForHost(target);
36066
+ const previous = usageSubscriptions.get(target);
36067
+ if (previous?.client === client) continue;
36068
+ previous?.dispose();
36069
+ usageSubscriptions.delete(target);
36070
+ if (!client?.subscribeThreadUsage) continue;
36071
+ try {
36072
+ const dispose = client.subscribeThreadUsage((update) => {
36073
+ if (usageSubscriptions.get(target)?.client !== client) return;
36074
+ if (modelClientForHost(target) !== client) return;
36075
+ applyThreadUsageUpdate(target, update);
36076
+ });
36077
+ usageSubscriptions.set(target, { client, dispose });
36078
+ } catch {
36079
+ }
36080
+ }
34084
36081
  const hostId = activeModelHostId();
34085
36082
  if (!hostId || hostId === activeAvailabilityHostId && [...mountedByComposer.values()].every((mounted) => mounted.hostId !== null))
34086
36083
  return;
34087
36084
  activeAvailabilityHostId = hostId;
34088
36085
  hostHarnessAvailabilityState(hostId);
34089
- reloadMountedOwnershipForHost(hostId);
34090
36086
  publishConnectionStatus();
34091
36087
  for (const mounted of mountedByComposer.values()) renderMounted(mounted);
34092
36088
  void refreshHarnessAvailabilityForHost(hostId);
34093
36089
  }
34094
- const refreshHarnessAvailability = (refresh = false) => {
36090
+ const refreshHarnessAvailability = async (refresh = false) => {
34095
36091
  reconcileHarnessAvailabilityHost();
34096
- return refreshHarnessAvailabilityForHost(activeAvailabilityHostId, refresh);
36092
+ const hostIds = /* @__PURE__ */ new Set([activeAvailabilityHostId]);
36093
+ for (const mounted of mountedByComposer.values()) {
36094
+ if (mounted.hostId) hostIds.add(mounted.hostId);
36095
+ }
36096
+ await Promise.all(
36097
+ [...hostIds].map((hostId) => refreshHarnessAvailabilityForHost(hostId, refresh))
36098
+ );
34097
36099
  };
34098
36100
  connectionDiagnostics = {
34099
36101
  snapshot() {
@@ -34118,11 +36120,18 @@ ${accounts_default}`;
34118
36120
  })
34119
36121
  };
34120
36122
  },
34121
- refresh() {
36123
+ refresh(hostId) {
36124
+ if (hostId !== void 0) {
36125
+ return refreshConnectionHosts(
36126
+ [hostId],
36127
+ (target) => refreshHarnessAvailabilityForHost(target, true, false, true),
36128
+ (target) => hostHarnessAvailabilityState(target).request?.promise
36129
+ );
36130
+ }
34122
36131
  reconcileHarnessAvailabilityHost();
34123
36132
  return refreshConnectionHosts(
34124
36133
  harnessAvailabilityByHost.keys(),
34125
- (hostId) => refreshHarnessAvailabilityForHost(hostId, true, false, true)
36134
+ (target) => refreshHarnessAvailabilityForHost(target, true, false, true)
34126
36135
  );
34127
36136
  },
34128
36137
  async installation(hostId, agent, action) {
@@ -34169,7 +36178,7 @@ ${accounts_default}`;
34169
36178
  const sendButton = sendButtonWithin(composer) ?? allButtons.at(-1) ?? null;
34170
36179
  if (!sendButton) return;
34171
36180
  const modelTarget = findComposerModelTarget(composer);
34172
- const hostId = activeModelHostId();
36181
+ const hostId = activeModelHostId(composer);
34173
36182
  const editor = composer.querySelector(EDITOR_SELECTOR);
34174
36183
  if (!editor) return;
34175
36184
  const state = controller.mount(
@@ -34253,7 +36262,7 @@ ${accounts_default}`;
34253
36262
  for (const [target, replacement] of pendingReplacements) {
34254
36263
  const sourceState = controller.get(replacement.source.composer);
34255
36264
  const replacementTarget = findComposerModelTarget(target);
34256
- const replacementHostId = activeModelHostId() ?? replacement.source.hostId;
36265
+ const replacementHostId = activeModelHostId(target);
34257
36266
  if (!shouldTransferComposerState(
34258
36267
  replacement.sourceModelTarget,
34259
36268
  replacementTarget,
@@ -34296,8 +36305,8 @@ ${accounts_default}`;
34296
36305
  void refreshHarnessAvailabilityForHost("local");
34297
36306
  }
34298
36307
  reconcileHarnessAvailabilityHost();
34299
- if (externalAgents.some(
34300
- (agent) => activeHarnessAvailabilityState().availability[agent] === "checking"
36308
+ if ([...mountedByComposer.values()].some(
36309
+ (mounted) => externalAgents.some((agent) => composerAvailability(mounted)[agent] === "checking")
34301
36310
  )) {
34302
36311
  void refreshHarnessAvailability();
34303
36312
  }
@@ -34449,8 +36458,8 @@ ${accounts_default}`;
34449
36458
  scheduleScan(relevant.some(mutationMayChangeComposerTarget));
34450
36459
  });
34451
36460
  const onHostRouteChange = () => {
34452
- const hostId = activeModelHostId();
34453
36461
  for (const mounted of mountedByComposer.values()) {
36462
+ const hostId = activeModelHostId(mounted.composer);
34454
36463
  if (hostId && mounted.hostId === hostId && !threadIdFromComposerModelTarget(mounted.modelTarget) && controller.get(mounted.composer).agent !== "codex" && !controller.isSwitching(mounted.composer) && isExternalConfigurationStable(mounted.modelView, mounted.permissionModeView)) {
34455
36464
  applyComposerAgent(mounted.composer);
34456
36465
  }
@@ -34473,14 +36482,14 @@ ${accounts_default}`;
34473
36482
  sidebarAgentIcons.refresh();
34474
36483
  void refreshHarnessAvailabilityForHost("local");
34475
36484
  void refreshHarnessAvailability();
34476
- for (const mounted of mountedByComposer.values()) {
34477
- if (mounted.modelView.status === "waitingForAdapter" && mounted.composer.isConnected) {
34478
- applyComposerAgent(mounted.composer);
34479
- void loadExternalCatalog(mounted);
34480
- }
36485
+ }
36486
+ for (const mounted of mountedByComposer.values()) {
36487
+ if (mounted.modelView.status === "waitingForAdapter" && mounted.composer.isConnected && composerClient(mounted)) {
36488
+ applyComposerAgent(mounted.composer);
36489
+ void loadExternalCatalog(mounted);
34481
36490
  }
36491
+ renderMounted(mounted);
34482
36492
  }
34483
- for (const mounted of mountedByComposer.values()) renderMounted(mounted);
34484
36493
  };
34485
36494
  mutationObserver.observe(document.documentElement, {
34486
36495
  attributes: true,
@@ -34497,7 +36506,7 @@ ${accounts_default}`;
34497
36506
  reconcileHarnessAvailabilityHost();
34498
36507
  void loadCodexAccounts();
34499
36508
  for (const mounted of mountedByComposer.values()) {
34500
- if (mounted.hostId === activeModelHostId() && mounted.ownershipStatus === "error") {
36509
+ if (mounted.hostId === activeModelHostId(mounted.composer) && mounted.ownershipStatus === "error") {
34501
36510
  void loadThreadOwnership(mounted);
34502
36511
  }
34503
36512
  }
@@ -34505,8 +36514,9 @@ ${accounts_default}`;
34505
36514
  if (externalAgents.some((agent) => local.availability[agent] !== "ready")) {
34506
36515
  void refreshHarnessAvailabilityForHost("local", true);
34507
36516
  }
34508
- const active = activeHarnessAvailabilityState();
34509
- if (externalAgents.some((agent) => active.availability[agent] !== "ready")) {
36517
+ if ([...mountedByComposer.values()].some(
36518
+ (mounted) => externalAgents.some((agent) => composerAvailability(mounted)[agent] !== "ready")
36519
+ )) {
34510
36520
  void refreshHarnessAvailability(true);
34511
36521
  }
34512
36522
  };
@@ -34517,7 +36527,7 @@ ${accounts_default}`;
34517
36527
  if (typeof hostId !== "string" || typeof cwd !== "string" || cwd.length === 0) return;
34518
36528
  draftWorkspaces.set(hostId, cwd);
34519
36529
  for (const mounted of mountedByComposer.values()) {
34520
- if (!threadIdFromComposerModelTarget(mounted.modelTarget) && activeModelHostId() === hostId && controller.get(mounted.composer).agent !== "codex") {
36530
+ if (!threadIdFromComposerModelTarget(mounted.modelTarget) && activeModelHostId(mounted.composer) === hostId && controller.get(mounted.composer).agent !== "codex") {
34521
36531
  void refreshCommands(mounted, { keepCurrent: true });
34522
36532
  }
34523
36533
  }
@@ -34534,8 +36544,10 @@ ${accounts_default}`;
34534
36544
  void refreshCommands(mounted, { keepCurrent: true });
34535
36545
  }
34536
36546
  },
34537
- readTargets: () => {
34538
- const availability = activeHarnessAvailabilityState().availability;
36547
+ readTargets: (editor) => {
36548
+ const composer = composerForElement(editor);
36549
+ const mounted = composer ? mountedByComposer.get(composer) : void 0;
36550
+ const availability = mounted ? composerAvailability(mounted) : {};
34539
36551
  return enabledAgents.filter((agent) => agent === "codex" || availability[agent] === "ready").map((agent) => ({ agent, label: RENDERER_AGENT_LABELS[agent] }));
34540
36552
  },
34541
36553
  isComposerEditor: (editor) => {
@@ -34571,6 +36583,10 @@ ${accounts_default}`;
34571
36583
  (mounted) => mounted.composer.isConnected && mounted.control.root.isConnected
34572
36584
  );
34573
36585
  const api = {
36586
+ currentThreadId() {
36587
+ const mounted = connectedComposers();
36588
+ return mounted.length === 1 && mounted[0] ? threadIdFromComposerModelTarget(mounted[0].modelTarget) ?? null : null;
36589
+ },
34574
36590
  status() {
34575
36591
  const selections = connectedComposers().map((mounted) => ({
34576
36592
  composerId: mounted.composerId,
@@ -34604,17 +36620,11 @@ ${accounts_default}`;
34604
36620
  };
34605
36621
  },
34606
36622
  setAdapter(status, dispose, applyAgent, nextModelControl) {
34607
- usageNotificationDispose?.();
34608
- usageNotificationDispose = null;
36623
+ usageNotificationDispose();
34609
36624
  adapterDispose?.();
34610
36625
  adapterDispose = dispose ?? null;
34611
36626
  applyAdapterAgent = applyAgent ?? null;
34612
36627
  modelControl = nextModelControl ?? null;
34613
- try {
34614
- usageNotificationDispose = modelControl?.subscribeThreadUsage?.(applyThreadUsageUpdate) ?? null;
34615
- } catch {
34616
- usageNotificationDispose = null;
34617
- }
34618
36628
  adapterStatus = status;
34619
36629
  publishConnectionStatus();
34620
36630
  const installedModelControl = modelControl;
@@ -34643,7 +36653,7 @@ ${accounts_default}`;
34643
36653
  if (mounted) {
34644
36654
  const state = controller.get(mounted.composer);
34645
36655
  if (!threadIdFromComposerModelTarget(mounted.modelTarget)) {
34646
- mounted.hostId = activeModelHostId();
36656
+ mounted.hostId = activeModelHostId(mounted.composer);
34647
36657
  }
34648
36658
  if (threadIdFromComposerModelTarget(mounted.modelTarget) && mounted.ownershipStatus !== "ready") {
34649
36659
  void loadThreadOwnership(mounted);
@@ -34662,8 +36672,7 @@ ${accounts_default}`;
34662
36672
  dispose() {
34663
36673
  if (disposed) return;
34664
36674
  disposed = true;
34665
- usageNotificationDispose?.();
34666
- usageNotificationDispose = null;
36675
+ usageNotificationDispose();
34667
36676
  adapterDispose?.();
34668
36677
  adapterDispose = null;
34669
36678
  applyAdapterAgent = null;