@codexhost/cli-linux-arm64 0.3.5 → 0.4.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.
@@ -16559,6 +16559,86 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16559
16559
  ["path", { d: "M12 16h.01" }]
16560
16560
  ];
16561
16561
 
16562
+ // src/renderer-harness-localization.ts
16563
+ var ENGLISH_HARNESS_MESSAGES = Object.freeze({
16564
+ commands: "Commands",
16565
+ harnessCommands: "Harness commands",
16566
+ textArgument: "Text",
16567
+ permissionMode: "Permission mode",
16568
+ permissions: "Permissions",
16569
+ loadingPermissions: "Loading permissions...",
16570
+ selecting: "Selecting...",
16571
+ permissionsUnavailable: "Permissions unavailable"
16572
+ });
16573
+ var CHINESE_HARNESS_MESSAGES = Object.freeze({
16574
+ commands: "\u547D\u4EE4",
16575
+ harnessCommands: "Harness \u547D\u4EE4",
16576
+ textArgument: "\u6587\u672C",
16577
+ permissionMode: "\u6743\u9650\u6A21\u5F0F",
16578
+ permissions: "\u6743\u9650",
16579
+ loadingPermissions: "\u6B63\u5728\u52A0\u8F7D\u6743\u9650...",
16580
+ selecting: "\u6B63\u5728\u9009\u62E9...",
16581
+ permissionsUnavailable: "\u6743\u9650\u4E0D\u53EF\u7528"
16582
+ });
16583
+ var CHINESE_PERMISSION_MODE_LABELS = /* @__PURE__ */ new Map([
16584
+ ["Always ask", "\u59CB\u7EC8\u8BE2\u95EE"],
16585
+ ["Write", "\u5199\u5165"],
16586
+ ["Full access", "\u5B8C\u5168\u8BBF\u95EE"],
16587
+ ["Plan mode", "\u89C4\u5212\u6A21\u5F0F"],
16588
+ ["Default", "\u9ED8\u8BA4"],
16589
+ ["Accept edits", "\u63A5\u53D7\u7F16\u8F91"],
16590
+ ["Auto mode", "\u81EA\u52A8\u6A21\u5F0F"],
16591
+ ["Bypass permissions", "\u7ED5\u8FC7\u6743\u9650"],
16592
+ ["Ask", "\u8BE2\u95EE"],
16593
+ ["Auto", "\u81EA\u52A8"],
16594
+ ["Always approve", "\u59CB\u7EC8\u6279\u51C6"]
16595
+ ]);
16596
+ var CHINESE_PERMISSION_MODE_DESCRIPTIONS = /* @__PURE__ */ new Map([
16597
+ [
16598
+ "Automatically allow reads and ask before write or execution actions.",
16599
+ "\u81EA\u52A8\u5141\u8BB8\u8BFB\u53D6\uFF1B\u5199\u5165\u6216\u6267\u884C\u64CD\u4F5C\u524D\u8BE2\u95EE\u3002"
16600
+ ],
16601
+ [
16602
+ "Automatically allow reads and writes; ask before execution actions.",
16603
+ "\u81EA\u52A8\u5141\u8BB8\u8BFB\u53D6\u548C\u5199\u5165\uFF1B\u6267\u884C\u64CD\u4F5C\u524D\u8BE2\u95EE\u3002"
16604
+ ],
16605
+ ["Run all tool actions without approval prompts.", "\u65E0\u9700\u6279\u51C6\u63D0\u793A\u5373\u53EF\u8FD0\u884C\u6240\u6709\u5DE5\u5177\u64CD\u4F5C\u3002"],
16606
+ ["Analyze and plan without executing tools.", "\u4EC5\u5206\u6790\u548C\u89C4\u5212\uFF0C\u4E0D\u6267\u884C\u5DE5\u5177\u3002"],
16607
+ ["Ask before edits and other protected actions.", "\u7F16\u8F91\u548C\u5176\u4ED6\u53D7\u4FDD\u62A4\u64CD\u4F5C\u524D\u8BE2\u95EE\u3002"],
16608
+ ["Allow file edits and ask for other protected actions.", "\u5141\u8BB8\u6587\u4EF6\u7F16\u8F91\uFF1B\u5176\u4ED6\u53D7\u4FDD\u62A4\u64CD\u4F5C\u524D\u8BE2\u95EE\u3002"],
16609
+ ["Let Claude classify permission requests.", "\u7531 Claude \u5224\u65AD\u6743\u9650\u8BF7\u6C42\u3002"],
16610
+ ["Skip Claude Code permission checks.", "\u8DF3\u8FC7 Claude Code \u6743\u9650\u68C0\u67E5\u3002"],
16611
+ [
16612
+ "Use Grok Build's default interactive approval policy.",
16613
+ "\u4F7F\u7528 Grok Build \u7684\u9ED8\u8BA4\u4EA4\u4E92\u5F0F\u6279\u51C6\u7B56\u7565\u3002"
16614
+ ],
16615
+ ["Ask before protected tool actions.", "\u6267\u884C\u53D7\u4FDD\u62A4\u7684\u5DE5\u5177\u64CD\u4F5C\u524D\u8BE2\u95EE\u3002"],
16616
+ [
16617
+ "Let Grok Build decide which tool actions may run automatically.",
16618
+ "\u7531 Grok Build \u51B3\u5B9A\u54EA\u4E9B\u5DE5\u5177\u64CD\u4F5C\u53EF\u81EA\u52A8\u8FD0\u884C\u3002"
16619
+ ],
16620
+ ["Approve all tool actions without prompting.", "\u65E0\u9700\u63D0\u793A\u5373\u53EF\u6279\u51C6\u6240\u6709\u5DE5\u5177\u64CD\u4F5C\u3002"]
16621
+ ]);
16622
+ function rendererHarnessMessages(locale) {
16623
+ return locale === "zh-CN" ? CHINESE_HARNESS_MESSAGES : ENGLISH_HARNESS_MESSAGES;
16624
+ }
16625
+ function rendererHarnessCommandPresentation(command, locale) {
16626
+ if (locale === "zh-CN" && command.invocation === "/compact") {
16627
+ return { label: "\u538B\u7F29\u4E0A\u4E0B\u6587", description: "\u538B\u7F29\u5F53\u524D\u5BF9\u8BDD\u4E0A\u4E0B\u6587" };
16628
+ }
16629
+ return {
16630
+ label: command.label,
16631
+ description: command.description ?? command.label
16632
+ };
16633
+ }
16634
+ function rendererPermissionModePresentation(mode, locale) {
16635
+ if (locale !== "zh-CN") return { label: mode.label, description: mode.description };
16636
+ return {
16637
+ label: CHINESE_PERMISSION_MODE_LABELS.get(mode.label) ?? mode.label,
16638
+ description: mode.description === void 0 ? void 0 : CHINESE_PERMISSION_MODE_DESCRIPTIONS.get(mode.description) ?? mode.description
16639
+ };
16640
+ }
16641
+
16562
16642
  // src/renderer-permission-mode-picker.ts
16563
16643
  var MENU_CLASSES2 = "fixed z-50 overflow-hidden rounded-lg bg-token-dropdown-background/95 text-token-foreground shadow-lg ring-[0.5px] ring-token-border backdrop-blur-xl";
16564
16644
  var OPTION_CLASSES2 = "flex w-full cursor-interaction items-start gap-2 rounded-md px-2 py-2 text-left text-sm text-token-foreground outline-none enabled:hover:bg-token-list-hover-background enabled:active:bg-token-foreground/15 disabled:cursor-not-allowed disabled:opacity-40";
@@ -16585,13 +16665,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16585
16665
  if (view.status === "unsupported") return true;
16586
16666
  return (view.status === "ready" || view.status === "error") && selectedMode(view) !== void 0;
16587
16667
  }
16588
- function rendererPermissionModeLabel(view) {
16668
+ function rendererPermissionModeLabel(view, locale = "en") {
16589
16669
  const selected = selectedMode(view);
16590
- if (selected) return selected.label;
16591
- if (view.status === "loading") return "Loading permissions...";
16592
- if (view.status === "selecting") return "Selecting...";
16593
- if (view.status === "error") return "Permissions unavailable";
16594
- return "Permissions";
16670
+ if (selected) return rendererPermissionModePresentation(selected, locale).label;
16671
+ const messages = rendererHarnessMessages(locale);
16672
+ if (view.status === "loading") return messages.loadingPermissions;
16673
+ if (view.status === "selecting") return messages.selecting;
16674
+ if (view.status === "error") return messages.permissionsUnavailable;
16675
+ return messages.permissions;
16595
16676
  }
16596
16677
  function positionMenu(control) {
16597
16678
  const rect = control.trigger.getBoundingClientRect();
@@ -16606,7 +16687,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16606
16687
  control.trigger.style.maxWidth = "min(220px, 34vw)";
16607
16688
  control.trigger.style.letterSpacing = "0";
16608
16689
  }
16609
- function mountRendererPermissionModePicker(composerId, onSelect) {
16690
+ function mountRendererPermissionModePicker(composerId, onSelect, locale = "en") {
16610
16691
  ensureRendererTriggerChipStyle(document);
16611
16692
  const root = document.createElement("div");
16612
16693
  root.setAttribute("data-codexhost-permission-mode-control", composerId);
@@ -16638,7 +16719,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16638
16719
  const menu = document.createElement("div");
16639
16720
  menu.id = `${composerId}-permission-mode-menu`;
16640
16721
  menu.setAttribute("role", "menu");
16641
- menu.setAttribute("aria-label", "Permission mode");
16722
+ menu.setAttribute("aria-label", rendererHarnessMessages(locale).permissionMode);
16642
16723
  menu.setAttribute("popover", "auto");
16643
16724
  menu.hidden = typeof menu.showPopover !== "function";
16644
16725
  menu.className = MENU_CLASSES2;
@@ -16728,6 +16809,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16728
16809
  label,
16729
16810
  menu,
16730
16811
  options,
16812
+ locale,
16731
16813
  close,
16732
16814
  dispose() {
16733
16815
  close();
@@ -16744,10 +16826,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16744
16826
  syncRendererPermissionModeTriggerClass(control);
16745
16827
  return control;
16746
16828
  }
16747
- function rebuildOptions2(control, catalog) {
16829
+ function rebuildOptions2(control, catalog, locale) {
16748
16830
  control.options.clear();
16749
16831
  control.menu.replaceChildren();
16750
16832
  for (const mode of catalog.modes) {
16833
+ const presentation = rendererPermissionModePresentation(mode, locale);
16751
16834
  const button = document.createElement("button");
16752
16835
  button.type = "button";
16753
16836
  button.dataset.permissionModeId = mode.id;
@@ -16761,13 +16844,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16761
16844
  const copy = document.createElement("span");
16762
16845
  copy.className = "min-w-0 flex-1";
16763
16846
  const title = document.createElement("span");
16764
- title.textContent = mode.label;
16847
+ title.textContent = presentation.label;
16765
16848
  title.className = "block font-medium";
16766
16849
  title.style.letterSpacing = "0";
16767
16850
  copy.append(title);
16768
- if (mode.description) {
16851
+ if (presentation.description) {
16769
16852
  const description = document.createElement("span");
16770
- description.textContent = mode.description;
16853
+ description.textContent = presentation.description;
16771
16854
  description.className = "mt-0.5 block text-xs text-token-text-tertiary";
16772
16855
  description.style.lineHeight = "16px";
16773
16856
  description.style.letterSpacing = "0";
@@ -16782,7 +16865,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16782
16865
  control.menu.append(button);
16783
16866
  }
16784
16867
  }
16785
- function renderRendererPermissionModePicker(control, view, visible) {
16868
+ function renderRendererPermissionModePicker(control, view, visible, locale = control.locale) {
16869
+ control.locale = locale;
16870
+ const messages = rendererHarnessMessages(locale);
16871
+ control.menu.setAttribute("aria-label", messages.permissionMode);
16786
16872
  control.root.style.display = visible ? "inline-flex" : "none";
16787
16873
  control.root.style.alignItems = "center";
16788
16874
  control.root.style.alignSelf = "center";
@@ -16793,15 +16879,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16793
16879
  control.close();
16794
16880
  return;
16795
16881
  }
16796
- const signature = JSON.stringify(view.catalog);
16882
+ const signature = `${locale}:${JSON.stringify(view.catalog)}`;
16797
16883
  if (view.catalog && control.root.dataset.catalogSignature !== signature) {
16798
- rebuildOptions2(control, view.catalog);
16884
+ rebuildOptions2(control, view.catalog, locale);
16799
16885
  control.root.dataset.catalogSignature = signature;
16800
16886
  }
16801
- const label = rendererPermissionModeLabel(view);
16887
+ const label = rendererPermissionModeLabel(view, locale);
16802
16888
  if (control.label.textContent !== label) control.label.textContent = label;
16803
16889
  control.trigger.title = view.error ? `${label}: ${view.error}` : label;
16804
- control.trigger.setAttribute("aria-label", `Permission mode: ${label}`);
16890
+ control.trigger.setAttribute("aria-label", `${messages.permissionMode}: ${label}`);
16805
16891
  control.trigger.disabled = view.status === "loading" || view.status === "selecting" || view.catalog === void 0;
16806
16892
  control.trigger.setAttribute("aria-busy", String(view.status === "selecting"));
16807
16893
  if (control.trigger.disabled) control.close();
@@ -16814,6 +16900,51 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16814
16900
  }
16815
16901
 
16816
16902
  // src/renderer-usage-control.ts
16903
+ var ENGLISH_USAGE_MESSAGES = Object.freeze({
16904
+ usage: "Usage",
16905
+ context: "Context",
16906
+ latestCacheHit: "Latest cache hit",
16907
+ outputSpeed: "Output speed",
16908
+ cacheRead: "Cache read",
16909
+ cacheWrite: "Cache write",
16910
+ reasoning: "Reasoning",
16911
+ totalTokens: "Total tokens",
16912
+ inputOutput: "Input / output",
16913
+ fiveHourLimit: "5-hour limit",
16914
+ sevenDayLimit: "7-day limit",
16915
+ sessionCostEstimate: "Session cost estimate",
16916
+ threadUsage: "Thread Usage",
16917
+ threadUsageDetails: "Thread Usage details",
16918
+ contextSummary: "context",
16919
+ tokensSummary: "tokens",
16920
+ tokensPerSecond: "tok/s",
16921
+ fiveHourSummary: "5h",
16922
+ sevenDaySummary: "7d"
16923
+ });
16924
+ var CHINESE_USAGE_MESSAGES = Object.freeze({
16925
+ usage: "\u7528\u91CF",
16926
+ context: "\u4E0A\u4E0B\u6587",
16927
+ latestCacheHit: "\u6700\u8FD1\u7F13\u5B58\u547D\u4E2D\u7387",
16928
+ outputSpeed: "\u8F93\u51FA\u901F\u5EA6",
16929
+ cacheRead: "\u7F13\u5B58\u8BFB\u53D6",
16930
+ cacheWrite: "\u7F13\u5B58\u5199\u5165",
16931
+ reasoning: "\u63A8\u7406",
16932
+ totalTokens: "Token \u603B\u6570",
16933
+ inputOutput: "\u8F93\u5165 / \u8F93\u51FA",
16934
+ fiveHourLimit: "5 \u5C0F\u65F6\u9650\u989D",
16935
+ sevenDayLimit: "7 \u5929\u9650\u989D",
16936
+ sessionCostEstimate: "\u4F1A\u8BDD\u8D39\u7528\u4F30\u7B97",
16937
+ threadUsage: "\u5BF9\u8BDD\u7528\u91CF",
16938
+ threadUsageDetails: "\u5BF9\u8BDD\u7528\u91CF\u8BE6\u60C5",
16939
+ contextSummary: "\u4E0A\u4E0B\u6587",
16940
+ tokensSummary: "Token",
16941
+ tokensPerSecond: "Token/\u79D2",
16942
+ fiveHourSummary: "5 \u5C0F\u65F6",
16943
+ sevenDaySummary: "7 \u5929"
16944
+ });
16945
+ function rendererUsageMessages(locale) {
16946
+ return locale === "zh-CN" ? CHINESE_USAGE_MESSAGES : ENGLISH_USAGE_MESSAGES;
16947
+ }
16817
16948
  function decimal(value, fractionDigits) {
16818
16949
  return value.toFixed(fractionDigits).replace(/\.?0+$/u, "");
16819
16950
  }
@@ -16823,14 +16954,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16823
16954
  function formatRendererCost(value) {
16824
16955
  return `$${value.toFixed(3)}`;
16825
16956
  }
16826
- function formatRendererTokenRate(value) {
16827
- return `${decimal(value, 1)} tok/s`;
16957
+ function formatRendererTokenRate(value, locale = "en") {
16958
+ return `${decimal(value, 1)} ${rendererUsageMessages(locale).tokensPerSecond}`;
16828
16959
  }
16829
16960
  function formatRendererTokenCount(value) {
16830
16961
  const sign = value < 0 ? "-" : "";
16831
16962
  const absolute = Math.abs(value);
16832
16963
  if (absolute < 1e3) return `${sign}${Math.round(absolute)}`;
16833
- return `${sign}${decimal(absolute / 1e3, 1)}k`;
16964
+ if (absolute < 1e6) return `${sign}${decimal(absolute / 1e3, 1)}k`;
16965
+ if (absolute < 1e9) return `${sign}${decimal(absolute / 1e6, 1)}M`;
16966
+ return `${sign}${decimal(absolute / 1e9, 1)}B`;
16834
16967
  }
16835
16968
  function formatRendererCreditsPercent(value) {
16836
16969
  return `${decimal(value, 1)}%`;
@@ -16872,20 +17005,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16872
17005
  svg.append(track, fill);
16873
17006
  return svg;
16874
17007
  }
16875
- function formatRendererPlanReset(unixSeconds) {
17008
+ function formatRendererPlanReset(unixSeconds, locale) {
16876
17009
  const date5 = new Date(unixSeconds * 1e3);
16877
17010
  if (Number.isNaN(date5.getTime())) return "";
16878
- return date5.toLocaleString(void 0, {
17011
+ return date5.toLocaleString(locale === "zh-CN" ? "zh-CN" : void 0, {
16879
17012
  month: "long",
16880
17013
  day: "numeric",
16881
17014
  hour: "numeric",
16882
17015
  minute: "2-digit"
16883
17016
  });
16884
17017
  }
16885
- function formatRendererPlanWindow(usedPercent, resetsAtUnix) {
17018
+ function formatRendererPlanWindow(usedPercent, resetsAtUnix, locale) {
16886
17019
  const percent = formatRendererCreditsPercent(usedPercent);
16887
17020
  if (resetsAtUnix === void 0) return percent;
16888
- const reset = formatRendererPlanReset(resetsAtUnix);
17021
+ const reset = formatRendererPlanReset(resetsAtUnix, locale);
16889
17022
  return reset.length > 0 ? `${percent} \xB7 ${reset}` : percent;
16890
17023
  }
16891
17024
  function rendererUsageTriggerMaxWidth() {
@@ -16917,10 +17050,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16917
17050
  row.append(labelElement, valueElement);
16918
17051
  parent.append(row);
16919
17052
  }
16920
- function renderDetails(popover, usage) {
17053
+ function renderDetails(popover, usage, messages, locale) {
16921
17054
  popover.replaceChildren();
16922
17055
  const heading = document.createElement("div");
16923
- heading.textContent = "Usage";
17056
+ heading.textContent = messages.usage;
16924
17057
  heading.style.fontWeight = "600";
16925
17058
  heading.style.marginBottom = "6px";
16926
17059
  popover.append(heading);
@@ -16928,55 +17061,75 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16928
17061
  const contextPercent = usage.contextWindowTokens > 0 ? usage.contextUsedTokens / usage.contextWindowTokens * 100 : null;
16929
17062
  addDetailRow(
16930
17063
  popover,
16931
- "Context",
17064
+ messages.context,
16932
17065
  contextPercent === null ? `/${formatRendererTokenCount(usage.contextWindowTokens)}` : `${decimal(contextPercent, 1)}% / ${formatRendererTokenCount(usage.contextWindowTokens)}`
16933
17066
  );
16934
17067
  }
16935
17068
  if (usage?.cacheHitRatePercent !== void 0) {
16936
17069
  addDetailRow(
16937
17070
  popover,
16938
- "Latest cache hit",
17071
+ messages.latestCacheHit,
16939
17072
  formatRendererCacheHitRate(usage.cacheHitRatePercent)
16940
17073
  );
16941
17074
  }
16942
17075
  if (usage?.outputTokensPerSecond !== void 0) {
16943
- addDetailRow(popover, "Output speed", formatRendererTokenRate(usage.outputTokensPerSecond));
17076
+ addDetailRow(
17077
+ popover,
17078
+ messages.outputSpeed,
17079
+ formatRendererTokenRate(usage.outputTokensPerSecond, locale)
17080
+ );
16944
17081
  }
16945
17082
  if (usage?.cachedInputTokens !== void 0) {
16946
- addDetailRow(popover, "Cache read", formatRendererTokenCount(usage.cachedInputTokens));
17083
+ addDetailRow(popover, messages.cacheRead, formatRendererTokenCount(usage.cachedInputTokens));
16947
17084
  }
16948
17085
  if (usage?.cacheWriteInputTokens !== void 0) {
16949
- addDetailRow(popover, "Cache write", formatRendererTokenCount(usage.cacheWriteInputTokens));
17086
+ addDetailRow(
17087
+ popover,
17088
+ messages.cacheWrite,
17089
+ formatRendererTokenCount(usage.cacheWriteInputTokens)
17090
+ );
16950
17091
  }
16951
17092
  if (usage?.reasoningOutputTokens !== void 0) {
16952
- addDetailRow(popover, "Reasoning", formatRendererTokenCount(usage.reasoningOutputTokens));
17093
+ addDetailRow(
17094
+ popover,
17095
+ messages.reasoning,
17096
+ formatRendererTokenCount(usage.reasoningOutputTokens)
17097
+ );
16953
17098
  }
16954
17099
  if (usage?.totalTokens !== void 0) {
16955
- addDetailRow(popover, "Total tokens", formatRendererTokenCount(usage.totalTokens));
17100
+ addDetailRow(popover, messages.totalTokens, formatRendererTokenCount(usage.totalTokens));
16956
17101
  }
16957
17102
  if (usage?.inputTokens !== void 0 || usage?.outputTokens !== void 0) {
16958
17103
  addDetailRow(
16959
17104
  popover,
16960
- "Input / output",
17105
+ messages.inputOutput,
16961
17106
  `${formatRendererTokenCount(usage.inputTokens ?? 0)} / ${formatRendererTokenCount(usage.outputTokens ?? 0)}`
16962
17107
  );
16963
17108
  }
16964
17109
  if (usage?.planFiveHourUsedPercent !== void 0) {
16965
17110
  addDetailRow(
16966
17111
  popover,
16967
- "5-hour limit",
16968
- formatRendererPlanWindow(usage.planFiveHourUsedPercent, usage.planFiveHourResetsAtUnix)
17112
+ messages.fiveHourLimit,
17113
+ formatRendererPlanWindow(
17114
+ usage.planFiveHourUsedPercent,
17115
+ usage.planFiveHourResetsAtUnix,
17116
+ locale
17117
+ )
16969
17118
  );
16970
17119
  }
16971
17120
  if (usage?.planSevenDayUsedPercent !== void 0) {
16972
17121
  addDetailRow(
16973
17122
  popover,
16974
- "7-day limit",
16975
- formatRendererPlanWindow(usage.planSevenDayUsedPercent, usage.planSevenDayResetsAtUnix)
17123
+ messages.sevenDayLimit,
17124
+ formatRendererPlanWindow(
17125
+ usage.planSevenDayUsedPercent,
17126
+ usage.planSevenDayResetsAtUnix,
17127
+ locale
17128
+ )
16976
17129
  );
16977
17130
  }
16978
17131
  if (usage?.totalCostUsd !== void 0) {
16979
- addDetailRow(popover, "Session cost estimate", formatRendererCost(usage.totalCostUsd));
17132
+ addDetailRow(popover, messages.sessionCostEstimate, formatRendererCost(usage.totalCostUsd));
16980
17133
  }
16981
17134
  }
16982
17135
  function popoverIsOpen(popover) {
@@ -17017,8 +17170,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17017
17170
  if (control.trigger.getAttribute("aria-expanded") === "true") closePopover(control);
17018
17171
  else openPopover(control);
17019
17172
  }
17020
- function mountRendererUsageControl(composerId) {
17173
+ function mountRendererUsageControl(composerId, locale = "en") {
17021
17174
  ensureRendererTriggerChipStyle(document);
17175
+ const messages = rendererUsageMessages(locale);
17022
17176
  const root = document.createElement("div");
17023
17177
  root.dataset.codexhostUsageControl = composerId;
17024
17178
  root.className = "relative min-w-0";
@@ -17033,8 +17187,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17033
17187
  trigger.type = "button";
17034
17188
  trigger.setAttribute("aria-haspopup", "dialog");
17035
17189
  trigger.setAttribute("aria-expanded", "false");
17036
- trigger.setAttribute("aria-label", "Thread Usage");
17037
- trigger.title = "Thread Usage";
17190
+ trigger.setAttribute("aria-label", messages.threadUsage);
17191
+ trigger.title = messages.threadUsage;
17038
17192
  trigger.style.color = "var(--color-text-tertiary, #8f8f8f)";
17039
17193
  trigger.style.gap = "4px";
17040
17194
  trigger.style.width = "fit-content";
@@ -17056,7 +17210,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17056
17210
  const popover = document.createElement("div");
17057
17211
  popover.id = `${composerId}-usage-popover`;
17058
17212
  popover.setAttribute("role", "dialog");
17059
- popover.setAttribute("aria-label", "Thread Usage details");
17213
+ popover.setAttribute("aria-label", messages.threadUsageDetails);
17060
17214
  popover.setAttribute("popover", "auto");
17061
17215
  popover.hidden = typeof popover.showPopover !== "function";
17062
17216
  popover.style.position = "fixed";
@@ -17076,6 +17230,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17076
17230
  popover,
17077
17231
  anchor: null,
17078
17232
  label,
17233
+ locale,
17079
17234
  onOpen: null,
17080
17235
  dispose() {
17081
17236
  closePopover(control);
@@ -17130,7 +17285,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17130
17285
  document.body.append(popover);
17131
17286
  return control;
17132
17287
  }
17133
- function renderRendererUsageControl(control, usage) {
17288
+ function renderRendererUsageControl(control, usage, locale = control.locale) {
17289
+ control.locale = locale;
17290
+ const messages = rendererUsageMessages(locale);
17291
+ control.popover.setAttribute("aria-label", messages.threadUsageDetails);
17134
17292
  const cacheHitRatePercent = usage?.cacheHitRatePercent;
17135
17293
  const outputTokensPerSecond = usage?.outputTokensPerSecond;
17136
17294
  const totalCostUsd = usage?.totalCostUsd;
@@ -17145,34 +17303,34 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17145
17303
  }
17146
17304
  const summary = [
17147
17305
  cacheHitRatePercent !== void 0 ? formatRendererCacheHitRate(cacheHitRatePercent) : null,
17148
- outputTokensPerSecond !== void 0 ? formatRendererTokenRate(outputTokensPerSecond) : null,
17306
+ outputTokensPerSecond !== void 0 ? formatRendererTokenRate(outputTokensPerSecond, locale) : null,
17149
17307
  totalCostUsd !== void 0 ? formatRendererCost(totalCostUsd) : null
17150
17308
  ].filter((value) => value !== null);
17151
17309
  if (summary.length === 0 && hasContext && usage?.contextWindowTokens !== void 0 && usage.contextWindowTokens > 0) {
17152
17310
  summary.push(
17153
- `${decimal((usage.contextUsedTokens ?? 0) / usage.contextWindowTokens * 100, 1)}% context`
17311
+ `${decimal((usage.contextUsedTokens ?? 0) / usage.contextWindowTokens * 100, 1)}% ${messages.contextSummary}`
17154
17312
  );
17155
17313
  }
17156
17314
  if (summary.length === 0 && usage?.totalTokens !== void 0) {
17157
- summary.push(`${formatRendererTokenCount(usage.totalTokens)} tokens`);
17315
+ summary.push(`${formatRendererTokenCount(usage.totalTokens)} ${messages.tokensSummary}`);
17158
17316
  }
17159
17317
  if (summary.length === 0 && hasTokenUsage) {
17160
17318
  summary.push(
17161
- `${formatRendererTokenCount((usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0))} tokens`
17319
+ `${formatRendererTokenCount((usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0))} ${messages.tokensSummary}`
17162
17320
  );
17163
17321
  }
17164
17322
  if (summary.length === 0 && hasPlanLimit) {
17165
17323
  summary.push(
17166
- usage?.planFiveHourUsedPercent !== void 0 ? `5h ${formatRendererCreditsPercent(usage.planFiveHourUsedPercent)}` : `7d ${formatRendererCreditsPercent(usage?.planSevenDayUsedPercent ?? 0)}`
17324
+ usage?.planFiveHourUsedPercent !== void 0 ? `${messages.fiveHourSummary} ${formatRendererCreditsPercent(usage.planFiveHourUsedPercent)}` : `${messages.sevenDaySummary} ${formatRendererCreditsPercent(usage?.planSevenDayUsedPercent ?? 0)}`
17167
17325
  );
17168
17326
  }
17169
17327
  const compactSummary = summary.join(" \xB7 ");
17170
- const accessibleSummary = `Thread Usage: ${compactSummary}`;
17328
+ const accessibleSummary = `${messages.threadUsage}: ${compactSummary}`;
17171
17329
  control.trigger.style.maxWidth = rendererUsageTriggerMaxWidth();
17172
17330
  control.trigger.setAttribute("aria-label", accessibleSummary);
17173
17331
  control.trigger.title = accessibleSummary;
17174
17332
  control.label.textContent = compactSummary;
17175
- renderDetails(control.popover, usage);
17333
+ renderDetails(control.popover, usage, messages, locale);
17176
17334
  return true;
17177
17335
  }
17178
17336
 
@@ -17511,12 +17669,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17511
17669
  }
17512
17670
  return svg;
17513
17671
  }
17514
- function menuItem(ownerDocument, command, onSelect) {
17672
+ function menuItem(ownerDocument, command, locale, messages, onSelect) {
17673
+ const presentation = rendererHarnessCommandPresentation(command, locale);
17515
17674
  const item = ownerDocument.createElement("button");
17516
17675
  item.type = "button";
17517
17676
  item.setAttribute("role", "menuitem");
17518
17677
  item.setAttribute("data-command-id", command.id);
17519
- item.setAttribute("aria-label", `${command.invocation} ${command.label}`);
17678
+ item.setAttribute("aria-label", `${command.invocation} ${presentation.label}`);
17520
17679
  item.style.display = "flex";
17521
17680
  item.style.alignItems = "center";
17522
17681
  item.style.width = "100%";
@@ -17547,14 +17706,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17547
17706
  title.style.font = "600 13px/18px system-ui, sans-serif";
17548
17707
  title.style.whiteSpace = "nowrap";
17549
17708
  const description = ownerDocument.createElement("span");
17550
- description.textContent = command.description ?? command.label;
17709
+ description.textContent = presentation.description;
17551
17710
  description.style.overflow = "hidden";
17552
17711
  description.style.color = "rgba(127, 127, 127, 0.9)";
17553
17712
  description.style.font = "400 11px/16px system-ui, sans-serif";
17554
17713
  description.style.textOverflow = "ellipsis";
17555
17714
  description.style.whiteSpace = "nowrap";
17556
17715
  const hint = ownerDocument.createElement("span");
17557
- hint.textContent = command.argumentMode === "text" ? "Text" : "\u21B5";
17716
+ hint.textContent = command.argumentMode === "text" ? messages.textArgument : "\u21B5";
17558
17717
  hint.style.flex = "0 0 auto";
17559
17718
  hint.style.color = "rgba(127, 127, 127, 0.75)";
17560
17719
  hint.style.font = "400 11px/16px ui-monospace, SFMono-Regular, Menlo, monospace";
@@ -17580,8 +17739,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17580
17739
  button.style.cursor = "pointer";
17581
17740
  button.style.whiteSpace = "nowrap";
17582
17741
  }
17583
- function mountRendererHarnessCommandControl(parent, insertBefore, onCommandSelected) {
17742
+ function mountRendererHarnessCommandControl(parent, insertBefore, onCommandSelected, initialLocale = "en") {
17584
17743
  const ownerDocument = parent.ownerDocument;
17744
+ let locale = initialLocale;
17745
+ let messages = rendererHarnessMessages(locale);
17585
17746
  const root = ownerDocument.createElement("div");
17586
17747
  root.setAttribute(CONTROL_ATTRIBUTE2, "true");
17587
17748
  root.style.display = "inline-flex";
@@ -17591,15 +17752,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17591
17752
  trigger.type = "button";
17592
17753
  trigger.setAttribute("aria-haspopup", "menu");
17593
17754
  trigger.setAttribute("aria-expanded", "false");
17594
- trigger.setAttribute("aria-label", "Harness commands");
17595
- trigger.title = "Harness commands";
17755
+ trigger.setAttribute("aria-label", messages.harnessCommands);
17756
+ trigger.title = messages.harnessCommands;
17596
17757
  setButtonClass(trigger);
17597
17758
  trigger.append(commandIcon(ownerDocument));
17598
17759
  root.append(trigger);
17599
17760
  const menu = ownerDocument.createElement("div");
17600
17761
  menu.setAttribute(MENU_ATTRIBUTE, "true");
17601
17762
  menu.setAttribute("role", "menu");
17602
- menu.setAttribute("aria-label", "Harness commands");
17763
+ menu.setAttribute("aria-label", messages.harnessCommands);
17603
17764
  menu.hidden = true;
17604
17765
  menu.style.position = "fixed";
17605
17766
  menu.style.inset = "auto";
@@ -17677,12 +17838,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17677
17838
  const renderItems = () => {
17678
17839
  menu.replaceChildren();
17679
17840
  const header = ownerDocument.createElement("div");
17680
- header.textContent = "Commands";
17841
+ header.textContent = messages.commands;
17681
17842
  header.style.padding = "5px 8px 4px";
17682
17843
  header.style.color = "rgba(127, 127, 127, 0.75)";
17683
17844
  header.style.font = "600 11px/16px system-ui, sans-serif";
17684
17845
  menu.append(header);
17685
- items = commands.map((command) => menuItem(ownerDocument, command, () => select(command)));
17846
+ items = commands.map(
17847
+ (command) => menuItem(ownerDocument, command, locale, messages, () => select(command))
17848
+ );
17686
17849
  menu.append(...items);
17687
17850
  activeIndex = Math.min(activeIndex, Math.max(0, items.length - 1));
17688
17851
  if (executingCommandId !== null) {
@@ -17780,6 +17943,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17780
17943
  trigger.style.opacity = commandId !== null ? "0.65" : "1";
17781
17944
  syncTriggerBackground();
17782
17945
  },
17946
+ setLocale(nextLocale) {
17947
+ if (locale === nextLocale) return;
17948
+ locale = nextLocale;
17949
+ messages = rendererHarnessMessages(locale);
17950
+ trigger.setAttribute("aria-label", messages.harnessCommands);
17951
+ trigger.title = messages.harnessCommands;
17952
+ menu.setAttribute("aria-label", messages.harnessCommands);
17953
+ renderItems();
17954
+ },
17783
17955
  close,
17784
17956
  dispose() {
17785
17957
  if (disposed) return;
@@ -18138,9 +18310,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18138
18310
  refreshCreditsPlacement(control);
18139
18311
  return control;
18140
18312
  }
18141
- function renderComposerAgentControl(control, state, adapterState, switching, availability = {}, modelView = { status: "idle" }, permissionModeView = { status: "idle" }, usage = null, accountCredits = null) {
18313
+ function renderComposerAgentControl(control, state, adapterState, switching, availability = {}, modelView = { status: "idle" }, permissionModeView = { status: "idle" }, usage = null, accountCredits = null, locale = "en") {
18142
18314
  if (control.usage === null) {
18143
- control.usage = mountRendererUsageControl(control.composerId);
18315
+ control.usage = mountRendererUsageControl(control.composerId, locale);
18144
18316
  }
18145
18317
  const selectedModel = modelView.selected;
18146
18318
  const selectedCatalogModel = modelView.catalog?.models.find(
@@ -18176,9 +18348,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18176
18348
  renderRendererPermissionModePicker(
18177
18349
  control.permissionModePicker,
18178
18350
  permissionModeView,
18179
- permissionModeVisible
18351
+ permissionModeVisible,
18352
+ locale
18180
18353
  );
18181
- if (control.usage) renderRendererUsageControl(control.usage, usage);
18354
+ if (control.usage) renderRendererUsageControl(control.usage, usage, locale);
18355
+ control.harnessCommands.setLocale(locale);
18182
18356
  renderRendererCreditsControl(control.credits, accountCredits);
18183
18357
  }
18184
18358
  function disposeComposerAgentControl(control) {
@@ -18679,7 +18853,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18679
18853
  const parsed = hostThreadIdSchema.safeParse(params.threadId);
18680
18854
  return parsed.success ? parsed.data : null;
18681
18855
  }
18682
- function usageNotificationTarget(manager) {
18856
+ function notificationTarget(manager) {
18683
18857
  if (typeof manager.addNotificationCallback === "function") return manager;
18684
18858
  const nested = manager.requestClient;
18685
18859
  return nested && typeof nested.addNotificationCallback === "function" ? nested : null;
@@ -18780,7 +18954,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18780
18954
  },
18781
18955
  inspectThreadUsage,
18782
18956
  subscribeThreadUsage(listener) {
18783
- const notifications = usageNotificationTarget(manager);
18957
+ const notifications = notificationTarget(manager);
18784
18958
  if (!notifications?.addNotificationCallback) {
18785
18959
  throw new Error("Renderer Usage notification callback is unavailable");
18786
18960
  }
@@ -18956,19 +19130,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18956
19130
  ...thinking?.success ? { thinkingOptionId: thinking.data } : {}
18957
19131
  };
18958
19132
  }
18959
- function deepSeekHarnessTransportModelId(model) {
18960
- if (!model) return DEEPSEEK_HARNESS_TRANSPORT_MODEL_ID;
18961
- return `${DEEPSEEK_HARNESS_TRANSPORT_MODEL_PREFIX}${harnessModelRefSchema.parse(model).id}`;
19133
+ function deepSeekHarnessTransportModelId(model, permissionModeId) {
19134
+ if (!model) {
19135
+ if (permissionModeId) {
19136
+ throw new Error("DeepSeek Harness transport Permission Mode requires a Model Ref");
19137
+ }
19138
+ return DEEPSEEK_HARNESS_TRANSPORT_MODEL_ID;
19139
+ }
19140
+ const parsedPermissionModeId = permissionModeId ? harnessPermissionModeIdSchema.parse(permissionModeId) : void 0;
19141
+ return `${DEEPSEEK_HARNESS_TRANSPORT_MODEL_PREFIX}${harnessModelRefSchema.parse(model).id}${parsedPermissionModeId ? `@${parsedPermissionModeId}` : ""}`;
18962
19142
  }
18963
19143
  function decodeDeepSeekHarnessTransportModelId(value) {
18964
19144
  if (value === DEEPSEEK_HARNESS_TRANSPORT_MODEL_ID) return {};
18965
19145
  if (typeof value !== "string" || !value.startsWith(DEEPSEEK_HARNESS_TRANSPORT_MODEL_PREFIX)) {
18966
19146
  return null;
18967
19147
  }
18968
- const model = harnessModelRefSchema.safeParse({
18969
- id: value.slice(DEEPSEEK_HARNESS_TRANSPORT_MODEL_PREFIX.length)
18970
- });
18971
- return model.success ? { model: model.data } : null;
19148
+ const components = value.slice(DEEPSEEK_HARNESS_TRANSPORT_MODEL_PREFIX.length).split("@");
19149
+ if (components.length < 1 || components.length > 2) return null;
19150
+ const [modelId, permissionModeId] = components;
19151
+ if (components.length === 2 && !permissionModeId) return null;
19152
+ const model = harnessModelRefSchema.safeParse({ id: modelId });
19153
+ if (!model.success) return null;
19154
+ const permissionMode = permissionModeId ? harnessPermissionModeIdSchema.safeParse(permissionModeId) : null;
19155
+ if (permissionMode && !permissionMode.success) return null;
19156
+ return {
19157
+ model: model.data,
19158
+ ...permissionMode?.success ? { permissionModeId: permissionMode.data } : {}
19159
+ };
18972
19160
  }
18973
19161
  function decodePiTransportModelId(value) {
18974
19162
  if (value === PI_TRANSPORT_MODEL_ID) return {};
@@ -19145,12 +19333,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19145
19333
  const hostId = target.getHostId?.() ?? bridge.hostId;
19146
19334
  return typeof hostId === "string" && hostId.length > 0 ? hostId : null;
19147
19335
  }
19336
+ function isRendererRequestTarget(value) {
19337
+ if (!isRecord5(value) || typeof value.sendRequest !== "function") return false;
19338
+ return isCurrentRequestBridge(value.requestClient ?? value);
19339
+ }
19340
+ function hasPolicyRequestTarget(policy) {
19341
+ return "requestTarget" in policy;
19342
+ }
19343
+ function exactRendererRequestTarget(policy) {
19344
+ if (typeof policy.requestTarget !== "function") return null;
19345
+ try {
19346
+ const target = policy.requestTarget();
19347
+ if (!isRendererRequestTarget(target) || prewarmTargetHostId(target) !== policy.hostId) {
19348
+ return null;
19349
+ }
19350
+ return [target];
19351
+ } catch {
19352
+ return null;
19353
+ }
19354
+ }
19148
19355
  function rendererRequestTargetsForHost(targets, hostId) {
19149
19356
  const matching = targets.filter((target) => prewarmTargetHostId(target) === hostId);
19150
19357
  return matching.length === 1 ? matching : null;
19151
19358
  }
19152
19359
  function activeRendererDraftPrewarmTargets(policy, targets) {
19153
19360
  if (!isDraftPrewarmPolicyReady(policy)) return null;
19361
+ if (hasPolicyRequestTarget(policy)) return exactRendererRequestTarget(policy);
19154
19362
  return rendererRequestTargetsForHost(targets, policy.hostId);
19155
19363
  }
19156
19364
  function resolveRendererRequestRoute(policy, discoveredTargets, previous) {
@@ -19158,13 +19366,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19158
19366
  if (isDraftPrewarmPolicyReady(policy) && activeTargets) {
19159
19367
  return { policy, targets: activeTargets };
19160
19368
  }
19369
+ if (isDraftPrewarmPolicyReady(policy) && hasPolicyRequestTarget(policy)) return null;
19161
19370
  return discoveredTargets.length === 0 && isDraftPrewarmPolicyReady(policy) && previous?.policy === policy ? previous : null;
19162
19371
  }
19163
19372
  function createRendererRequestRouteResolver(readPolicy, discoverTargets) {
19164
19373
  let route = null;
19165
19374
  return {
19166
19375
  resolve() {
19167
- route = resolveRendererRequestRoute(readPolicy(), discoverTargets(), route);
19376
+ const policy = readPolicy();
19377
+ const discoveredTargets = isDraftPrewarmPolicyReady(policy) && hasPolicyRequestTarget(policy) ? [] : discoverTargets();
19378
+ route = resolveRendererRequestRoute(policy, discoveredTargets, route);
19168
19379
  return route;
19169
19380
  },
19170
19381
  clear() {
@@ -19185,7 +19396,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19185
19396
  }
19186
19397
  }
19187
19398
  function modelSelectionForAgent(officialSelection, reasoningEffort, agent, model, thinkingOptionId, permissionModeId) {
19188
- const transportModelId = agent === "pi" ? piTransportModelId(model, thinkingOptionId) : agent === "claude-code" ? claudeTransportModelId(model, permissionModeId, thinkingOptionId) : agent === "deepseek-harness" ? deepSeekHarnessTransportModelId(model) : agent === "grok" ? grokTransportModelId(model, permissionModeId, thinkingOptionId) : agent === "omp" ? ompTransportModelId(model, thinkingOptionId) : transportModelIdForAgent(agent);
19399
+ const transportModelId = agent === "pi" ? piTransportModelId(model, thinkingOptionId) : agent === "claude-code" ? claudeTransportModelId(model, permissionModeId, thinkingOptionId) : agent === "deepseek-harness" ? deepSeekHarnessTransportModelId(model, permissionModeId) : agent === "grok" ? grokTransportModelId(model, permissionModeId, thinkingOptionId) : agent === "omp" ? ompTransportModelId(model, thinkingOptionId) : transportModelIdForAgent(agent);
19189
19400
  return transportModelId ? { model: transportModelId, reasoningEffort } : officialSelection;
19190
19401
  }
19191
19402
  function installCurrentRendererAdapter() {
@@ -19215,7 +19426,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19215
19426
  () => window.__codexhostDraftPrewarmPolicyV1,
19216
19427
  () => findActivePrewarmTargets(document)
19217
19428
  );
19218
- const currentRequestRoute = () => requestRouteResolver.resolve();
19219
19429
  const clientsByTarget = /* @__PURE__ */ new WeakMap();
19220
19430
  const modelClientForTargets = (targets) => {
19221
19431
  const target = targets[0];
@@ -19226,8 +19436,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19226
19436
  if (client) clientsByTarget.set(target, client);
19227
19437
  return client;
19228
19438
  };
19439
+ let activeRoutePolicy = null;
19440
+ let activeRouteClient = null;
19441
+ const syncActiveRoute = (route) => {
19442
+ const policy = route?.policy ?? null;
19443
+ const client = route ? modelClientForTargets(route.targets) : null;
19444
+ if (activeRoutePolicy === policy && activeRouteClient === client) return client;
19445
+ activeRoutePolicy = policy;
19446
+ activeRouteClient = client;
19447
+ return client;
19448
+ };
19449
+ const currentRequestRoute = () => {
19450
+ const route = requestRouteResolver.resolve();
19451
+ syncActiveRoute(route);
19452
+ return route;
19453
+ };
19229
19454
  const currentModelClient = () => {
19230
- const client = modelClientForTargets(currentRequestRoute()?.targets ?? []);
19455
+ const client = currentRequestRoute() ? activeRouteClient : null;
19231
19456
  if (!client) throw new Error("Renderer Model request manager is unavailable");
19232
19457
  usageSubscription.connect(client);
19233
19458
  return client;
@@ -19235,6 +19460,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19235
19460
  const modelControl = Object.freeze({
19236
19461
  currentHostId: () => currentRequestRoute()?.policy.hostId ?? null,
19237
19462
  clientForHost(hostId) {
19463
+ const route = currentRequestRoute();
19464
+ if (route?.policy.hostId === hostId) return modelClientForTargets(route.targets);
19465
+ const policy = window.__codexhostDraftPrewarmPolicyV1;
19466
+ if (isDraftPrewarmPolicyReady(policy) && hasPolicyRequestTarget(policy)) return null;
19238
19467
  const targets = rendererRequestTargetsForHost(findActivePrewarmTargets(document), hostId);
19239
19468
  return modelClientForTargets(targets ?? []);
19240
19469
  },
@@ -19268,25 +19497,76 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19268
19497
  });
19269
19498
  let routingPolicy = null;
19270
19499
  let policyTimer = null;
19500
+ let policyRecaptureObserver = null;
19501
+ let hasCapturedRoutingPolicy = false;
19502
+ let selectedRoutingPolicy = null;
19503
+ let selectedCarrier = null;
19271
19504
  let desiredCarrier = null;
19505
+ const stopPolicyCapture = () => {
19506
+ if (policyTimer === null) return;
19507
+ window.clearInterval(policyTimer);
19508
+ policyTimer = null;
19509
+ };
19510
+ const stopPolicyRecapture = () => {
19511
+ policyRecaptureObserver?.disconnect();
19512
+ policyRecaptureObserver = null;
19513
+ };
19272
19514
  const captureRoutingPolicy = () => {
19273
19515
  const route = currentRequestRoute();
19274
19516
  if (!route) return false;
19275
19517
  routingPolicy = route.policy;
19276
- routingPolicy.select(desiredCarrier);
19277
- if (policyTimer !== null) {
19278
- window.clearInterval(policyTimer);
19279
- policyTimer = null;
19518
+ stopPolicyRecapture();
19519
+ if (selectedRoutingPolicy !== routingPolicy || selectedCarrier !== desiredCarrier) {
19520
+ try {
19521
+ routingPolicy.select(desiredCarrier);
19522
+ } catch {
19523
+ updateStatus("installing", "draft-routing-policy-unavailable", null);
19524
+ return false;
19525
+ }
19526
+ selectedRoutingPolicy = routingPolicy;
19527
+ selectedCarrier = desiredCarrier;
19280
19528
  }
19529
+ hasCapturedRoutingPolicy = true;
19530
+ stopPolicyCapture();
19281
19531
  updateStatus("ready", "ready", "request-bridge");
19282
19532
  return true;
19283
19533
  };
19534
+ const startPolicyCapture = () => {
19535
+ stopPolicyCapture();
19536
+ policyTimer = window.setInterval(captureRoutingPolicy, DRAFT_PREWARM_POLICY_POLL_INTERVAL_MS);
19537
+ };
19538
+ const startPolicyRecapture = () => {
19539
+ stopPolicyRecapture();
19540
+ policyRecaptureObserver = new MutationObserver(() => {
19541
+ captureRoutingPolicy();
19542
+ });
19543
+ policyRecaptureObserver.observe(document.documentElement, {
19544
+ attributes: true,
19545
+ attributeFilter: ["hidden", "aria-hidden", "data-codex-composer-root"],
19546
+ characterData: true,
19547
+ childList: true,
19548
+ subtree: true
19549
+ });
19550
+ };
19284
19551
  if (!captureRoutingPolicy()) {
19285
19552
  updateStatus("installing", "draft-routing-policy-unavailable", null);
19286
- policyTimer = window.setInterval(captureRoutingPolicy, DRAFT_PREWARM_POLICY_POLL_INTERVAL_MS);
19553
+ const policy = window.__codexhostDraftPrewarmPolicyV1;
19554
+ if (!isDraftPrewarmPolicyReady(policy) || !hasPolicyRequestTarget(policy)) {
19555
+ startPolicyCapture();
19556
+ }
19287
19557
  }
19288
19558
  const handleRoutingPolicyChange = () => {
19289
- captureRoutingPolicy();
19559
+ stopPolicyRecapture();
19560
+ if (captureRoutingPolicy()) return;
19561
+ const policy = window.__codexhostDraftPrewarmPolicyV1;
19562
+ if (isDraftPrewarmPolicyReady(policy) && hasPolicyRequestTarget(policy)) {
19563
+ stopPolicyCapture();
19564
+ }
19565
+ if (!hasCapturedRoutingPolicy) return;
19566
+ updateStatus("installing", "draft-routing-policy-unavailable", null);
19567
+ if (isDraftPrewarmPolicyReady(policy) && !hasPolicyRequestTarget(policy)) {
19568
+ startPolicyRecapture();
19569
+ }
19290
19570
  };
19291
19571
  window.addEventListener("codexhost:draft-prewarm-policy-changed", handleRoutingPolicyChange);
19292
19572
  const applyAgent = (agent, model, thinkingOptionId, permissionModeId) => {
@@ -19305,9 +19585,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19305
19585
  const route = currentRequestRoute();
19306
19586
  if (!route) return false;
19307
19587
  routingPolicy = route.policy;
19308
- if (route.policy.select(desiredCarrier)) {
19309
- modelUpdates += 1;
19310
- liveStatus.modelUpdates = modelUpdates;
19588
+ try {
19589
+ if (route.policy.select(desiredCarrier)) {
19590
+ modelUpdates += 1;
19591
+ liveStatus.modelUpdates = modelUpdates;
19592
+ }
19593
+ selectedRoutingPolicy = route.policy;
19594
+ selectedCarrier = desiredCarrier;
19595
+ } catch {
19596
+ updateStatus("installing", "draft-routing-policy-unavailable", null);
19597
+ return false;
19311
19598
  }
19312
19599
  return true;
19313
19600
  };
@@ -19318,16 +19605,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19318
19605
  dispose() {
19319
19606
  if (disposed) return;
19320
19607
  disposed = true;
19321
- if (policyTimer !== null) window.clearInterval(policyTimer);
19608
+ stopPolicyCapture();
19609
+ stopPolicyRecapture();
19322
19610
  window.removeEventListener(
19323
19611
  "codexhost:draft-prewarm-policy-changed",
19324
19612
  handleRoutingPolicyChange
19325
19613
  );
19326
- routingPolicy?.select(null);
19614
+ const activeRoutingPolicy = routingPolicy;
19327
19615
  routingPolicy = null;
19328
19616
  requestRouteResolver.clear();
19329
- forkControl.dispose();
19330
- usageSubscription.dispose();
19617
+ const cleanups = [
19618
+ () => activeRoutingPolicy?.select(null),
19619
+ () => syncActiveRoute(null),
19620
+ () => forkControl.dispose(),
19621
+ () => usageSubscription.dispose()
19622
+ ];
19623
+ for (const cleanup of cleanups) {
19624
+ try {
19625
+ cleanup();
19626
+ } catch {
19627
+ }
19628
+ }
19331
19629
  }
19332
19630
  };
19333
19631
  }
@@ -19650,11 +19948,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19650
19948
  close: "Close settings",
19651
19949
  starOnGitHub: "Give us a Star~",
19652
19950
  sectionsLabel: "Settings sections",
19951
+ generalSection: "General",
19952
+ otherSection: "Other",
19653
19953
  pageUnavailable: "Page unavailable",
19654
19954
  inDevelopment: "In development",
19655
19955
  notAvailable: "Not available",
19656
19956
  runtimeCapabilityNotInstalled: "This runtime capability is not installed yet.",
19657
- connectionsDescription: "Inspect the adapter and each Agent runtime used by the picker. Failed checks keep their error details here.",
19957
+ connectionsDescription: "View runtime status by Host. Select an item to inspect details or complete its setup.",
19658
19958
  connectionAdapter: "Renderer adapter",
19659
19959
  connectionHosts: "Hosts",
19660
19960
  connectionLocalHost: "Local",
@@ -19663,9 +19963,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19663
19963
  connectionReason: "Reason",
19664
19964
  connectionRefresh: "Run connection diagnostics",
19665
19965
  connectionRefreshing: "Running diagnostics...",
19666
- connectionViewDetails: "View details",
19966
+ connectionViewError: "View error",
19667
19967
  connectionCopyDetails: "Copy diagnostics",
19668
- connectionCopyAll: "Copy all diagnostics",
19669
19968
  connectionCopied: "Copied",
19670
19969
  connectionCopyFailed: "Copy failed",
19671
19970
  connectionErrorCode: "Error code",
@@ -19682,6 +19981,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19682
19981
  connectionStatusError: "Error",
19683
19982
  connectionStatusInstalling: "Installing",
19684
19983
  connectionStatusUnsupported: "Unsupported",
19984
+ connectionComponent: "Component",
19985
+ connectionStatus: "Status",
19986
+ connectionHostsScrollLeft: "Show previous Hosts",
19987
+ connectionHostsScrollRight: "Show more Hosts",
19988
+ connectionOpenInstallation: "Open official installation page",
19989
+ connectionInstall: "Install",
19990
+ connectionInstallDescription: "This Harness was not detected. Follow its official installation guide, then return here and run the check again.",
19991
+ connectionErrorTitle: "Connection check failed",
19992
+ connectionErrorLog: "Error log",
19993
+ connectionOpenIssue: "Open GitHub Issue",
19994
+ connectionIssueDescription: "Copy the error log and include the Host and reproduction steps when reporting the issue.",
19995
+ connectionReadyDescription: "This component is available on the selected Host.",
19996
+ connectionUnavailableDescription: "This component is not currently available on the selected Host.",
19997
+ enabled: "Enabled",
19998
+ disabled: "Disabled",
19685
19999
  openSettings: "Open codexhost settings",
19686
20000
  settingsButtonTitle: "codexhost settings",
19687
20001
  settingsUnavailableTitle: "codexhost settings unavailable",
@@ -19694,7 +20008,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19694
20008
  updateLatestVersion: "Latest version",
19695
20009
  updateUpToDate: "You are up to date.",
19696
20010
  updateAvailable: "A new version is available.",
19697
- updateAndRestart: "Update and restart",
20011
+ updateWindowsManualRequired: "Automatic updates are unavailable on Windows. Update manually below.",
20012
+ updateAndRestart: "Update",
19698
20013
  updateChecking: "Checking for updates...",
19699
20014
  updateDownloading: "Downloading update...",
19700
20015
  updatePreparing: "Preparing update...",
@@ -19707,13 +20022,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19707
20022
  updateFailed: "Update failed.",
19708
20023
  updateRetry: "Retry",
19709
20024
  updateManualNpmDescription: "To update manually, quit codexhost and run this command:",
20025
+ updateWindowsNpmDescription: "Automatic updates are unavailable on Windows. Quit codexhost and run this command in a terminal:",
20026
+ updateWindowsInstallerDescription: "Automatic updates are unavailable on Windows. Download and run the installer for this system.",
20027
+ updateManualTitle: "Manual update",
20028
+ updateManualFallbackDescription: "The automatic update did not complete. Run this command in a terminal instead, then quit Codex and relaunch it with codexhost.",
20029
+ updateCopyCommand: "Copy",
20030
+ updateCommandCopied: "Copied",
20031
+ updateCopyFailed: "Copy failed",
19710
20032
  updateDownloadFromReleases: "Download from GitHub Releases",
20033
+ updateDownloadWindowsInstaller: "Download Windows installer",
20034
+ aboutTagline: "Run Pi and other Harnesses in Codex Desktop",
20035
+ aboutParagraphs: Object.freeze([
20036
+ "We believe Codex Desktop offers the best desktop development experience available today.",
20037
+ "But Codex is not the only excellent Agent Harness. Some developers prefer Claude Code or Pi Agent.",
20038
+ "codexhost lets you choose the Agent that actually executes tasks inside Codex Desktop, while preserving the native Codex experience and enabling them to collaborate."
20039
+ ]),
20040
+ aboutOpenSource: "codexhost is an open-source project. The source code is available at:",
20041
+ aboutStarCallout: "\u2B50 If this project helps you, please give us a Star! \u2B50",
20042
+ aboutRepository: "Open-source repository",
19711
20043
  pageLabels: Object.freeze({
19712
20044
  connections: "Connections",
19713
- "model-pool": "Model Pool",
19714
- routes: "Routes",
19715
- gateway: "Gateway",
19716
- updates: "Updates"
20045
+ updates: "Updates",
20046
+ about: "About"
19717
20047
  })
19718
20048
  });
19719
20049
  var CHINESE_MESSAGES = Object.freeze({
@@ -19722,11 +20052,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19722
20052
  close: "\u5173\u95ED\u8BBE\u7F6E",
19723
20053
  starOnGitHub: "\u70B9\u4E2A Star~",
19724
20054
  sectionsLabel: "\u8BBE\u7F6E\u5206\u7C7B",
20055
+ generalSection: "\u901A\u7528",
20056
+ otherSection: "\u5176\u4ED6",
19725
20057
  pageUnavailable: "\u9875\u9762\u4E0D\u53EF\u7528",
19726
20058
  inDevelopment: "\u5F00\u53D1\u4E2D",
19727
20059
  notAvailable: "\u6682\u4E0D\u53EF\u7528",
19728
20060
  runtimeCapabilityNotInstalled: "\u8FD0\u884C\u65F6\u5C1A\u672A\u5B89\u88C5\u8BE5\u9879\u80FD\u529B\uFF0C\u56E0\u6B64\u6682\u4E0D\u53EF\u7528\u3002",
19729
- connectionsDescription: "\u67E5\u770B\u9002\u914D\u5668\u548C Agent \u8FD0\u884C\u65F6\u7684\u771F\u5B9E\u68C0\u67E5\u7ED3\u679C\u3002\u5931\u8D25\u68C0\u67E5\u4F1A\u4FDD\u7559\u9519\u8BEF\u8BE6\u60C5\uFF0C\u65B9\u4FBF\u6392\u67E5\u65E0\u6CD5\u9009\u62E9\u7684\u95EE\u9898\u3002",
20061
+ connectionsDescription: "\u6309 Host \u67E5\u770B\u8FD0\u884C\u65F6\u72B6\u6001\u3002\u9009\u62E9\u4E00\u9879\uFF0C\u5728\u53F3\u4FA7\u68C0\u67E5\u8BE6\u60C5\u6216\u5B8C\u6210\u914D\u7F6E\u3002",
19730
20062
  connectionAdapter: "Renderer \u9002\u914D\u5668",
19731
20063
  connectionHosts: "Host \u5217\u8868",
19732
20064
  connectionLocalHost: "\u672C\u5730",
@@ -19735,9 +20067,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19735
20067
  connectionReason: "\u539F\u56E0",
19736
20068
  connectionRefresh: "\u91CD\u65B0\u8BCA\u65AD\u8FDE\u63A5",
19737
20069
  connectionRefreshing: "\u6B63\u5728\u8BCA\u65AD...",
19738
- connectionViewDetails: "\u67E5\u770B\u8BE6\u60C5",
20070
+ connectionViewError: "\u67E5\u770B\u9519\u8BEF",
19739
20071
  connectionCopyDetails: "\u590D\u5236\u8BCA\u65AD\u4FE1\u606F",
19740
- connectionCopyAll: "\u590D\u5236\u5168\u90E8\u8BCA\u65AD\u4FE1\u606F",
19741
20072
  connectionCopied: "\u5DF2\u590D\u5236",
19742
20073
  connectionCopyFailed: "\u590D\u5236\u5931\u8D25",
19743
20074
  connectionErrorCode: "\u9519\u8BEF\u7801",
@@ -19754,6 +20085,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19754
20085
  connectionStatusError: "\u9519\u8BEF",
19755
20086
  connectionStatusInstalling: "\u5B89\u88C5\u4E2D",
19756
20087
  connectionStatusUnsupported: "\u4E0D\u652F\u6301",
20088
+ connectionComponent: "\u7EC4\u4EF6",
20089
+ connectionStatus: "\u72B6\u6001",
20090
+ connectionHostsScrollLeft: "\u67E5\u770B\u524D\u9762\u7684 Host",
20091
+ connectionHostsScrollRight: "\u67E5\u770B\u66F4\u591A Host",
20092
+ connectionOpenInstallation: "\u524D\u5F80\u5B98\u65B9\u5B89\u88C5\u9875\u9762",
20093
+ connectionInstall: "\u5B89\u88C5",
20094
+ connectionInstallDescription: "\u5C1A\u672A\u68C0\u6D4B\u5230\u8BE5 Harness\u3002\u8BF7\u6309\u7167\u5B98\u65B9\u5B89\u88C5\u6307\u5357\u5B8C\u6210\u5B89\u88C5\uFF0C\u7136\u540E\u8FD4\u56DE\u6B64\u9875\u9762\u91CD\u65B0\u68C0\u67E5\u3002",
20095
+ connectionErrorTitle: "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25",
20096
+ connectionErrorLog: "\u9519\u8BEF\u65E5\u5FD7",
20097
+ connectionOpenIssue: "\u63D0\u4EA4 GitHub Issue",
20098
+ connectionIssueDescription: "\u63D0\u4EA4\u524D\u8BF7\u590D\u5236\u9519\u8BEF\u65E5\u5FD7\uFF0C\u5E76\u5728 Issue \u4E2D\u8BF4\u660E\u5F53\u524D Host \u4E0E\u590D\u73B0\u6B65\u9AA4\u3002",
20099
+ connectionReadyDescription: "\u8BE5\u7EC4\u4EF6\u5728\u5F53\u524D Host \u4E0A\u53EF\u7528\u3002",
20100
+ connectionUnavailableDescription: "\u8BE5\u7EC4\u4EF6\u5F53\u524D\u65E0\u6CD5\u5728\u6240\u9009 Host \u4E0A\u4F7F\u7528\u3002",
20101
+ enabled: "\u5DF2\u5F00\u542F",
20102
+ disabled: "\u5DF2\u5173\u95ED",
19757
20103
  openSettings: "\u6253\u5F00 codexhost \u8BBE\u7F6E",
19758
20104
  settingsButtonTitle: "codexhost \u8BBE\u7F6E",
19759
20105
  settingsUnavailableTitle: "codexhost \u8BBE\u7F6E\u4E0D\u53EF\u7528",
@@ -19766,7 +20112,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19766
20112
  updateLatestVersion: "\u6700\u65B0\u7248\u672C",
19767
20113
  updateUpToDate: "\u5F53\u524D\u5DF2\u662F\u6700\u65B0\u7248\u672C\u3002",
19768
20114
  updateAvailable: "\u6709\u65B0\u7248\u672C\u53EF\u7528\u3002",
19769
- updateAndRestart: "\u66F4\u65B0\u5E76\u91CD\u542F",
20115
+ updateWindowsManualRequired: "Windows \u6682\u4E0D\u652F\u6301\u81EA\u52A8\u66F4\u65B0\uFF0C\u8BF7\u5728\u4E0B\u65B9\u624B\u52A8\u66F4\u65B0\u3002",
20116
+ updateAndRestart: "\u66F4\u65B0",
19770
20117
  updateChecking: "\u6B63\u5728\u68C0\u67E5\u66F4\u65B0...",
19771
20118
  updateDownloading: "\u6B63\u5728\u4E0B\u8F7D\u66F4\u65B0...",
19772
20119
  updatePreparing: "\u6B63\u5728\u51C6\u5907\u66F4\u65B0...",
@@ -19779,13 +20126,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19779
20126
  updateFailed: "\u66F4\u65B0\u5931\u8D25\u3002",
19780
20127
  updateRetry: "\u91CD\u8BD5",
19781
20128
  updateManualNpmDescription: "\u5982\u9700\u624B\u52A8\u66F4\u65B0\uFF0C\u8BF7\u5728\u7EC8\u7AEF\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u3002\u66F4\u65B0\u5B8C\u6210\u540E\uFF0C\u8BF7\u9000\u51FA Codex \u5E76\u901A\u8FC7 codexhost \u91CD\u65B0\u542F\u52A8\u3002",
20129
+ updateWindowsNpmDescription: "Windows \u6682\u4E0D\u652F\u6301\u81EA\u52A8\u66F4\u65B0\u3002\u8BF7\u9000\u51FA codexhost\uFF0C\u5728\u7EC8\u7AEF\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u5B8C\u6210\u66F4\u65B0\u3002",
20130
+ updateWindowsInstallerDescription: "Windows \u6682\u4E0D\u652F\u6301\u81EA\u52A8\u66F4\u65B0\u3002\u8BF7\u4E0B\u8F7D\u5E76\u8FD0\u884C\u9002\u7528\u4E8E\u5F53\u524D\u7CFB\u7EDF\u7684\u5B89\u88C5\u5305\u3002",
20131
+ updateManualTitle: "\u624B\u52A8\u66F4\u65B0",
20132
+ updateManualFallbackDescription: "\u81EA\u52A8\u66F4\u65B0\u672A\u80FD\u5B8C\u6210\uFF0C\u8BF7\u6539\u7528\u4E0B\u5217\u547D\u4EE4\u5728\u7EC8\u7AEF\u624B\u52A8\u66F4\u65B0\u3002\u5B8C\u6210\u540E\u8BF7\u9000\u51FA Codex \u5E76\u901A\u8FC7 codexhost \u91CD\u65B0\u542F\u52A8\u3002",
20133
+ updateCopyCommand: "\u590D\u5236",
20134
+ updateCommandCopied: "\u5DF2\u590D\u5236",
20135
+ updateCopyFailed: "\u590D\u5236\u5931\u8D25",
19782
20136
  updateDownloadFromReleases: "\u524D\u5F80 GitHub Releases \u4E0B\u8F7D",
20137
+ updateDownloadWindowsInstaller: "\u4E0B\u8F7D Windows \u5B89\u88C5\u5305",
20138
+ aboutTagline: "\u5728 Codex Desktop \u4E2D\u8FD0\u884C Pi \u548C\u5176\u4ED6 Harness",
20139
+ aboutParagraphs: Object.freeze([
20140
+ "\u6211\u4EEC\u8BA4\u4E3A Codex Desktop \u63D0\u4F9B\u4E86\u76EE\u524D\u6700\u597D\u7684\u684C\u9762\u5F00\u53D1\u4EA4\u4E92\u4F53\u9A8C\u3002",
20141
+ "\u4F46 Codex \u5E76\u4E0D\u662F\u552F\u4E00\u4F18\u79C0\u7684 Agent Harness\uFF0C\u4E5F\u6709\u4EBA\u504F\u597D Claude Code \u548C Pi Agent\u3002",
20142
+ "codexhost \u8BA9\u4F60\u5728 Codex Desktop \u4E2D\u9009\u62E9\u771F\u6B63\u6267\u884C\u4EFB\u52A1\u7684 Agent\uFF0C\u540C\u65F6\u4FDD\u7559 Codex \u7684\u539F\u751F\u4F53\u9A8C\uFF0C\u5E76\u8BA9\u5B83\u4EEC\u534F\u4F5C\u5B8C\u6210\u4EFB\u52A1\u3002"
20143
+ ]),
20144
+ aboutOpenSource: "codexhost \u662F\u4E00\u4E2A\u5F00\u6E90\u9879\u76EE\uFF0C\u5F00\u6E90\u5730\u5740\uFF1A",
20145
+ aboutStarCallout: "\u2B50 \u5982\u679C\u8FD9\u4E2A\u9879\u76EE\u5BF9\u4F60\u6709\u5E2E\u52A9\uFF0C\u8BF7\u7ED9\u6211\u4EEC\u4E00\u4E2A Star\uFF01\u2B50",
20146
+ aboutRepository: "\u5F00\u6E90\u4ED3\u5E93",
19783
20147
  pageLabels: Object.freeze({
19784
20148
  connections: "\u8FDE\u63A5",
19785
- "model-pool": "\u6A21\u578B\u6C60",
19786
- routes: "\u8DEF\u7531",
19787
- gateway: "\u7F51\u5173",
19788
- updates: "\u66F4\u65B0"
20149
+ updates: "\u66F4\u65B0",
20150
+ about: "\u5173\u4E8E"
19789
20151
  })
19790
20152
  });
19791
20153
  var DEFAULT_RENDERER_SETTINGS_MESSAGES = ENGLISH_MESSAGES;
@@ -19839,6 +20201,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19839
20201
  ["path", { d: "M12 13.5V8" }]
19840
20202
  ];
19841
20203
 
20204
+ // ../../node_modules/lucide/dist/esm/icons/circle-check.mjs
20205
+ var CircleCheck = [
20206
+ ["circle", { cx: "12", cy: "12", r: "10" }],
20207
+ ["path", { d: "m9 12 2 2 4-4" }]
20208
+ ];
20209
+
20210
+ // ../../node_modules/lucide/dist/esm/icons/chevron-left.mjs
20211
+ var ChevronLeft = [["path", { d: "m15 18-6-6 6-6" }]];
20212
+
20213
+ // ../../node_modules/lucide/dist/esm/icons/chevron-right.mjs
20214
+ var ChevronRight = [["path", { d: "m9 18 6-6-6-6" }]];
20215
+
19842
20216
  // ../../node_modules/lucide/dist/esm/icons/circle-off.mjs
19843
20217
  var CircleOff = [
19844
20218
  ["path", { d: "m2 2 20 20" }],
@@ -19866,6 +20240,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19866
20240
  ["path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }]
19867
20241
  ];
19868
20242
 
20243
+ // ../../node_modules/lucide/dist/esm/icons/info.mjs
20244
+ var Info = [
20245
+ ["circle", { cx: "12", cy: "12", r: "10" }],
20246
+ ["path", { d: "M12 16v-4" }],
20247
+ ["path", { d: "M12 8h.01" }]
20248
+ ];
20249
+
19869
20250
  // ../../node_modules/lucide/dist/esm/icons/languages.mjs
19870
20251
  var Languages = [
19871
20252
  ["path", { d: "m5 8 6 6" }],
@@ -19939,6 +20320,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19939
20320
  ]
19940
20321
  ];
19941
20322
 
20323
+ // ../../node_modules/lucide/dist/esm/icons/triangle-alert.mjs
20324
+ var TriangleAlert = [
20325
+ ["path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }],
20326
+ ["path", { d: "M12 9v4" }],
20327
+ ["path", { d: "M12 17h.01" }]
20328
+ ];
20329
+
19942
20330
  // ../../node_modules/lucide/dist/esm/icons/x.mjs
19943
20331
  var X = [
19944
20332
  ["path", { d: "M18 6 6 18" }],
@@ -19959,11 +20347,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19959
20347
  "routes",
19960
20348
  "gateway",
19961
20349
  "updates",
20350
+ "about",
19962
20351
  "external-link",
19963
20352
  "refresh",
19964
20353
  "unavailable",
20354
+ "alert",
20355
+ "check",
19965
20356
  "diagnose",
19966
- "copy"
20357
+ "copy",
20358
+ "download",
20359
+ "chevron-left",
20360
+ "chevron-right"
19967
20361
  ];
19968
20362
  var iconNodes = {
19969
20363
  settings: Settings,
@@ -19975,11 +20369,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19975
20369
  routes: Route,
19976
20370
  gateway: Network,
19977
20371
  updates: Download,
20372
+ about: Info,
19978
20373
  "external-link": ExternalLink,
19979
20374
  refresh: RefreshCw,
19980
20375
  unavailable: CircleOff,
20376
+ alert: TriangleAlert,
20377
+ check: CircleCheck,
19981
20378
  diagnose: Stethoscope,
19982
- copy: Copy
20379
+ copy: Copy,
20380
+ download: Download,
20381
+ "chevron-left": ChevronLeft,
20382
+ "chevron-right": ChevronRight
19983
20383
  };
19984
20384
  function isRendererSettingsIconName(value) {
19985
20385
  return RENDERER_SETTINGS_ICON_NAMES.includes(value);
@@ -20112,103 +20512,652 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20112
20512
  }
20113
20513
  };
20114
20514
 
20115
- // src/settings/release-notes.ts
20116
- var HEADING_PATTERN = /^(#{1,6})[ \t]+(.+?)\s*$/u;
20117
- var BLOCKQUOTE_PATTERN = /^>[ \t]?(.*?)\s*$/u;
20118
- var UNORDERED_ITEM_PATTERN = /^[-*][ \t]+(.+?)\s*$/u;
20119
- var CONTINUATION_PATTERN = /^[ \t]+(.+?)\s*$/u;
20120
- var ORDERED_ITEM_PATTERN = /^\d+[.)][ \t]+(.+?)\s*$/u;
20121
- var FENCE_PATTERN = /^```([\w+-]*)\s*$/u;
20122
- var THEMATIC_BREAK_PATTERN = /^(?:---|___|\*\*\*)[ \t]*$/u;
20123
- var INLINE_PATTERN = /(`+)((?:(?!\1).)+)\1|\*\*(.+?)\*\*|\[([^\]]+)\]\(([^)\s]+)\)/gu;
20124
- function createReleaseNotesElement(document2, markdown) {
20125
- const root = document2.createElement("div");
20126
- root.className = "settings-update-notes";
20127
- appendReleaseNotesBlocks(document2, root, markdown);
20128
- return root;
20515
+ // src/settings/connections-page.ts
20516
+ var CODEXHOST_GITHUB_ISSUES_NEW_URL = "https://github.com/BytePioneer-AI/codex-host/issues/new";
20517
+ var HARNESS_INSTALL_URLS = Object.freeze({
20518
+ pi: "https://pi.dev/",
20519
+ "claude-code": "https://code.claude.com/docs/en/quickstart",
20520
+ "deepseek-harness": "https://deepseek-harness.github.io/deepseek-harness/",
20521
+ grok: "https://grok.com/",
20522
+ omp: "https://github.com/can1357/oh-my-pi"
20523
+ });
20524
+ function connectionStatusLabel(availability, messages, hasError = false) {
20525
+ if (hasError && availability !== "notInstalled") return messages.connectionStatusError;
20526
+ if (availability === "ready") return messages.connectionStatusReady;
20527
+ if (availability === "checking") return messages.connectionStatusChecking;
20528
+ if (availability === "notInstalled") return messages.connectionStatusNotInstalled;
20529
+ if (availability === "unavailable" || availability === "error") {
20530
+ return availability === "error" ? messages.connectionStatusError : messages.connectionStatusUnavailable;
20531
+ }
20532
+ return availability === "installing" ? messages.connectionStatusInstalling : messages.connectionStatusUnsupported;
20129
20533
  }
20130
- function appendReleaseNotesBlocks(document2, root, markdown) {
20131
- const lines = markdown.replaceAll("\r\n", "\n").split("\n");
20132
- let index = 0;
20133
- while (index < lines.length) {
20134
- const line = lines[index] ?? "";
20135
- if (line.trim().length === 0) {
20136
- index += 1;
20137
- continue;
20138
- }
20139
- const fence = FENCE_PATTERN.exec(line);
20140
- if (fence) {
20141
- index = appendFencedCode(document2, root, lines, index, fence[1] ?? "");
20142
- continue;
20143
- }
20144
- const heading = HEADING_PATTERN.exec(line);
20145
- if (heading?.[1] && heading[2]) {
20146
- const tag = `h${heading[1].length}`;
20147
- const element = document2.createElement(tag);
20148
- appendInline(document2, element, heading[2]);
20149
- root.append(element);
20150
- index += 1;
20151
- continue;
20152
- }
20153
- if (BLOCKQUOTE_PATTERN.test(line)) {
20154
- index = appendBlockquote(document2, root, lines, index);
20155
- continue;
20156
- }
20157
- if (THEMATIC_BREAK_PATTERN.test(line)) {
20158
- root.append(document2.createElement("hr"));
20159
- index += 1;
20160
- continue;
20161
- }
20162
- if (UNORDERED_ITEM_PATTERN.test(line)) {
20163
- index = appendList(document2, root, lines, index, "ul", UNORDERED_ITEM_PATTERN);
20164
- continue;
20165
- }
20166
- if (ORDERED_ITEM_PATTERN.test(line)) {
20167
- index = appendList(document2, root, lines, index, "ol", ORDERED_ITEM_PATTERN);
20168
- continue;
20169
- }
20170
- index = appendParagraph(document2, root, lines, index);
20534
+ function connectionStatusTone(availability, hasError = false) {
20535
+ if (hasError && availability !== "notInstalled") return "failed";
20536
+ if (availability === "ready") return "ready";
20537
+ if (availability === "checking" || availability === "installing") return "checking";
20538
+ if (availability === "notInstalled") return "setup";
20539
+ return "failed";
20540
+ }
20541
+ function diagnosticText(hostId, item) {
20542
+ const error51 = item.error;
20543
+ return [
20544
+ "codexhost connection diagnostics",
20545
+ `host: ${hostId}`,
20546
+ `agent: ${item.name}`,
20547
+ `status: ${item.availability}`,
20548
+ ...error51 ? [
20549
+ `error.code: ${error51.code}`,
20550
+ `error.message: ${error51.message}`,
20551
+ `retryable: ${error51.retryable}`,
20552
+ ...error51.stage ? [`stage: ${error51.stage}`] : [],
20553
+ ...error51.durationMs !== void 0 ? [`durationMs: ${error51.durationMs}`] : [],
20554
+ ...error51.diagnostic ? [`diagnostic: ${error51.diagnostic}`] : [],
20555
+ ...error51.stderrTail ? [`stderr:
20556
+ ${error51.stderrTail}`] : []
20557
+ ] : []
20558
+ ].join("\n");
20559
+ }
20560
+ function detailLine(document2, label, value) {
20561
+ const line = document2.createElement("div");
20562
+ line.className = "settings-connection-detail-line";
20563
+ const name = document2.createElement("span");
20564
+ name.textContent = label;
20565
+ const content = document2.createElement("code");
20566
+ content.textContent = value;
20567
+ line.append(name, content);
20568
+ return line;
20569
+ }
20570
+ function setCopyButtonLabel(button, label) {
20571
+ button.replaceChildren(createRendererSettingsIcon("copy", 16), label);
20572
+ }
20573
+ function showCopyButtonFeedback(button, label, restoreLabel) {
20574
+ setCopyButtonLabel(button, label);
20575
+ button.ownerDocument.defaultView?.setTimeout(() => {
20576
+ setCopyButtonLabel(button, restoreLabel);
20577
+ }, 2e3);
20578
+ }
20579
+ function copyDiagnosticsToClipboard(document2, button, report, messages, restoreLabel) {
20580
+ const clipboard = document2.defaultView?.navigator.clipboard;
20581
+ if (!clipboard) {
20582
+ showCopyButtonFeedback(button, messages.connectionCopyFailed, restoreLabel);
20583
+ return;
20171
20584
  }
20585
+ void clipboard.writeText(report).then(
20586
+ () => showCopyButtonFeedback(button, messages.connectionCopied, restoreLabel),
20587
+ () => showCopyButtonFeedback(button, messages.connectionCopyFailed, restoreLabel)
20588
+ );
20172
20589
  }
20173
- function appendFencedCode(document2, root, lines, start, language) {
20174
- const body = [];
20175
- let index = start + 1;
20176
- while (index < lines.length && !FENCE_PATTERN.test(lines[index] ?? "")) {
20177
- body.push(lines[index] ?? "");
20178
- index += 1;
20590
+ function connectionHostName(hostId, messages) {
20591
+ if (hostId === "local") return messages.connectionLocalHost;
20592
+ const separator = hostId.lastIndexOf(":");
20593
+ const encodedName = separator >= 0 ? hostId.slice(separator + 1) : hostId;
20594
+ try {
20595
+ return decodeURIComponent(encodedName);
20596
+ } catch {
20597
+ return encodedName;
20179
20598
  }
20180
- if (index < lines.length) index += 1;
20181
- const pre = document2.createElement("pre");
20182
- const code = document2.createElement("code");
20183
- if (language.length > 0) code.className = `language-${language}`;
20184
- code.textContent = body.join("\n");
20185
- pre.append(code);
20186
- root.append(pre);
20187
- return index;
20188
20599
  }
20189
- function appendBlockquote(document2, root, lines, start) {
20190
- const blockquote = document2.createElement("blockquote");
20191
- const content = [];
20192
- let index = start;
20193
- while (index < lines.length) {
20194
- const match = BLOCKQUOTE_PATTERN.exec(lines[index] ?? "");
20195
- if (!match) break;
20196
- content.push(match[1] ?? "");
20197
- index += 1;
20600
+ function createHostScrollButton(document2, direction, messages) {
20601
+ const button = document2.createElement("button");
20602
+ button.type = "button";
20603
+ button.className = "settings-connection-host-scroll";
20604
+ button.dataset.connectionHostScroll = direction;
20605
+ const label = direction === "left" ? messages.connectionHostsScrollLeft : messages.connectionHostsScrollRight;
20606
+ button.setAttribute("aria-label", label);
20607
+ button.title = label;
20608
+ button.append(
20609
+ createRendererSettingsIcon(direction === "left" ? "chevron-left" : "chevron-right", 16)
20610
+ );
20611
+ return button;
20612
+ }
20613
+ function configureHostScroller(document2, strip, tabs, left, right) {
20614
+ const maxScrollLeft = () => Math.max(0, (tabs.scrollWidth || 0) - (tabs.clientWidth || 0));
20615
+ const updateButtons = () => {
20616
+ const maximum = maxScrollLeft();
20617
+ strip.dataset.connectionHostOverflow = String(maximum > 1);
20618
+ left.disabled = tabs.scrollLeft <= 1;
20619
+ right.disabled = maximum <= 1 || tabs.scrollLeft >= maximum - 1;
20620
+ };
20621
+ const scroll = (direction) => {
20622
+ const distance = Math.max(180, Math.round((tabs.clientWidth || 250) * 0.72));
20623
+ if (typeof tabs.scrollBy === "function") {
20624
+ tabs.scrollBy({ left: direction * distance, behavior: "smooth" });
20625
+ } else {
20626
+ tabs.scrollLeft += direction * distance;
20627
+ updateButtons();
20628
+ }
20629
+ };
20630
+ const onLeft = () => scroll(-1);
20631
+ const onRight = () => scroll(1);
20632
+ const onWheel = (event) => {
20633
+ const delta = Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY;
20634
+ const maximum = maxScrollLeft();
20635
+ const canScroll = delta < 0 ? tabs.scrollLeft > 1 : tabs.scrollLeft < maximum - 1;
20636
+ if (!delta || maximum <= 1 || !canScroll) return;
20637
+ event.preventDefault();
20638
+ tabs.scrollLeft += delta;
20639
+ };
20640
+ left.addEventListener("click", onLeft);
20641
+ right.addEventListener("click", onRight);
20642
+ tabs.addEventListener("scroll", updateButtons, { passive: true });
20643
+ tabs.addEventListener("wheel", onWheel, { passive: false });
20644
+ const ownerWindow = document2.defaultView;
20645
+ ownerWindow?.addEventListener("resize", updateButtons);
20646
+ ownerWindow?.setTimeout(updateButtons, 0);
20647
+ updateButtons();
20648
+ return () => {
20649
+ left.removeEventListener("click", onLeft);
20650
+ right.removeEventListener("click", onRight);
20651
+ tabs.removeEventListener("scroll", updateButtons);
20652
+ tabs.removeEventListener("wheel", onWheel);
20653
+ ownerWindow?.removeEventListener("resize", updateButtons);
20654
+ };
20655
+ }
20656
+ function createConnectionIdentityIcon(document2, item, size) {
20657
+ const container = document2.createElement("span");
20658
+ container.className = item.agentSnapshot ? "settings-connection-row__mark settings-connection-row__mark--logo" : "settings-connection-row__mark";
20659
+ container.setAttribute("aria-hidden", "true");
20660
+ if (item.agentSnapshot) {
20661
+ container.append(createRendererAgentIcon(item.agentSnapshot.agent, size, document2));
20662
+ } else {
20663
+ container.textContent = "CH";
20198
20664
  }
20199
- const paragraph = document2.createElement("p");
20200
- appendInline(document2, paragraph, content.join(" ").trim());
20201
- blockquote.append(paragraph);
20202
- root.append(blockquote);
20203
- return index;
20665
+ return container;
20204
20666
  }
20205
- function appendList(document2, root, lines, start, tag, pattern) {
20206
- const list = document2.createElement(tag);
20207
- let index = start;
20208
- while (index < lines.length) {
20209
- const item = pattern.exec(lines[index] ?? "");
20210
- if (!item?.[1]) break;
20211
- const entry = document2.createElement("li");
20667
+ function createConnectionRow(document2, item, messages, selected, select) {
20668
+ const row = document2.createElement("div");
20669
+ row.className = "settings-connection-row";
20670
+ row.dataset.connectionItem = item.key;
20671
+ row.dataset.connectionSelected = String(selected);
20672
+ row.tabIndex = selected ? 0 : -1;
20673
+ row.setAttribute("role", "row");
20674
+ const identity = document2.createElement("div");
20675
+ identity.className = "settings-connection-row__identity";
20676
+ identity.setAttribute("role", "cell");
20677
+ const label = document2.createElement("strong");
20678
+ label.textContent = item.name;
20679
+ identity.append(createConnectionIdentityIcon(document2, item, 19), label);
20680
+ const status = document2.createElement("span");
20681
+ status.className = "settings-connection-row__status";
20682
+ status.dataset.connectionTone = connectionStatusTone(item.availability, item.error !== null);
20683
+ status.setAttribute("role", "cell");
20684
+ status.textContent = connectionStatusLabel(item.availability, messages, item.error !== null);
20685
+ const action = document2.createElement("div");
20686
+ action.className = "settings-connection-row__action";
20687
+ action.setAttribute("role", "cell");
20688
+ if (item.agentSnapshot?.availability === "notInstalled") {
20689
+ const install = document2.createElement("a");
20690
+ install.className = "settings-connection-install-link";
20691
+ install.href = HARNESS_INSTALL_URLS[item.agentSnapshot.agent];
20692
+ install.target = "_blank";
20693
+ install.rel = "noopener noreferrer";
20694
+ install.setAttribute("aria-label", `${messages.connectionOpenInstallation}: ${item.name}`);
20695
+ install.title = messages.connectionOpenInstallation;
20696
+ install.append(createRendererSettingsIcon("download", 17));
20697
+ action.append(install);
20698
+ } else if (item.error) {
20699
+ const viewError = document2.createElement("button");
20700
+ viewError.type = "button";
20701
+ viewError.className = "settings-connection-view-error";
20702
+ viewError.textContent = messages.connectionViewError;
20703
+ viewError.addEventListener("click", (event) => {
20704
+ event.stopPropagation();
20705
+ select();
20706
+ });
20707
+ action.append(viewError);
20708
+ }
20709
+ row.addEventListener("click", (event) => {
20710
+ const target = event.target;
20711
+ if (target?.closest?.("a")) return;
20712
+ select();
20713
+ });
20714
+ row.addEventListener("keydown", (event) => {
20715
+ if (event.key !== "Enter" && event.key !== " ") return;
20716
+ event.preventDefault();
20717
+ select();
20718
+ });
20719
+ row.append(identity, status, action);
20720
+ return row;
20721
+ }
20722
+ function createInspectorHeader(document2, item, messages) {
20723
+ const header = document2.createElement("header");
20724
+ header.className = "settings-connection-inspector__header";
20725
+ const identity = document2.createElement("div");
20726
+ identity.className = "settings-connection-inspector__identity";
20727
+ const title = document2.createElement("strong");
20728
+ title.textContent = item.name;
20729
+ identity.append(createConnectionIdentityIcon(document2, item, 20), title);
20730
+ const status = document2.createElement("span");
20731
+ status.className = "settings-connection-row__status";
20732
+ status.dataset.connectionTone = connectionStatusTone(item.availability, item.error !== null);
20733
+ status.textContent = connectionStatusLabel(item.availability, messages, item.error !== null);
20734
+ header.append(identity, status);
20735
+ return header;
20736
+ }
20737
+ function renderConnectionInspector(document2, inspector, item, hostId, messages) {
20738
+ inspector.replaceChildren(createInspectorHeader(document2, item, messages));
20739
+ const body = document2.createElement("div");
20740
+ body.className = "settings-connection-inspector__body";
20741
+ if (item.agentSnapshot?.availability === "notInstalled") {
20742
+ const callout = document2.createElement("div");
20743
+ callout.className = "settings-connection-install-callout";
20744
+ const icon2 = document2.createElement("span");
20745
+ icon2.className = "settings-connection-install-callout__icon";
20746
+ icon2.append(createRendererSettingsIcon("download", 18));
20747
+ const copy = document2.createElement("div");
20748
+ const title = document2.createElement("strong");
20749
+ title.textContent = `${messages.connectionInstall} ${item.name}`;
20750
+ const description = document2.createElement("p");
20751
+ description.textContent = messages.connectionInstallDescription;
20752
+ copy.append(title, description);
20753
+ callout.append(icon2, copy);
20754
+ const install = document2.createElement("a");
20755
+ install.className = "settings-command-button settings-connection-install-button";
20756
+ install.href = HARNESS_INSTALL_URLS[item.agentSnapshot.agent];
20757
+ install.target = "_blank";
20758
+ install.rel = "noopener noreferrer";
20759
+ install.append(
20760
+ messages.connectionOpenInstallation,
20761
+ createRendererSettingsIcon("external-link", 14)
20762
+ );
20763
+ body.append(callout, install);
20764
+ } else if (item.error) {
20765
+ const summary = document2.createElement("div");
20766
+ summary.className = "settings-connection-error-summary";
20767
+ const title = document2.createElement("strong");
20768
+ title.textContent = messages.connectionErrorTitle;
20769
+ const description = document2.createElement("p");
20770
+ description.textContent = item.error.message;
20771
+ summary.append(title, description);
20772
+ const metadata = document2.createElement("div");
20773
+ metadata.className = "settings-connection-error-metadata";
20774
+ metadata.append(
20775
+ detailLine(document2, messages.connectionErrorCode, item.error.code),
20776
+ detailLine(document2, messages.connectionRetryable, String(item.error.retryable))
20777
+ );
20778
+ if (item.error.stage) {
20779
+ metadata.append(detailLine(document2, messages.connectionFailureStage, item.error.stage));
20780
+ }
20781
+ if (item.error.durationMs !== void 0) {
20782
+ metadata.append(
20783
+ detailLine(document2, messages.connectionDuration, `${item.error.durationMs} ms`)
20784
+ );
20785
+ }
20786
+ if (item.error.diagnostic) {
20787
+ metadata.append(detailLine(document2, messages.connectionDiagnostic, item.error.diagnostic));
20788
+ }
20789
+ const logHeader = document2.createElement("div");
20790
+ logHeader.className = "settings-connection-error-log-header";
20791
+ const logTitle = document2.createElement("strong");
20792
+ logTitle.textContent = messages.connectionErrorLog;
20793
+ const copy = document2.createElement("button");
20794
+ copy.type = "button";
20795
+ copy.className = "settings-command-button settings-command-button--secondary";
20796
+ setCopyButtonLabel(copy, messages.connectionCopyDetails);
20797
+ const report = diagnosticText(hostId, item);
20798
+ copy.addEventListener("click", () => {
20799
+ copyDiagnosticsToClipboard(document2, copy, report, messages, messages.connectionCopyDetails);
20800
+ });
20801
+ logHeader.append(logTitle, copy);
20802
+ const log = document2.createElement("pre");
20803
+ log.className = "settings-connection-stderr";
20804
+ log.textContent = item.error.stderrTail ?? item.error.diagnostic ?? report;
20805
+ const actions = document2.createElement("div");
20806
+ actions.className = "settings-connection-error-actions";
20807
+ const issue2 = document2.createElement("a");
20808
+ issue2.className = "settings-command-button settings-command-button--secondary";
20809
+ issue2.href = CODEXHOST_GITHUB_ISSUES_NEW_URL;
20810
+ issue2.target = "_blank";
20811
+ issue2.rel = "noopener noreferrer";
20812
+ issue2.append(messages.connectionOpenIssue, createRendererSettingsIcon("external-link", 14));
20813
+ actions.append(issue2);
20814
+ const issueNote = document2.createElement("p");
20815
+ issueNote.className = "settings-connection-issue-note";
20816
+ issueNote.textContent = messages.connectionIssueDescription;
20817
+ body.append(summary, metadata, logHeader, log, actions, issueNote);
20818
+ } else {
20819
+ const status = document2.createElement("div");
20820
+ status.className = "settings-connection-state-summary";
20821
+ const title = document2.createElement("strong");
20822
+ title.textContent = connectionStatusLabel(item.availability, messages);
20823
+ const description = document2.createElement("p");
20824
+ description.textContent = item.availability === "ready" ? messages.connectionReadyDescription : messages.connectionUnavailableDescription;
20825
+ status.append(title, description);
20826
+ body.append(status);
20827
+ }
20828
+ inspector.append(body);
20829
+ }
20830
+ function connectionItems(snapshot, host, messages) {
20831
+ return [
20832
+ {
20833
+ key: "renderer-adapter",
20834
+ name: messages.connectionAdapter,
20835
+ availability: snapshot.adapter.state,
20836
+ error: null
20837
+ },
20838
+ ...host.agents.map((agent) => ({
20839
+ key: agent.agent,
20840
+ name: RENDERER_AGENT_LABELS[agent.agent],
20841
+ availability: agent.availability,
20842
+ error: agent.availability === "notInstalled" ? null : agent.error,
20843
+ agentSnapshot: agent
20844
+ }))
20845
+ ];
20846
+ }
20847
+ function createConnectionsSettingsPage(messages, getDiagnostics) {
20848
+ return Object.freeze({
20849
+ id: "connections",
20850
+ label: messages.pageLabels.connections,
20851
+ icon: "connections",
20852
+ mount(context) {
20853
+ const document2 = context.content.ownerDocument;
20854
+ const header = document2.createElement("div");
20855
+ header.className = "settings-connection-page-header";
20856
+ const headingCopy = document2.createElement("div");
20857
+ const heading = document2.createElement("div");
20858
+ heading.className = "settings-section-label";
20859
+ heading.textContent = messages.pageLabels.connections;
20860
+ const description = document2.createElement("p");
20861
+ description.className = "settings-page-description";
20862
+ description.textContent = messages.connectionsDescription;
20863
+ headingCopy.append(heading, description);
20864
+ const refresh = document2.createElement("button");
20865
+ refresh.type = "button";
20866
+ refresh.className = "settings-command-button settings-command-button--secondary";
20867
+ refresh.dataset.connectionAction = "refresh";
20868
+ refresh.append(createRendererSettingsIcon("diagnose", 16), messages.connectionRefresh);
20869
+ header.append(headingCopy, refresh);
20870
+ const content = document2.createElement("div");
20871
+ content.className = "settings-connections-content";
20872
+ context.content.append(header, content);
20873
+ let pending = false;
20874
+ let selectedHostId = null;
20875
+ let selectedItemKey = null;
20876
+ let latestSnapshot = null;
20877
+ let disposeHostScroller = () => void 0;
20878
+ const diagnostics = getDiagnostics();
20879
+ const runRefresh = () => {
20880
+ if (pending || !diagnostics) return;
20881
+ pending = true;
20882
+ refresh.disabled = true;
20883
+ refresh.replaceChildren(
20884
+ createRendererSettingsIcon("diagnose", 16),
20885
+ messages.connectionRefreshing
20886
+ );
20887
+ void context.runLatest(() => diagnostics.refresh(), {
20888
+ success() {
20889
+ pending = false;
20890
+ refresh.disabled = false;
20891
+ refresh.replaceChildren(
20892
+ createRendererSettingsIcon("diagnose", 16),
20893
+ messages.connectionRefresh
20894
+ );
20895
+ render(diagnostics.snapshot());
20896
+ },
20897
+ failure(error51) {
20898
+ pending = false;
20899
+ refresh.disabled = false;
20900
+ refresh.replaceChildren(
20901
+ createRendererSettingsIcon("diagnose", 16),
20902
+ messages.connectionRefresh
20903
+ );
20904
+ const snapshot = diagnostics.snapshot();
20905
+ render({
20906
+ ...snapshot,
20907
+ hosts: snapshot.hosts.map((host) => ({
20908
+ ...host,
20909
+ agents: host.agents.map((agent) => ({
20910
+ ...agent,
20911
+ availability: "error",
20912
+ error: {
20913
+ code: "internalError",
20914
+ message: error51 instanceof Error ? error51.message : String(error51),
20915
+ retryable: true,
20916
+ stage: "request"
20917
+ }
20918
+ }))
20919
+ }))
20920
+ });
20921
+ }
20922
+ });
20923
+ };
20924
+ const render = (snapshot) => {
20925
+ latestSnapshot = snapshot;
20926
+ disposeHostScroller();
20927
+ disposeHostScroller = () => void 0;
20928
+ content.replaceChildren();
20929
+ if (!snapshot) {
20930
+ const empty = document2.createElement("div");
20931
+ empty.className = "settings-empty";
20932
+ empty.textContent = messages.connectionNoRuntime;
20933
+ content.append(empty);
20934
+ return;
20935
+ }
20936
+ 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];
20937
+ if (!selectedHost) return;
20938
+ selectedHostId = selectedHost.hostId;
20939
+ const layout = document2.createElement("div");
20940
+ layout.className = "settings-connections-layout";
20941
+ const list = document2.createElement("section");
20942
+ list.className = "settings-connection-list";
20943
+ const hostStrip = document2.createElement("div");
20944
+ hostStrip.className = "settings-connection-host-strip";
20945
+ const scrollLeft = createHostScrollButton(document2, "left", messages);
20946
+ const tabs = document2.createElement("div");
20947
+ tabs.className = "settings-connection-host-tabs";
20948
+ tabs.setAttribute("role", "tablist");
20949
+ tabs.setAttribute("aria-label", messages.connectionHosts);
20950
+ const scrollRight = createHostScrollButton(document2, "right", messages);
20951
+ const panelId = "codexhost-settings-connection-host-panel";
20952
+ snapshot.hosts.forEach((host, index) => {
20953
+ const tab = document2.createElement("button");
20954
+ tab.type = "button";
20955
+ tab.className = "settings-connection-host-tab";
20956
+ tab.dataset.connectionHostTab = host.hostId;
20957
+ tab.dataset.connectionHostActive = String(host.active);
20958
+ tab.setAttribute("role", "tab");
20959
+ tab.setAttribute("aria-controls", panelId);
20960
+ tab.setAttribute("aria-selected", String(host.hostId === selectedHost.hostId));
20961
+ tab.tabIndex = host.hostId === selectedHost.hostId ? 0 : -1;
20962
+ const hostName = connectionHostName(host.hostId, messages);
20963
+ tab.textContent = hostName;
20964
+ tab.title = host.active ? `${hostName} \xB7 ${messages.connectionActiveHost}` : hostName;
20965
+ tab.addEventListener("click", () => {
20966
+ selectedHostId = host.hostId;
20967
+ render(latestSnapshot);
20968
+ });
20969
+ tab.addEventListener("keydown", (event) => {
20970
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
20971
+ event.preventDefault();
20972
+ const direction = event.key === "ArrowRight" ? 1 : -1;
20973
+ const next = snapshot.hosts[(index + direction + snapshot.hosts.length) % snapshot.hosts.length];
20974
+ if (!next) return;
20975
+ selectedHostId = next.hostId;
20976
+ render(latestSnapshot);
20977
+ const nextTab = [
20978
+ ...content.querySelectorAll("[data-connection-host-tab]")
20979
+ ].find((candidate) => candidate.dataset.connectionHostTab === next.hostId);
20980
+ nextTab?.focus();
20981
+ });
20982
+ tabs.append(tab);
20983
+ });
20984
+ hostStrip.append(scrollLeft, tabs, scrollRight);
20985
+ const tableHeader = document2.createElement("div");
20986
+ tableHeader.className = "settings-connection-table-header";
20987
+ tableHeader.setAttribute("role", "row");
20988
+ const componentHeading = document2.createElement("span");
20989
+ componentHeading.textContent = messages.connectionComponent;
20990
+ componentHeading.setAttribute("role", "columnheader");
20991
+ const statusHeading = document2.createElement("span");
20992
+ statusHeading.textContent = messages.connectionStatus;
20993
+ statusHeading.setAttribute("role", "columnheader");
20994
+ const actionHeading = document2.createElement("span");
20995
+ actionHeading.setAttribute("aria-hidden", "true");
20996
+ tableHeader.append(componentHeading, statusHeading, actionHeading);
20997
+ const rows = document2.createElement("div");
20998
+ rows.className = "settings-connection-rows";
20999
+ rows.id = panelId;
21000
+ rows.dataset.connectionHost = selectedHost.hostId;
21001
+ rows.setAttribute("role", "rowgroup");
21002
+ const inspector = document2.createElement("aside");
21003
+ inspector.className = "settings-connection-inspector";
21004
+ inspector.setAttribute("aria-live", "polite");
21005
+ const items = connectionItems(snapshot, selectedHost, messages);
21006
+ if (!items.some((item) => item.key === selectedItemKey)) {
21007
+ selectedItemKey = items.find(
21008
+ (item) => item.agentSnapshot?.availability === "notInstalled" || item.error !== null
21009
+ )?.key ?? items[0]?.key ?? null;
21010
+ }
21011
+ const rowElements = /* @__PURE__ */ new Map();
21012
+ const selectItem = (item) => {
21013
+ selectedItemKey = item.key;
21014
+ for (const [key, row] of rowElements) {
21015
+ const selected = key === item.key;
21016
+ row.dataset.connectionSelected = String(selected);
21017
+ row.tabIndex = selected ? 0 : -1;
21018
+ }
21019
+ renderConnectionInspector(document2, inspector, item, selectedHost.hostId, messages);
21020
+ };
21021
+ for (const item of items) {
21022
+ const row = createConnectionRow(
21023
+ document2,
21024
+ item,
21025
+ messages,
21026
+ item.key === selectedItemKey,
21027
+ () => selectItem(item)
21028
+ );
21029
+ rowElements.set(item.key, row);
21030
+ rows.append(row);
21031
+ }
21032
+ const selectedItem = items.find((item) => item.key === selectedItemKey) ?? items[0];
21033
+ if (selectedItem) selectItem(selectedItem);
21034
+ list.append(hostStrip, tableHeader, rows);
21035
+ layout.append(list, inspector);
21036
+ content.append(layout);
21037
+ disposeHostScroller = configureHostScroller(
21038
+ document2,
21039
+ hostStrip,
21040
+ tabs,
21041
+ scrollLeft,
21042
+ scrollRight
21043
+ );
21044
+ const selectedTab = [...tabs.children].find(
21045
+ (child) => child.getAttribute("aria-selected") === "true"
21046
+ );
21047
+ selectedTab?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
21048
+ };
21049
+ render(diagnostics?.snapshot() ?? null);
21050
+ if (!diagnostics) {
21051
+ refresh.disabled = true;
21052
+ return void 0;
21053
+ }
21054
+ refresh.addEventListener("click", runRefresh);
21055
+ const unsubscribe = diagnostics.subscribe(() => render(diagnostics.snapshot()));
21056
+ return () => {
21057
+ disposeHostScroller();
21058
+ unsubscribe();
21059
+ };
21060
+ }
21061
+ });
21062
+ }
21063
+
21064
+ // src/settings/release-notes.ts
21065
+ var HEADING_PATTERN = /^(#{1,6})[ \t]+(.+?)\s*$/u;
21066
+ var BLOCKQUOTE_PATTERN = /^>[ \t]?(.*?)\s*$/u;
21067
+ var UNORDERED_ITEM_PATTERN = /^[-*][ \t]+(.+?)\s*$/u;
21068
+ var CONTINUATION_PATTERN = /^[ \t]+(.+?)\s*$/u;
21069
+ var ORDERED_ITEM_PATTERN = /^\d+[.)][ \t]+(.+?)\s*$/u;
21070
+ var FENCE_PATTERN = /^```([\w+-]*)\s*$/u;
21071
+ var THEMATIC_BREAK_PATTERN = /^(?:---|___|\*\*\*)[ \t]*$/u;
21072
+ var INLINE_PATTERN = /(`+)((?:(?!\1).)+)\1|\*\*(.+?)\*\*|\[([^\]]+)\]\(([^)\s]+)\)/gu;
21073
+ function createReleaseNotesElement(document2, markdown) {
21074
+ const root = document2.createElement("div");
21075
+ root.className = "settings-update-notes";
21076
+ appendReleaseNotesBlocks(document2, root, markdown);
21077
+ return root;
21078
+ }
21079
+ function appendReleaseNotesBlocks(document2, root, markdown) {
21080
+ const lines = markdown.replaceAll("\r\n", "\n").split("\n");
21081
+ let index = 0;
21082
+ while (index < lines.length) {
21083
+ const line = lines[index] ?? "";
21084
+ if (line.trim().length === 0) {
21085
+ index += 1;
21086
+ continue;
21087
+ }
21088
+ const fence = FENCE_PATTERN.exec(line);
21089
+ if (fence) {
21090
+ index = appendFencedCode(document2, root, lines, index, fence[1] ?? "");
21091
+ continue;
21092
+ }
21093
+ const heading = HEADING_PATTERN.exec(line);
21094
+ if (heading?.[1] && heading[2]) {
21095
+ const tag = `h${heading[1].length}`;
21096
+ const element = document2.createElement(tag);
21097
+ appendInline(document2, element, heading[2]);
21098
+ root.append(element);
21099
+ index += 1;
21100
+ continue;
21101
+ }
21102
+ if (BLOCKQUOTE_PATTERN.test(line)) {
21103
+ index = appendBlockquote(document2, root, lines, index);
21104
+ continue;
21105
+ }
21106
+ if (THEMATIC_BREAK_PATTERN.test(line)) {
21107
+ root.append(document2.createElement("hr"));
21108
+ index += 1;
21109
+ continue;
21110
+ }
21111
+ if (UNORDERED_ITEM_PATTERN.test(line)) {
21112
+ index = appendList(document2, root, lines, index, "ul", UNORDERED_ITEM_PATTERN);
21113
+ continue;
21114
+ }
21115
+ if (ORDERED_ITEM_PATTERN.test(line)) {
21116
+ index = appendList(document2, root, lines, index, "ol", ORDERED_ITEM_PATTERN);
21117
+ continue;
21118
+ }
21119
+ index = appendParagraph(document2, root, lines, index);
21120
+ }
21121
+ }
21122
+ function appendFencedCode(document2, root, lines, start, language) {
21123
+ const body = [];
21124
+ let index = start + 1;
21125
+ while (index < lines.length && !FENCE_PATTERN.test(lines[index] ?? "")) {
21126
+ body.push(lines[index] ?? "");
21127
+ index += 1;
21128
+ }
21129
+ if (index < lines.length) index += 1;
21130
+ const pre = document2.createElement("pre");
21131
+ const code = document2.createElement("code");
21132
+ if (language.length > 0) code.className = `language-${language}`;
21133
+ code.textContent = body.join("\n");
21134
+ pre.append(code);
21135
+ root.append(pre);
21136
+ return index;
21137
+ }
21138
+ function appendBlockquote(document2, root, lines, start) {
21139
+ const blockquote = document2.createElement("blockquote");
21140
+ const content = [];
21141
+ let index = start;
21142
+ while (index < lines.length) {
21143
+ const match = BLOCKQUOTE_PATTERN.exec(lines[index] ?? "");
21144
+ if (!match) break;
21145
+ content.push(match[1] ?? "");
21146
+ index += 1;
21147
+ }
21148
+ const paragraph = document2.createElement("p");
21149
+ appendInline(document2, paragraph, content.join(" ").trim());
21150
+ blockquote.append(paragraph);
21151
+ root.append(blockquote);
21152
+ return index;
21153
+ }
21154
+ function appendList(document2, root, lines, start, tag, pattern) {
21155
+ const list = document2.createElement(tag);
21156
+ let index = start;
21157
+ while (index < lines.length) {
21158
+ const item = pattern.exec(lines[index] ?? "");
21159
+ if (!item?.[1]) break;
21160
+ const entry = document2.createElement("li");
20212
21161
  appendInline(document2, entry, item[1]);
20213
21162
  index += 1;
20214
21163
  const continuationLines = [];
@@ -20266,427 +21215,110 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
20266
21215
  if (!isSafeLink(href)) {
20267
21216
  parent.append(match[4] ?? "");
20268
21217
  } else {
20269
- const link = document2.createElement("a");
20270
- link.textContent = match[4] ?? "";
20271
- link.setAttribute("href", href);
20272
- link.setAttribute("target", "_blank");
20273
- link.setAttribute("rel", "noopener noreferrer");
20274
- parent.append(link);
20275
- }
20276
- }
20277
- lastIndex = index + match[0].length;
20278
- }
20279
- if (lastIndex < text.length) parent.append(text.slice(lastIndex));
20280
- }
20281
- function isSafeLink(href) {
20282
- try {
20283
- const url2 = new URL(href, "https://codexhost.invalid");
20284
- return url2.protocol === "http:" || url2.protocol === "https:";
20285
- } catch {
20286
- return false;
20287
- }
20288
- }
20289
-
20290
- // src/settings/update-request.ts
20291
- var RENDERER_UPDATE_REQUEST_TIMEOUT_MS = 15e3;
20292
- var RendererUpdateRequestTimeoutError = class extends Error {
20293
- constructor() {
20294
- super("Update request timed out");
20295
- this.name = "RendererUpdateRequestTimeoutError";
20296
- }
20297
- };
20298
- function runBoundedRendererUpdateRequest(operation, signal, timeoutMs = RENDERER_UPDATE_REQUEST_TIMEOUT_MS) {
20299
- return new Promise((resolve, reject) => {
20300
- let settled = false;
20301
- const settle = (handler) => {
20302
- if (settled) return;
20303
- settled = true;
20304
- clearTimeout(timeout);
20305
- signal.removeEventListener("abort", abort);
20306
- handler();
20307
- };
20308
- const timeout = setTimeout(() => {
20309
- settle(() => reject(new RendererUpdateRequestTimeoutError()));
20310
- }, timeoutMs);
20311
- const abort = () => {
20312
- settle(() => reject(new Error("Update request was aborted")));
20313
- };
20314
- const rejectRequest = (error51) => {
20315
- settle(() => reject(error51));
20316
- };
20317
- if (signal.aborted) {
20318
- abort();
20319
- return;
20320
- }
20321
- signal.addEventListener("abort", abort, { once: true });
20322
- try {
20323
- void operation().then(
20324
- (value) => settle(() => resolve(value)),
20325
- (error51) => rejectRequest(error51)
20326
- );
20327
- } catch (error51) {
20328
- rejectRequest(error51);
20329
- }
20330
- });
20331
- }
20332
-
20333
- // src/settings/pages.ts
20334
- var CODEXHOST_RELEASES_LATEST_URL = "https://github.com/BytePioneer-AI/codex-host/releases/latest";
20335
- var CODEXHOST_NPM_MANUAL_UPDATE_COMMAND = "npm install -g @codexhost/cli@latest";
20336
- var DEFAULT_RENDERER_SETTINGS_PAGE_IDS = [
20337
- "connections",
20338
- "model-pool",
20339
- "routes",
20340
- "gateway",
20341
- "updates"
20342
- ];
20343
- function appendUnavailableStatus(content, messages) {
20344
- const status = content.ownerDocument.createElement("div");
20345
- status.className = "settings-empty";
20346
- const copy = content.ownerDocument.createElement("div");
20347
- const title = content.ownerDocument.createElement("strong");
20348
- title.textContent = messages.inDevelopment;
20349
- copy.append(title);
20350
- status.append(copy);
20351
- content.append(status);
20352
- }
20353
- function unavailablePage(id, messages) {
20354
- return Object.freeze({
20355
- id,
20356
- label: messages.pageLabels[id],
20357
- icon: id,
20358
- mount(context) {
20359
- appendUnavailableStatus(context.content, messages);
20360
- return void 0;
20361
- }
20362
- });
20363
- }
20364
- function connectionStatusLabel(availability, messages) {
20365
- if (availability === "ready") return messages.connectionStatusReady;
20366
- if (availability === "checking") return messages.connectionStatusChecking;
20367
- if (availability === "notInstalled") return messages.connectionStatusNotInstalled;
20368
- if (availability === "unavailable" || availability === "error") {
20369
- return availability === "error" ? messages.connectionStatusError : messages.connectionStatusUnavailable;
20370
- }
20371
- return availability === "installing" ? messages.connectionStatusInstalling : messages.connectionStatusUnsupported;
20372
- }
20373
- function connectionStatusTone(availability) {
20374
- if (availability === "ready") return "ready";
20375
- if (availability === "checking" || availability === "installing") return "checking";
20376
- return "failed";
20377
- }
20378
- function diagnosticText(hostId, name, snapshot) {
20379
- const error51 = snapshot.error;
20380
- const lines = [
20381
- "codexhost connection diagnostics",
20382
- `host: ${hostId}`,
20383
- `agent: ${name}`,
20384
- `status: ${snapshot.availability}`,
20385
- ...error51 ? [
20386
- `error.code: ${error51.code}`,
20387
- `error.message: ${error51.message}`,
20388
- `retryable: ${error51.retryable}`,
20389
- ...error51.stage ? [`stage: ${error51.stage}`] : [],
20390
- ...error51.durationMs !== void 0 ? [`durationMs: ${error51.durationMs}`] : [],
20391
- ...error51.diagnostic ? [`diagnostic: ${error51.diagnostic}`] : [],
20392
- ...error51.stderrTail ? [`stderr:
20393
- ${error51.stderrTail}`] : []
20394
- ] : []
20395
- ];
20396
- return lines.join("\n");
20397
- }
20398
- function detailLine(document2, label, value) {
20399
- const line = document2.createElement("div");
20400
- line.className = "settings-connection-detail-line";
20401
- const name = document2.createElement("span");
20402
- name.textContent = label;
20403
- const content = document2.createElement("code");
20404
- content.textContent = value;
20405
- line.append(name, content);
20406
- return line;
20407
- }
20408
- function setCopyButtonLabel(button, label) {
20409
- button.replaceChildren(createRendererSettingsIcon("copy", 16), label);
20410
- }
20411
- function showCopyButtonFeedback(button, label, restoreLabel) {
20412
- setCopyButtonLabel(button, label);
20413
- button.ownerDocument.defaultView?.setTimeout(() => {
20414
- setCopyButtonLabel(button, restoreLabel);
20415
- }, 2e3);
20416
- }
20417
- function copyDiagnosticsToClipboard(document2, button, report, messages, restoreLabel) {
20418
- const clipboard = document2.defaultView?.navigator.clipboard;
20419
- if (!clipboard) {
20420
- showCopyButtonFeedback(button, messages.connectionCopyFailed, restoreLabel);
20421
- return;
20422
- }
20423
- void clipboard.writeText(report).then(
20424
- () => showCopyButtonFeedback(button, messages.connectionCopied, restoreLabel),
20425
- () => showCopyButtonFeedback(button, messages.connectionCopyFailed, restoreLabel)
20426
- );
20427
- }
20428
- function appendConnectionRow(document2, parent, name, availability, detail, error51, messages, rowAttribute, diagnosticSnapshot, diagnosticHostId) {
20429
- const row = document2.createElement("div");
20430
- row.className = "settings-connection-row";
20431
- if (rowAttribute) row.dataset.connectionAgent = rowAttribute;
20432
- const identity = document2.createElement("div");
20433
- identity.className = "settings-connection-row__identity";
20434
- const dot = document2.createElement("span");
20435
- dot.className = "settings-connection-row__dot";
20436
- dot.dataset.connectionTone = connectionStatusTone(availability);
20437
- dot.setAttribute("aria-hidden", "true");
20438
- const label = document2.createElement("strong");
20439
- label.textContent = name;
20440
- identity.append(dot, label);
20441
- const status = document2.createElement("span");
20442
- status.className = "settings-status-badge";
20443
- status.dataset.connectionTone = connectionStatusTone(availability);
20444
- status.textContent = connectionStatusLabel(availability, messages);
20445
- const detailElement = document2.createElement("div");
20446
- detailElement.className = "settings-connection-row__detail";
20447
- const summary = document2.createElement("span");
20448
- summary.className = "settings-connection-row__reason";
20449
- summary.textContent = error51 ? `${messages.connectionReason}: ${error51.code}: ${error51.message}` : detail ? `${messages.connectionReason}: ${detail}` : "";
20450
- detailElement.append(summary);
20451
- let toggle = null;
20452
- if (error51) {
20453
- toggle = document2.createElement("button");
20454
- toggle.type = "button";
20455
- toggle.className = "settings-connection-details-toggle";
20456
- toggle.textContent = messages.connectionViewDetails;
20457
- toggle.setAttribute("aria-expanded", "true");
20458
- detailElement.append(toggle);
20459
- }
20460
- const details = document2.createElement("div");
20461
- details.className = "settings-connection-details";
20462
- details.hidden = !error51;
20463
- if (error51) {
20464
- details.append(
20465
- detailLine(document2, messages.connectionErrorCode, error51.code),
20466
- detailLine(document2, messages.connectionErrorMessage, error51.message),
20467
- detailLine(document2, messages.connectionRetryable, String(error51.retryable))
20468
- );
20469
- if (error51.stage)
20470
- details.append(detailLine(document2, messages.connectionFailureStage, error51.stage));
20471
- if (error51.durationMs !== void 0) {
20472
- details.append(detailLine(document2, messages.connectionDuration, `${error51.durationMs} ms`));
20473
- }
20474
- if (error51.diagnostic)
20475
- details.append(detailLine(document2, messages.connectionDiagnostic, error51.diagnostic));
20476
- if (error51.stderrTail) {
20477
- const stderr = document2.createElement("pre");
20478
- stderr.className = "settings-connection-stderr";
20479
- stderr.textContent = error51.stderrTail;
20480
- details.append(stderr);
21218
+ const link = document2.createElement("a");
21219
+ link.textContent = match[4] ?? "";
21220
+ link.setAttribute("href", href);
21221
+ link.setAttribute("target", "_blank");
21222
+ link.setAttribute("rel", "noopener noreferrer");
21223
+ parent.append(link);
21224
+ }
20481
21225
  }
20482
- const copy = document2.createElement("button");
20483
- copy.type = "button";
20484
- copy.className = "settings-command-button settings-command-button--secondary";
20485
- setCopyButtonLabel(copy, messages.connectionCopyDetails);
20486
- copy.addEventListener("click", () => {
20487
- if (!diagnosticSnapshot) return;
20488
- copyDiagnosticsToClipboard(
20489
- document2,
20490
- copy,
20491
- diagnosticText(diagnosticHostId ?? "unknown", name, diagnosticSnapshot),
20492
- messages,
20493
- messages.connectionCopyDetails
20494
- );
20495
- });
20496
- details.append(copy);
20497
- }
20498
- if (toggle) {
20499
- toggle.addEventListener("click", () => {
20500
- details.hidden = !details.hidden;
20501
- toggle?.setAttribute("aria-expanded", String(!details.hidden));
20502
- });
21226
+ lastIndex = index + match[0].length;
20503
21227
  }
20504
- row.append(identity, status, detailElement, details);
20505
- parent.append(row);
21228
+ if (lastIndex < text.length) parent.append(text.slice(lastIndex));
20506
21229
  }
20507
- function connectionHostLabel(hostId, active, messages) {
20508
- const activeLabel = active ? ` \xB7 ${messages.connectionActiveHost}` : "";
20509
- if (hostId === "local") return `${messages.connectionLocalHost}${activeLabel}`;
20510
- const separator = hostId.lastIndexOf(":");
20511
- const encodedName = separator >= 0 ? hostId.slice(separator + 1) : hostId;
20512
- let name = encodedName;
21230
+ function isSafeLink(href) {
20513
21231
  try {
20514
- name = decodeURIComponent(encodedName);
21232
+ const url2 = new URL(href, "https://codexhost.invalid");
21233
+ return url2.protocol === "http:" || url2.protocol === "https:";
20515
21234
  } catch {
21235
+ return false;
20516
21236
  }
20517
- return `${messages.connectionRemoteHost}: ${name}${activeLabel}`;
20518
21237
  }
20519
- function connectionsPage(messages, getDiagnostics) {
20520
- return Object.freeze({
20521
- id: "connections",
20522
- label: messages.pageLabels.connections,
20523
- icon: "connections",
20524
- mount(context) {
20525
- const document2 = context.content.ownerDocument;
20526
- const heading = document2.createElement("div");
20527
- heading.className = "settings-section-label";
20528
- heading.textContent = messages.pageLabels.connections;
20529
- const description = document2.createElement("p");
20530
- description.className = "settings-page-description";
20531
- description.textContent = messages.connectionsDescription;
20532
- const actions = document2.createElement("div");
20533
- actions.className = "settings-connection-actions";
20534
- const refresh = document2.createElement("button");
20535
- refresh.type = "button";
20536
- refresh.className = "settings-command-button settings-command-button--secondary";
20537
- refresh.dataset.connectionAction = "refresh";
20538
- refresh.append(createRendererSettingsIcon("diagnose", 16), messages.connectionRefresh);
20539
- const copyAll = document2.createElement("button");
20540
- copyAll.type = "button";
20541
- copyAll.className = "settings-command-button settings-command-button--secondary";
20542
- copyAll.dataset.connectionAction = "copy-all";
20543
- copyAll.append(createRendererSettingsIcon("copy", 16), messages.connectionCopyAll);
20544
- actions.append(refresh, copyAll);
20545
- const content = document2.createElement("div");
20546
- content.className = "settings-connection-list";
20547
- context.content.append(heading, description, actions, content);
20548
- let pending = false;
20549
- let selectedHostId = "local";
20550
- let latestSnapshot = null;
20551
- const render = (snapshot) => {
20552
- latestSnapshot = snapshot;
20553
- content.replaceChildren();
20554
- if (!snapshot) {
20555
- const empty = document2.createElement("div");
20556
- empty.className = "settings-empty";
20557
- empty.textContent = messages.connectionNoRuntime;
20558
- content.append(empty);
20559
- return;
20560
- }
20561
- appendConnectionRow(
20562
- document2,
20563
- content,
20564
- messages.connectionAdapter,
20565
- snapshot.adapter.state,
20566
- `reason=${snapshot.adapter.reason}, hook=${snapshot.adapter.hook ?? "none"}`,
20567
- null,
20568
- messages,
20569
- "renderer-adapter"
20570
- );
20571
- const selectedHost = snapshot.hosts.find((host) => host.hostId === selectedHostId) ?? snapshot.hosts.find((host) => host.hostId === "local") ?? snapshot.hosts[0];
20572
- if (!selectedHost) return;
20573
- selectedHostId = selectedHost.hostId;
20574
- const tabs = document2.createElement("div");
20575
- tabs.className = "settings-connection-host-tabs";
20576
- tabs.setAttribute("role", "tablist");
20577
- tabs.setAttribute("aria-label", messages.connectionHosts);
20578
- const hostSection = document2.createElement("section");
20579
- hostSection.className = "settings-connection-host";
20580
- hostSection.dataset.connectionHost = selectedHost.hostId;
20581
- hostSection.setAttribute("role", "tabpanel");
20582
- const panelId = "codexhost-settings-connection-host-panel";
20583
- hostSection.id = panelId;
20584
- for (const host of snapshot.hosts) {
20585
- const tab = document2.createElement("button");
20586
- tab.type = "button";
20587
- tab.className = "settings-connection-host-tab";
20588
- tab.dataset.connectionHostTab = host.hostId;
20589
- tab.setAttribute("role", "tab");
20590
- tab.setAttribute("aria-controls", panelId);
20591
- tab.setAttribute("aria-selected", String(host.hostId === selectedHost.hostId));
20592
- tab.tabIndex = host.hostId === selectedHost.hostId ? 0 : -1;
20593
- tab.textContent = connectionHostLabel(host.hostId, host.active, messages);
20594
- tab.addEventListener("click", () => {
20595
- selectedHostId = host.hostId;
20596
- render(latestSnapshot);
20597
- });
20598
- tabs.append(tab);
20599
- }
20600
- for (const agent of selectedHost.agents) {
20601
- appendConnectionRow(
20602
- document2,
20603
- hostSection,
20604
- RENDERER_AGENT_LABELS[agent.agent],
20605
- agent.availability,
20606
- null,
20607
- agent.error,
20608
- messages,
20609
- agent.agent,
20610
- agent,
20611
- selectedHost.hostId
20612
- );
20613
- }
20614
- content.append(tabs, hostSection);
20615
- };
20616
- const diagnostics = getDiagnostics();
20617
- render(diagnostics?.snapshot() ?? null);
20618
- if (!diagnostics) {
20619
- copyAll.disabled = true;
20620
- return void 0;
20621
- }
20622
- copyAll.addEventListener("click", () => {
20623
- const snapshot = diagnostics.snapshot();
20624
- const report = snapshot.hosts.flatMap(
20625
- (host) => host.agents.map(
20626
- (agent) => diagnosticText(host.hostId, RENDERER_AGENT_LABELS[agent.agent], agent)
20627
- )
20628
- ).join("\n\n");
20629
- copyDiagnosticsToClipboard(document2, copyAll, report, messages, messages.connectionCopyAll);
20630
- });
20631
- const unsubscribe = diagnostics.subscribe(() => render(diagnostics.snapshot()));
20632
- refresh.addEventListener("click", () => {
20633
- if (pending) return;
20634
- pending = true;
20635
- refresh.disabled = true;
20636
- refresh.replaceChildren(
20637
- createRendererSettingsIcon("diagnose", 16),
20638
- messages.connectionRefreshing
20639
- );
20640
- void context.runLatest(() => diagnostics.refresh(), {
20641
- success() {
20642
- pending = false;
20643
- refresh.disabled = false;
20644
- refresh.replaceChildren(
20645
- createRendererSettingsIcon("diagnose", 16),
20646
- messages.connectionRefresh
20647
- );
20648
- render(diagnostics.snapshot());
20649
- },
20650
- failure(error51) {
20651
- pending = false;
20652
- refresh.disabled = false;
20653
- refresh.replaceChildren(
20654
- createRendererSettingsIcon("diagnose", 16),
20655
- messages.connectionRefresh
20656
- );
20657
- const snapshot = diagnostics.snapshot();
20658
- render({
20659
- ...snapshot,
20660
- hosts: snapshot.hosts.map((host) => ({
20661
- ...host,
20662
- agents: host.agents.map((agent) => ({
20663
- ...agent,
20664
- availability: "error",
20665
- error: {
20666
- code: "internalError",
20667
- message: error51 instanceof Error ? error51.message : String(error51),
20668
- retryable: true,
20669
- stage: "request"
20670
- }
20671
- }))
20672
- }))
20673
- });
20674
- }
20675
- });
20676
- });
20677
- return unsubscribe;
21238
+
21239
+ // src/settings/update-request.ts
21240
+ var RENDERER_UPDATE_REQUEST_TIMEOUT_MS = 15e3;
21241
+ var RendererUpdateRequestTimeoutError = class extends Error {
21242
+ constructor() {
21243
+ super("Update request timed out");
21244
+ this.name = "RendererUpdateRequestTimeoutError";
21245
+ }
21246
+ };
21247
+ function runBoundedRendererUpdateRequest(operation, signal, timeoutMs = RENDERER_UPDATE_REQUEST_TIMEOUT_MS) {
21248
+ return new Promise((resolve, reject) => {
21249
+ let settled = false;
21250
+ const settle = (handler) => {
21251
+ if (settled) return;
21252
+ settled = true;
21253
+ clearTimeout(timeout);
21254
+ signal.removeEventListener("abort", abort);
21255
+ handler();
21256
+ };
21257
+ const timeout = setTimeout(() => {
21258
+ settle(() => reject(new RendererUpdateRequestTimeoutError()));
21259
+ }, timeoutMs);
21260
+ const abort = () => {
21261
+ settle(() => reject(new Error("Update request was aborted")));
21262
+ };
21263
+ const rejectRequest = (error51) => {
21264
+ settle(() => reject(error51));
21265
+ };
21266
+ if (signal.aborted) {
21267
+ abort();
21268
+ return;
21269
+ }
21270
+ signal.addEventListener("abort", abort, { once: true });
21271
+ try {
21272
+ void operation().then(
21273
+ (value) => settle(() => resolve(value)),
21274
+ (error51) => rejectRequest(error51)
21275
+ );
21276
+ } catch (error51) {
21277
+ rejectRequest(error51);
20678
21278
  }
20679
21279
  });
20680
21280
  }
20681
- function versionRow(context, label, version2) {
20682
- const row = context.content.ownerDocument.createElement("div");
20683
- row.className = "settings-update-version-row";
20684
- const name = context.content.ownerDocument.createElement("span");
20685
- name.textContent = label;
20686
- const value = context.content.ownerDocument.createElement("strong");
20687
- value.textContent = `v${version2}`;
20688
- row.append(name, value);
20689
- return row;
21281
+
21282
+ // src/settings/pages.ts
21283
+ var CODEXHOST_GITHUB_REPOSITORY_URL = "https://github.com/BytePioneer-AI/codex-host";
21284
+ var CODEXHOST_RELEASES_LATEST_URL = `${CODEXHOST_GITHUB_REPOSITORY_URL}/releases/latest`;
21285
+ var CODEXHOST_NPM_MANUAL_UPDATE_COMMAND = "npm install -g @codexhost/cli@latest";
21286
+ function rendererUserAgentData(navigator2) {
21287
+ return navigator2.userAgentData;
21288
+ }
21289
+ function isWindowsRenderer(window2) {
21290
+ const navigator2 = window2?.navigator;
21291
+ if (!navigator2) return false;
21292
+ const identity = `${rendererUserAgentData(navigator2)?.platform ?? ""} ${navigator2.platform ?? ""} ${navigator2.userAgent}`;
21293
+ return /windows|win32|win64/iu.test(identity);
21294
+ }
21295
+ function windowsInstallerDownloadUrl(window2, version2) {
21296
+ const navigator2 = window2?.navigator;
21297
+ const hints = navigator2 ? rendererUserAgentData(navigator2) : void 0;
21298
+ const identity = `${hints?.architecture ?? ""} ${hints?.platform ?? ""} ${navigator2?.platform ?? ""} ${navigator2?.userAgent ?? ""}`;
21299
+ const architecture = /arm64|aarch64|\barm\b/iu.test(identity) ? "arm64" : "x64";
21300
+ return `https://github.com/BytePioneer-AI/codex-host/releases/download/v${version2}/codexhost-${version2}-windows-${architecture}.exe`;
21301
+ }
21302
+ function panelIconName(view) {
21303
+ if (view === "failed" || view === "error") return "alert";
21304
+ if (view === "unavailable") return "unavailable";
21305
+ if (view === "current") return "check";
21306
+ return "updates";
21307
+ }
21308
+ function createPanelHead(document2, view, title) {
21309
+ const head = document2.createElement("div");
21310
+ head.className = "settings-update-panel__head";
21311
+ const label = document2.createElement("strong");
21312
+ label.className = "settings-update-panel__title";
21313
+ label.textContent = title;
21314
+ head.append(createRendererSettingsIcon(panelIconName(view), 16), label);
21315
+ return head;
21316
+ }
21317
+ function createPanelActions(document2, ...buttons) {
21318
+ const actions = document2.createElement("div");
21319
+ actions.className = "settings-update-actions";
21320
+ actions.append(...buttons);
21321
+ return actions;
20690
21322
  }
20691
21323
  function installationLabel(installation, messages) {
20692
21324
  if (installation === "npm") return messages.updateInstallationNpm;
@@ -20723,6 +21355,57 @@ ${error51.stderrTail}`] : []
20723
21355
  }
20724
21356
  return `${scaled.toFixed(scaled >= 10 ? 0 : 1)} ${unit}`;
20725
21357
  }
21358
+ function aboutPage(messages) {
21359
+ return Object.freeze({
21360
+ id: "about",
21361
+ label: messages.pageLabels.about,
21362
+ icon: "about",
21363
+ mount(context) {
21364
+ const document2 = context.content.ownerDocument;
21365
+ const heading = document2.createElement("div");
21366
+ heading.className = "settings-section-label";
21367
+ heading.textContent = messages.pageLabels.about;
21368
+ const panel = document2.createElement("section");
21369
+ panel.className = "settings-about-panel";
21370
+ const product = document2.createElement("strong");
21371
+ product.className = "settings-about-product";
21372
+ product.textContent = "CodexHost";
21373
+ const tagline = document2.createElement("strong");
21374
+ tagline.className = "settings-about-tagline";
21375
+ tagline.textContent = messages.aboutTagline;
21376
+ const introduction = document2.createElement("div");
21377
+ introduction.className = "settings-about-copy";
21378
+ for (const paragraphText of messages.aboutParagraphs) {
21379
+ const paragraph = document2.createElement("p");
21380
+ paragraph.textContent = paragraphText;
21381
+ introduction.append(paragraph);
21382
+ }
21383
+ const starCallout = document2.createElement("p");
21384
+ starCallout.className = "settings-about-star-callout";
21385
+ starCallout.textContent = messages.aboutStarCallout;
21386
+ const repositorySection = document2.createElement("div");
21387
+ repositorySection.className = "settings-about-repository";
21388
+ const openSource = document2.createElement("p");
21389
+ openSource.textContent = messages.aboutOpenSource;
21390
+ const repository = document2.createElement("a");
21391
+ repository.className = "settings-about-repository-link";
21392
+ repository.href = CODEXHOST_GITHUB_REPOSITORY_URL;
21393
+ repository.target = "_blank";
21394
+ repository.rel = "noopener noreferrer";
21395
+ const repositoryUrl = document2.createElement("code");
21396
+ repositoryUrl.textContent = CODEXHOST_GITHUB_REPOSITORY_URL;
21397
+ repository.append(
21398
+ createRendererSettingsIcon("external-link", 14),
21399
+ messages.aboutRepository,
21400
+ repositoryUrl
21401
+ );
21402
+ repositorySection.append(openSource, repository);
21403
+ panel.append(product, tagline, introduction, starCallout, repositorySection);
21404
+ context.content.append(heading, panel);
21405
+ return void 0;
21406
+ }
21407
+ });
21408
+ }
20726
21409
  function updatesPage(messages, getClient) {
20727
21410
  return Object.freeze({
20728
21411
  id: "updates",
@@ -20730,37 +21413,90 @@ ${error51.stderrTail}`] : []
20730
21413
  icon: "updates",
20731
21414
  mount(context) {
20732
21415
  const document2 = context.content.ownerDocument;
21416
+ const windows = isWindowsRenderer(document2.defaultView);
20733
21417
  const heading = document2.createElement("div");
20734
21418
  heading.className = "settings-section-label";
20735
21419
  heading.textContent = messages.pageLabels.updates;
20736
21420
  const metadata = document2.createElement("div");
20737
21421
  metadata.className = "settings-update-metadata";
20738
- const currentVersion = document2.createElement("div");
20739
- currentVersion.className = "settings-update-metadata__item";
20740
- const currentVersionLabel = document2.createElement("span");
20741
- currentVersionLabel.textContent = messages.updateCurrentVersion;
20742
- const currentVersionValue = document2.createElement("strong");
20743
- currentVersionValue.textContent = "-";
20744
- currentVersion.append(currentVersionLabel, currentVersionValue);
20745
- const installation = document2.createElement("div");
20746
- installation.className = "settings-update-metadata__item";
20747
- const installationName = document2.createElement("span");
20748
- installationName.textContent = messages.updateInstallation;
20749
- const installationValue = document2.createElement("strong");
20750
- installationValue.textContent = "-";
20751
- installation.append(installationName, installationValue);
20752
- metadata.append(currentVersion, installation);
21422
+ const createMetadataItem = (label) => {
21423
+ const item = document2.createElement("div");
21424
+ item.className = "settings-update-metadata__item";
21425
+ const name = document2.createElement("span");
21426
+ name.textContent = label;
21427
+ const value = document2.createElement("strong");
21428
+ value.textContent = "-";
21429
+ item.append(name, value);
21430
+ metadata.append(item);
21431
+ return value;
21432
+ };
21433
+ const currentVersionValue = createMetadataItem(messages.updateCurrentVersion);
21434
+ const latestVersionValue = createMetadataItem(messages.updateLatestVersion);
21435
+ const installationValue = createMetadataItem(messages.updateInstallation);
20753
21436
  const panel = document2.createElement("section");
20754
21437
  panel.className = "settings-update-panel";
20755
21438
  panel.setAttribute("aria-live", "polite");
21439
+ const controls = document2.createElement("div");
21440
+ controls.className = "settings-update-controls";
21441
+ const manualTitle = document2.createElement("div");
21442
+ manualTitle.className = "settings-update-manual-title";
21443
+ manualTitle.textContent = messages.updateManualTitle;
20756
21444
  const manualNpm = document2.createElement("div");
20757
21445
  manualNpm.className = "settings-update-manual";
20758
21446
  manualNpm.hidden = true;
20759
- const manualNpmDescription = document2.createElement("span");
21447
+ const manualNpmDescription = document2.createElement("p");
21448
+ manualNpmDescription.className = "settings-update-manual-description";
20760
21449
  manualNpmDescription.textContent = messages.updateManualNpmDescription;
21450
+ const manualNpmCommandRow = document2.createElement("div");
21451
+ manualNpmCommandRow.className = "settings-update-command";
20761
21452
  const manualNpmCommand = document2.createElement("code");
20762
21453
  manualNpmCommand.textContent = CODEXHOST_NPM_MANUAL_UPDATE_COMMAND;
20763
- manualNpm.append(manualNpmDescription, manualNpmCommand);
21454
+ const copyCommand = document2.createElement("button");
21455
+ copyCommand.type = "button";
21456
+ copyCommand.className = "settings-update-command__copy";
21457
+ const setCopyLabel = (label) => {
21458
+ copyCommand.replaceChildren(createRendererSettingsIcon("copy", 14), label);
21459
+ };
21460
+ setCopyLabel(messages.updateCopyCommand);
21461
+ copyCommand.addEventListener("click", () => {
21462
+ const clipboard = document2.defaultView?.navigator.clipboard;
21463
+ const restore = (label) => {
21464
+ setCopyLabel(label);
21465
+ document2.defaultView?.setTimeout(() => setCopyLabel(messages.updateCopyCommand), 2e3);
21466
+ };
21467
+ if (!clipboard) {
21468
+ restore(messages.updateCopyFailed);
21469
+ return;
21470
+ }
21471
+ void clipboard.writeText(CODEXHOST_NPM_MANUAL_UPDATE_COMMAND).then(
21472
+ () => restore(messages.updateCommandCopied),
21473
+ () => restore(messages.updateCopyFailed)
21474
+ );
21475
+ });
21476
+ manualNpmCommandRow.append(manualNpmCommand, copyCommand);
21477
+ manualNpm.append(manualNpmDescription, manualNpmCommandRow);
21478
+ const manualWindowsInstaller = document2.createElement("div");
21479
+ manualWindowsInstaller.className = "settings-update-manual";
21480
+ manualWindowsInstaller.hidden = true;
21481
+ const manualWindowsInstallerDescription = document2.createElement("p");
21482
+ manualWindowsInstallerDescription.className = "settings-update-manual-description";
21483
+ manualWindowsInstallerDescription.textContent = messages.updateWindowsInstallerDescription;
21484
+ const manualWindowsInstallerActions = document2.createElement("div");
21485
+ manualWindowsInstallerActions.className = "settings-update-actions";
21486
+ const manualWindowsInstallerLink = document2.createElement("a");
21487
+ manualWindowsInstallerLink.className = "settings-update-link";
21488
+ manualWindowsInstallerLink.href = CODEXHOST_RELEASES_LATEST_URL;
21489
+ manualWindowsInstallerLink.target = "_blank";
21490
+ manualWindowsInstallerLink.rel = "noopener noreferrer";
21491
+ manualWindowsInstallerLink.append(
21492
+ messages.updateDownloadWindowsInstaller,
21493
+ createRendererSettingsIcon("external-link", 14)
21494
+ );
21495
+ manualWindowsInstallerActions.append(manualWindowsInstallerLink);
21496
+ manualWindowsInstaller.append(
21497
+ manualWindowsInstallerDescription,
21498
+ manualWindowsInstallerActions
21499
+ );
20764
21500
  const actions = document2.createElement("div");
20765
21501
  actions.className = "settings-update-actions";
20766
21502
  const releaseLink = document2.createElement("a");
@@ -20773,7 +21509,14 @@ ${error51.stderrTail}`] : []
20773
21509
  createRendererSettingsIcon("external-link", 14)
20774
21510
  );
20775
21511
  actions.append(releaseLink);
20776
- context.content.append(heading, metadata, panel, manualNpm, actions);
21512
+ controls.append(manualTitle, manualNpm, manualWindowsInstaller, actions);
21513
+ const notes = document2.createElement("div");
21514
+ notes.className = "settings-update-notes-section";
21515
+ context.content.append(heading, metadata, panel, controls, notes);
21516
+ const setManualFallback = (fallback) => {
21517
+ manualNpmDescription.textContent = windows ? messages.updateWindowsNpmDescription : fallback ? messages.updateManualFallbackDescription : messages.updateManualNpmDescription;
21518
+ manualNpmDescription.className = fallback ? "settings-update-manual-description is-fallback" : "settings-update-manual-description";
21519
+ };
20777
21520
  let pollTimer;
20778
21521
  let pollAttempts = 0;
20779
21522
  let pending = false;
@@ -20786,11 +21529,11 @@ ${error51.stderrTail}`] : []
20786
21529
  const renderUnavailable = (detail) => {
20787
21530
  panel.dataset.updateState = "unavailable";
20788
21531
  panel.replaceChildren();
20789
- const title = document2.createElement("strong");
20790
- title.textContent = messages.notAvailable;
20791
- const copy = document2.createElement("span");
21532
+ const copy = document2.createElement("p");
21533
+ copy.className = "settings-update-summary";
20792
21534
  copy.textContent = detail;
20793
- panel.append(title, copy);
21535
+ panel.append(createPanelHead(document2, "unavailable", messages.notAvailable), copy);
21536
+ notes.replaceChildren();
20794
21537
  };
20795
21538
  const renderRequestFailure = (error51) => {
20796
21539
  renderPendingStatus(
@@ -20826,9 +21569,8 @@ ${error51.stderrTail}`] : []
20826
21569
  const renderPendingStatus = (status, message, viewPhase = status?.phase ?? "pending") => {
20827
21570
  panel.dataset.updateState = viewPhase;
20828
21571
  panel.replaceChildren();
20829
- const state = document2.createElement("strong");
20830
- state.textContent = message;
20831
- panel.append(state);
21572
+ panel.append(createPanelHead(document2, viewPhase, message));
21573
+ setManualFallback(viewPhase === "failed");
20832
21574
  if (status?.phase === "downloading" && status.totalBytes !== void 0 && status.downloadedBytes !== void 0) {
20833
21575
  const progress = document2.createElement("progress");
20834
21576
  progress.className = "settings-update-progress";
@@ -20850,7 +21592,7 @@ ${error51.stderrTail}`] : []
20850
21592
  retry.className = "settings-command-button";
20851
21593
  retry.append(createRendererSettingsIcon("refresh", 16), messages.updateRetry);
20852
21594
  retry.addEventListener("click", () => void load());
20853
- panel.append(retry);
21595
+ panel.append(createPanelActions(document2, retry));
20854
21596
  }
20855
21597
  };
20856
21598
  const start = (client) => {
@@ -20877,8 +21619,19 @@ ${error51.stderrTail}`] : []
20877
21619
  };
20878
21620
  const renderCheck = (result, client) => {
20879
21621
  currentVersionValue.textContent = `v${result.currentVersion}`;
21622
+ latestVersionValue.textContent = result.latestVersion ? `v${result.latestVersion}` : "-";
21623
+ latestVersionValue.className = result.updateAvailable ? "settings-update-metadata__value--newer" : "";
20880
21624
  installationValue.textContent = installationLabel(result.installation, messages);
20881
21625
  manualNpm.hidden = result.installation !== "npm";
21626
+ manualWindowsInstaller.hidden = !windows || result.installation !== "windows-installer";
21627
+ releaseLink.hidden = windows;
21628
+ manualTitle.hidden = windows && !["npm", "windows-installer"].includes(result.installation ?? "");
21629
+ if (windows && result.installation === "windows-installer" && result.latestVersion) {
21630
+ manualWindowsInstallerLink.href = windowsInstallerDownloadUrl(
21631
+ document2.defaultView,
21632
+ result.latestVersion
21633
+ );
21634
+ }
20882
21635
  if (result.releaseNotesUrl) releaseLink.href = result.releaseNotesUrl;
20883
21636
  const operationMessage = statusMessage(result.status, messages);
20884
21637
  if (isPendingStatus(result.status)) {
@@ -20886,42 +21639,53 @@ ${error51.stderrTail}`] : []
20886
21639
  scheduleStatusPoll(client, true);
20887
21640
  return;
20888
21641
  }
20889
- panel.dataset.updateState = result.error ? "error" : result.updateAvailable ? "available" : "current";
21642
+ const actionableStatus = result.status?.phase === "failed" && result.status.version === result.latestVersion ? result.status : null;
21643
+ const view = result.error ? "error" : result.updateAvailable ? "available" : "current";
21644
+ panel.dataset.updateState = view;
20890
21645
  panel.replaceChildren();
20891
- if (result.latestVersion) {
20892
- panel.append(versionRow(context, messages.updateLatestVersion, result.latestVersion));
21646
+ setManualFallback(Boolean(result.error) || actionableStatus !== null);
21647
+ if (result.error || !result.updateAvailable || windows || actionableStatus) {
21648
+ panel.append(
21649
+ createPanelHead(
21650
+ document2,
21651
+ view,
21652
+ actionableStatus ? statusMessage(actionableStatus, messages) ?? messages.updateFailed : result.error ? messages.updateFailed : result.updateAvailable ? messages.updateWindowsManualRequired : messages.updateUpToDate
21653
+ )
21654
+ );
20893
21655
  }
20894
- const summary = document2.createElement("p");
20895
- summary.className = "settings-update-summary";
20896
- summary.textContent = operationMessage ?? (result.error ? messages.updateFailed : result.updateAvailable ? messages.updateAvailable : messages.updateUpToDate);
20897
- panel.append(summary);
20898
- if (result.releaseNotes) {
20899
- panel.append(createReleaseNotesElement(document2, result.releaseNotes));
21656
+ if (actionableStatus?.error) {
21657
+ const error51 = document2.createElement("p");
21658
+ error51.className = "settings-update-error";
21659
+ error51.textContent = actionableStatus.error;
21660
+ panel.append(error51);
20900
21661
  }
20901
- if (result.status?.phase === "failed" && result.status.error) {
21662
+ if (result.error) {
20902
21663
  const error51 = document2.createElement("p");
20903
21664
  error51.className = "settings-update-error";
20904
- error51.textContent = result.status.error;
21665
+ error51.textContent = result.error;
20905
21666
  panel.append(error51);
20906
21667
  }
20907
- if (result.updateAvailable && result.installationAvailable) {
21668
+ const buttons = [];
21669
+ if (!windows && result.updateAvailable && result.installationAvailable) {
20908
21670
  const update = document2.createElement("button");
20909
21671
  update.type = "button";
20910
21672
  update.className = "settings-command-button";
20911
21673
  update.append(createRendererSettingsIcon("updates", 16), messages.updateAndRestart);
20912
21674
  update.addEventListener("click", () => start(client));
20913
- panel.append(update);
21675
+ buttons.push(update);
20914
21676
  }
20915
21677
  if (result.error) {
20916
- const error51 = document2.createElement("p");
20917
- error51.className = "settings-update-error";
20918
- error51.textContent = result.error;
20919
21678
  const retry = document2.createElement("button");
20920
21679
  retry.type = "button";
20921
21680
  retry.className = "settings-command-button settings-command-button--secondary";
20922
21681
  retry.append(createRendererSettingsIcon("refresh", 16), messages.updateRetry);
20923
21682
  retry.addEventListener("click", () => void load());
20924
- panel.append(error51, retry);
21683
+ buttons.push(retry);
21684
+ }
21685
+ if (buttons.length > 0) panel.append(createPanelActions(document2, ...buttons));
21686
+ notes.replaceChildren();
21687
+ if (result.releaseNotes) {
21688
+ notes.append(createReleaseNotesElement(document2, result.releaseNotes));
20925
21689
  }
20926
21690
  };
20927
21691
  const load = () => {
@@ -20952,13 +21716,10 @@ ${error51.stderrTail}`] : []
20952
21716
  });
20953
21717
  }
20954
21718
  function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null) {
20955
- const unavailableIds = DEFAULT_RENDERER_SETTINGS_PAGE_IDS.filter(
20956
- (id) => id !== "updates" && id !== "connections"
20957
- );
20958
21719
  return Object.freeze([
20959
- connectionsPage(messages, getDiagnostics),
20960
- ...unavailableIds.map((id) => unavailablePage(id, messages)),
20961
- updatesPage(messages, getUpdateClient)
21720
+ createConnectionsSettingsPage(messages, getDiagnostics),
21721
+ updatesPage(messages, getUpdateClient),
21722
+ aboutPage(messages)
20962
21723
  ]);
20963
21724
  }
20964
21725
  function createDefaultRendererSettingsRegistry(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null) {
@@ -20968,11 +21729,10 @@ ${error51.stderrTail}`] : []
20968
21729
  }
20969
21730
 
20970
21731
  // src/settings/shell.css
20971
- var shell_default = ':host {\n --settings-bg: #181818;\n --settings-sidebar: #1c1c1c;\n --settings-panel: transparent;\n --settings-text: #f5f5f5;\n --settings-muted: #a1a1a1;\n --settings-border: rgb(255 255 255 / 10%);\n --settings-divider: rgb(255 255 255 / 10%);\n --settings-hover: rgb(255 255 255 / 6%);\n --settings-active: rgb(51 156 255 / 12%);\n --settings-focus: #339cff;\n color: var(--settings-text);\n color-scheme: dark;\n font:\n 14px/1.5 system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n letter-spacing: 0;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n letter-spacing: 0;\n}\n\nbutton,\ninput,\nselect {\n font: inherit;\n}\n\nbutton {\n color: inherit;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.codexhost-settings-dialog {\n width: min(1120px, calc(100vw - 32px));\n height: min(780px, calc(100vh - 32px));\n max-width: none;\n max-height: none;\n margin: auto;\n padding: 0;\n overflow: hidden;\n color: var(--settings-text);\n background: var(--settings-bg);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n box-shadow: 0 24px 64px rgb(0 0 0 / 38%);\n}\n\n.codexhost-settings-dialog::backdrop {\n background: rgb(0 0 0 / 52%);\n}\n\n.settings-frame,\n.settings-layout {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n}\n\n.settings-frame {\n display: flex;\n flex-direction: column;\n}\n\n.settings-layout {\n flex: 1;\n display: grid;\n grid-template-columns: 240px minmax(0, 1fr);\n background: var(--settings-bg);\n}\n\n.settings-sidebar {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--settings-sidebar);\n border-right: 1px solid var(--settings-border);\n padding-top: 28px;\n}\n\n.settings-header {\n display: flex;\n align-items: center;\n min-width: 0;\n height: 64px;\n flex: none;\n padding: 0 16px;\n border-bottom: 1px solid var(--settings-border);\n}\n\n.settings-header-actions {\n display: flex;\n align-items: center;\n flex: none;\n gap: 12px;\n margin-left: auto;\n}\n\n.settings-icon-button.settings-star-link {\n width: auto;\n gap: 6px;\n padding-inline: 8px;\n font-size: 12px;\n line-height: 18px;\n text-decoration: none;\n border: 2px dashed #f5c542;\n white-space: nowrap;\n}\n\n.settings-star-link .codexhost-settings-icon {\n color: #f5c542;\n fill: currentColor;\n stroke: currentColor;\n}\n\n.settings-brand {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-brand__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n flex: none;\n color: var(--settings-text);\n}\n\n.settings-brand__mark .codexhost-settings-icon {\n width: 32px;\n height: 32px;\n object-fit: contain;\n}\n\n.settings-brand__copy {\n display: flex;\n min-width: 0;\n align-items: baseline;\n gap: 0;\n line-height: 20px;\n}\n\n.settings-brand__name {\n overflow: hidden;\n color: var(--settings-text);\n font-size: 14px;\n font-weight: 500;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-brand__title {\n margin-left: 14px;\n padding-left: 14px;\n color: var(--settings-muted);\n font-size: 14px;\n font-weight: 400;\n border-left: 1px solid var(--settings-border);\n}\n\n.settings-nav {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n padding: 0 12px 16px;\n overflow-y: auto;\n}\n\n.settings-nav-button {\n display: grid;\n grid-template-columns: 16px minmax(0, 1fr);\n align-items: center;\n width: 100%;\n position: relative;\n min-height: 40px;\n flex: none;\n gap: 12px;\n padding: 8px 12px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 21px;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.settings-nav-button .codexhost-settings-icon {\n width: 18px;\n height: 18px;\n opacity: 0.9;\n}\n\n.settings-nav-button:hover {\n background: var(--settings-hover);\n}\n\n.settings-nav-button[aria-current="page"] {\n background: var(--settings-active);\n color: var(--settings-focus);\n}\n\n.settings-nav-button[aria-current="page"]::before {\n position: absolute;\n left: 0;\n width: 3px;\n height: 22px;\n content: "";\n background: var(--settings-focus);\n border-radius: 0 3px 3px 0;\n}\n\n.settings-nav-button span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n padding: 0;\n color: var(--settings-muted);\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.settings-icon-button:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-icon-button:focus-visible,\n.settings-nav-button:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 1px;\n}\n\n.settings-page {\n display: block;\n position: relative;\n min-width: 0;\n min-height: 0;\n overflow-y: auto;\n background: var(--settings-bg);\n scrollbar-gutter: stable;\n}\n\n.settings-page__content {\n width: min(930px, calc(100% - 80px));\n margin-inline: auto;\n}\n\n.settings-page__content {\n min-width: 0;\n padding: 44px 0 56px;\n}\n\n.settings-section-label {\n min-height: auto;\n padding: 0 0 28px;\n color: var(--settings-text);\n font-size: 24px;\n font-weight: 600;\n line-height: 30px;\n}\n\n.settings-status-list,\n.settings-empty {\n overflow: hidden;\n background: var(--settings-panel);\n}\n\n.settings-status-row {\n display: grid;\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\n position: relative;\n align-items: center;\n min-height: 88px;\n gap: 28px;\n padding: 16px 0;\n}\n\n.settings-status-row:not(:last-child)::after {\n content: "";\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 1px;\n background: var(--settings-divider);\n}\n\n.settings-status-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--settings-text);\n gap: 14px;\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-status-row__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n flex: none;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 7%);\n border-radius: 50%;\n}\n\n.settings-status-badge {\n flex: none;\n padding: 5px 12px;\n color: var(--settings-muted);\n font-size: 13px;\n font-weight: 500;\n line-height: 18px;\n background: rgb(255 255 255 / 9%);\n border: 1px solid rgb(255 255 255 / 6%);\n border-radius: 6px;\n}\n\n.settings-status-row__detail {\n min-width: 0;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-page-description {\n max-width: 760px;\n margin: -12px 0 24px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-connection-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n.settings-connection-list {\n min-width: 0;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-connection-host-tabs {\n display: flex;\n gap: 6px;\n padding: 20px 0 8px;\n overflow-x: auto;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-host-tab {\n flex: none;\n padding: 7px 12px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: var(--settings-muted);\n font: inherit;\n font-size: 13px;\n line-height: 18px;\n cursor: pointer;\n}\n\n.settings-connection-host-tab:hover {\n background: var(--settings-hover);\n color: var(--settings-text);\n}\n\n.settings-connection-host-tab[aria-selected="true"] {\n background: var(--settings-active);\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-connection-host-tab:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: -2px;\n}\n\n.settings-connection-row {\n display: grid;\n grid-template-columns: minmax(180px, 1fr) auto minmax(260px, 1.5fr);\n align-items: center;\n min-width: 0;\n min-height: 76px;\n gap: 24px;\n padding: 14px 0;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n gap: 10px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 20px;\n}\n\n.settings-connection-row__identity strong {\n min-width: 0;\n overflow-wrap: anywhere;\n font-weight: 500;\n}\n\n.settings-connection-row__dot {\n width: 9px;\n height: 9px;\n flex: none;\n border-radius: 50%;\n background: var(--settings-muted);\n}\n\n.settings-connection-row__dot[data-connection-tone="ready"] {\n background: #22c55e;\n}\n\n.settings-connection-row__dot[data-connection-tone="checking"] {\n background: #f5c542;\n}\n\n.settings-connection-row__dot[data-connection-tone="failed"] {\n background: #ef4444;\n}\n\n.settings-status-badge[data-connection-tone="ready"] {\n color: #4ade80;\n}\n\n.settings-status-badge[data-connection-tone="checking"] {\n color: #f5c542;\n}\n\n.settings-status-badge[data-connection-tone="failed"] {\n color: #f87171;\n}\n\n.settings-connection-row__detail {\n display: grid;\n min-width: 0;\n align-items: center;\n gap: 2px 12px;\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-connection-row__reason {\n min-width: 0;\n overflow-wrap: anywhere;\n white-space: pre-wrap;\n}\n\n.settings-connection-details-toggle {\n width: fit-content;\n padding: 0;\n color: var(--settings-focus);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n background: transparent;\n border: 0;\n cursor: pointer;\n}\n\n.settings-connection-details-toggle:hover {\n text-decoration: underline;\n}\n\n.settings-connection-details {\n display: grid;\n grid-column: 1 / -1;\n gap: 6px;\n min-width: 0;\n margin: 4px 0 2px 32px;\n padding: 12px;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 4%);\n border-left: 2px solid #ef4444;\n border-radius: 4px;\n}\n\n.settings-connection-detail-line {\n display: grid;\n grid-template-columns: 112px minmax(0, 1fr);\n gap: 12px;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-connection-detail-line span {\n color: var(--settings-muted);\n}\n\n.settings-connection-detail-line code {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n white-space: pre-wrap;\n}\n\n.settings-connection-stderr {\n max-height: 220px;\n min-width: 0;\n margin: 4px 0 0;\n padding: 10px;\n overflow: auto;\n color: #fca5a5;\n font:\n 12px/18px ui-monospace,\n "SFMono-Regular",\n Consolas,\n "Liberation Mono",\n monospace;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n background: rgb(0 0 0 / 28%);\n border-radius: 4px;\n}\n\n.settings-connection-details .settings-command-button {\n justify-self: start;\n min-height: 30px;\n font-size: 12px;\n}\n\n.settings-connection-row__detail time {\n color: var(--settings-muted);\n opacity: 0.8;\n}\n\n.settings-empty {\n display: flex;\n align-items: center;\n min-height: 72px;\n padding: 12px 16px;\n}\n\n.settings-empty > div {\n display: grid;\n min-width: 0;\n gap: 2px;\n}\n\n.settings-empty strong {\n color: var(--settings-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 19px;\n}\n\n.settings-empty span {\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 19px;\n}\n\n.settings-page-error {\n padding: 16px;\n color: var(--settings-text);\n font-size: 13px;\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 20px;\n}\n\n.settings-update-metadata {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 24px;\n margin-bottom: 24px;\n padding: 0 4px 20px;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-update-metadata__item {\n display: grid;\n min-width: 0;\n gap: 4px;\n}\n\n.settings-update-metadata__item span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-metadata__item strong {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-update-panel {\n display: grid;\n gap: 16px;\n min-height: 128px;\n padding: 20px;\n color: var(--settings-text);\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n}\n\n.settings-update-panel > strong,\n.settings-update-panel > span,\n.settings-update-summary,\n.settings-update-error {\n margin: 0;\n overflow-wrap: anywhere;\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-panel > span,\n.settings-update-summary {\n color: var(--settings-muted);\n}\n\n.settings-update-error {\n color: #ef4444;\n}\n\n.settings-update-progress {\n width: 100%;\n height: 8px;\n accent-color: #238be8;\n}\n\n.settings-update-progress-detail {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-notes {\n display: grid;\n min-width: 0;\n gap: 12px;\n color: var(--settings-muted);\n font-size: 14px;\n line-height: 22px;\n overflow-wrap: anywhere;\n}\n\n.settings-update-notes > :first-child {\n margin-top: 0;\n}\n\n.settings-update-notes > :last-child {\n margin-bottom: 0;\n}\n\n.settings-update-notes h1,\n.settings-update-notes h2,\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n margin: 6px 0 0;\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes h1 {\n font-size: 18px;\n line-height: 26px;\n}\n\n.settings-update-notes h2 {\n font-size: 17px;\n line-height: 24px;\n}\n\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n font-size: 14px;\n line-height: 22px;\n}\n\n.settings-update-notes p,\n.settings-update-notes ul,\n.settings-update-notes ol,\n.settings-update-notes pre,\n.settings-update-notes blockquote {\n margin: 0;\n}\n\n.settings-update-notes ul,\n.settings-update-notes ol {\n padding-left: 24px;\n}\n\n.settings-update-notes li {\n padding-left: 4px;\n}\n\n.settings-update-notes li::marker {\n color: var(--settings-muted);\n font-size: 0.85em;\n}\n\n.settings-update-notes li + li {\n margin-top: 14px;\n}\n\n.settings-update-notes .release-note-translation {\n display: block;\n margin-top: 3px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-notes blockquote {\n padding: 8px 12px;\n color: var(--settings-muted);\n border-left: 3px solid var(--settings-focus);\n background: rgb(255 255 255 / 4%);\n}\n\n.settings-update-notes hr {\n width: 100%;\n margin: 2px 0;\n border: 0;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-update-notes strong {\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes a {\n color: #7cb8ff;\n text-decoration: none;\n}\n\n.settings-update-notes a:hover {\n text-decoration: underline;\n}\n\n.settings-update-notes code {\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n}\n\n.settings-update-notes :not(pre) > code {\n padding: 1px 5px;\n background: rgb(255 255 255 / 8%);\n border-radius: 4px;\n}\n\n.settings-update-notes pre {\n min-width: 0;\n max-width: 100%;\n padding: 10px 12px;\n overflow-x: auto;\n overflow-y: hidden;\n background: rgb(0 0 0 / 28%);\n border-radius: 6px;\n}\n\n.settings-update-notes pre code {\n line-height: 18px;\n white-space: pre-wrap;\n}\n\n.settings-update-version-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: 20px;\n min-height: 32px;\n color: var(--settings-muted);\n font-size: 13px;\n}\n\n.settings-update-version-row strong {\n color: var(--settings-text);\n font-size: 15px;\n}\n\n.settings-update-manual {\n display: grid;\n min-width: 0;\n gap: 6px;\n margin-top: 14px;\n padding-inline: 4px;\n}\n\n.settings-update-manual[hidden] {\n display: none;\n}\n\n.settings-update-manual span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-manual code {\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-actions {\n display: flex;\n min-width: 0;\n margin-top: 16px;\n}\n\n.settings-command-button,\n.settings-update-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n min-height: 36px;\n gap: 8px;\n padding: 8px 12px;\n color: white;\n font: inherit;\n font-size: 13px;\n text-decoration: none;\n background: #1677d2;\n border: 1px solid #238be8;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.settings-command-button--secondary,\n.settings-update-link {\n color: var(--settings-text);\n background: transparent;\n border-color: var(--settings-border);\n}\n\n.settings-command-button:hover,\n.settings-update-link:hover {\n filter: brightness(1.08);\n}\n\n.settings-command-button:focus-visible,\n.settings-update-link:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 2px;\n}\n\n.codexhost-settings-icon {\n display: block;\n flex: none;\n stroke: currentColor;\n}\n\n@media (max-width: 720px) {\n .codexhost-settings-dialog {\n width: calc(100vw - 16px);\n height: calc(100vh - 16px);\n border-radius: 10px;\n }\n\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n }\n\n .settings-sidebar {\n border-right: 0;\n border-bottom: 1px solid var(--settings-border);\n }\n\n .settings-header {\n height: 56px;\n padding-inline: 14px;\n }\n\n .settings-icon-button.settings-star-link {\n width: 28px;\n padding: 0;\n }\n\n .settings-star-link span {\n display: none;\n }\n\n .settings-sidebar {\n padding-top: 20px;\n }\n\n .settings-nav {\n flex: none;\n flex-direction: row;\n gap: 4px;\n padding: 7px 8px 8px;\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .settings-nav-button {\n width: auto;\n min-width: max-content;\n min-height: 34px;\n grid-template-columns: 16px auto;\n border-radius: 10px;\n }\n\n .settings-page__content {\n width: calc(100% - 40px);\n }\n\n .settings-page__content {\n padding-top: 32px;\n padding-bottom: 32px;\n }\n\n .settings-section-label {\n padding-bottom: 20px;\n font-size: 20px;\n line-height: 26px;\n }\n\n .settings-status-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 16px;\n }\n\n .settings-connection-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 10px 16px;\n }\n\n .settings-connection-row__detail {\n grid-column: 1 / -1;\n }\n\n .settings-status-row__detail {\n grid-column: 1 / -1;\n margin: -8px 0 0 58px;\n }\n}\n\n@media (forced-colors: active) {\n :host,\n :host([data-theme="dark"]) {\n --settings-bg: Canvas;\n --settings-sidebar: Canvas;\n --settings-panel: Canvas;\n --settings-text: CanvasText;\n --settings-muted: GrayText;\n --settings-border: ButtonBorder;\n --settings-divider: ButtonBorder;\n --settings-hover: Highlight;\n --settings-active: Highlight;\n --settings-focus: Highlight;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n }\n}\n';
21732
+ var shell_default = ':host {\n --settings-bg: #181818;\n --settings-sidebar: #1c1c1c;\n --settings-panel: transparent;\n --settings-text: #f5f5f5;\n --settings-muted: #a1a1a1;\n --settings-border: rgb(255 255 255 / 10%);\n --settings-divider: rgb(255 255 255 / 10%);\n --settings-hover: rgb(255 255 255 / 6%);\n --settings-active: rgb(51 156 255 / 12%);\n --settings-focus: #339cff;\n color: var(--settings-text);\n color-scheme: dark;\n font:\n 14px/1.5 system-ui,\n -apple-system,\n BlinkMacSystemFont,\n "Segoe UI",\n sans-serif;\n letter-spacing: 0;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n letter-spacing: 0;\n}\n\nbutton,\ninput,\nselect {\n font: inherit;\n}\n\nbutton {\n color: inherit;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.codexhost-settings-dialog {\n width: min(1120px, calc(100vw - 32px));\n height: min(780px, calc(100vh - 32px));\n max-width: none;\n max-height: none;\n margin: auto;\n padding: 0;\n overflow: hidden;\n color: var(--settings-text);\n background: var(--settings-bg);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n box-shadow: 0 24px 64px rgb(0 0 0 / 38%);\n}\n\n.codexhost-settings-dialog::backdrop {\n background: rgb(0 0 0 / 52%);\n}\n\n.settings-frame,\n.settings-layout {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n}\n\n.settings-frame {\n display: flex;\n flex-direction: column;\n}\n\n.settings-layout {\n flex: 1;\n display: grid;\n grid-template-columns: 240px minmax(0, 1fr);\n background: var(--settings-bg);\n}\n\n.settings-sidebar {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n background: var(--settings-sidebar);\n border-right: 1px solid var(--settings-border);\n padding-top: 28px;\n}\n\n.settings-header {\n display: flex;\n align-items: center;\n min-width: 0;\n height: 64px;\n flex: none;\n padding: 0 16px;\n border-bottom: 1px solid var(--settings-border);\n}\n\n.settings-header-actions {\n display: flex;\n align-items: center;\n flex: none;\n gap: 12px;\n margin-left: auto;\n}\n\n.settings-brand {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-brand__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n flex: none;\n color: var(--settings-text);\n}\n\n.settings-brand__mark .codexhost-settings-icon {\n width: 32px;\n height: 32px;\n object-fit: contain;\n}\n\n.settings-brand__copy {\n display: flex;\n min-width: 0;\n align-items: baseline;\n gap: 0;\n line-height: 20px;\n}\n\n.settings-brand__name {\n overflow: hidden;\n color: var(--settings-text);\n font-size: 14px;\n font-weight: 500;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-brand__title {\n margin-left: 14px;\n padding-left: 14px;\n color: var(--settings-muted);\n font-size: 14px;\n font-weight: 400;\n border-left: 1px solid var(--settings-border);\n}\n\n.settings-nav {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex: 1;\n flex-direction: column;\n gap: 4px;\n padding: 0 12px 16px;\n overflow-y: auto;\n}\n\n.settings-nav-section-label {\n min-height: 28px;\n padding: 7px 12px 3px;\n color: #777;\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n}\n\n.settings-nav-section-label:not(:first-child) {\n margin-top: 14px;\n}\n\n.settings-nav-button {\n display: grid;\n grid-template-columns: 16px minmax(0, 1fr);\n align-items: center;\n width: 100%;\n position: relative;\n min-height: 48px;\n flex: none;\n gap: 14px;\n padding: 10px 14px;\n color: var(--settings-text);\n font-size: 14px;\n line-height: 21px;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 12px;\n cursor: pointer;\n}\n\n.settings-nav-button .codexhost-settings-icon {\n width: 18px;\n height: 18px;\n opacity: 0.9;\n}\n\n.settings-nav-button:hover {\n background: var(--settings-hover);\n}\n\n.settings-nav-button[aria-current="page"] {\n background: var(--settings-active);\n color: var(--settings-focus);\n}\n\n.settings-nav-button[aria-current="page"]::before {\n position: absolute;\n left: 0;\n width: 3px;\n height: 22px;\n content: "";\n background: var(--settings-focus);\n border-radius: 0 3px 3px 0;\n}\n\n.settings-nav-button span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-nav-star-link {\n margin-top: 0;\n color: var(--settings-text);\n text-decoration: none;\n}\n\n.settings-nav-star-link .codexhost-settings-icon {\n color: #f5c542;\n}\n\n.settings-nav-star-link:hover {\n color: #fff4bd;\n background: rgb(245 197 66 / 8%);\n}\n\n.settings-icon-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n padding: 0;\n color: var(--settings-muted);\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n}\n\n.settings-icon-button:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-icon-button:focus-visible,\n.settings-nav-button:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 1px;\n}\n\n.settings-page {\n display: block;\n position: relative;\n min-width: 0;\n min-height: 0;\n overflow-y: auto;\n background: var(--settings-bg);\n scrollbar-gutter: stable;\n}\n\n.settings-page__content {\n width: min(930px, calc(100% - 80px));\n margin-inline: auto;\n}\n\n.settings-page__content {\n min-width: 0;\n padding: 44px 0 56px;\n}\n\n.settings-section-label {\n min-height: auto;\n padding: 0 0 28px;\n color: var(--settings-text);\n font-size: 24px;\n font-weight: 600;\n line-height: 30px;\n}\n\n.settings-status-list,\n.settings-empty {\n overflow: hidden;\n background: var(--settings-panel);\n}\n\n.settings-status-row {\n display: grid;\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\n position: relative;\n align-items: center;\n min-height: 88px;\n gap: 28px;\n padding: 16px 0;\n}\n\n.settings-status-row:not(:last-child)::after {\n content: "";\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n height: 1px;\n background: var(--settings-divider);\n}\n\n.settings-status-row__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n color: var(--settings-text);\n gap: 14px;\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.settings-status-row__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n flex: none;\n color: var(--settings-muted);\n background: rgb(255 255 255 / 7%);\n border-radius: 50%;\n}\n\n.settings-status-badge {\n flex: none;\n padding: 5px 12px;\n color: var(--settings-muted);\n font-size: 13px;\n font-weight: 500;\n line-height: 18px;\n background: rgb(255 255 255 / 9%);\n border: 1px solid rgb(255 255 255 / 6%);\n border-radius: 6px;\n}\n\n.settings-status-row__detail {\n min-width: 0;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-page-description {\n max-width: 760px;\n margin: -12px 0 24px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-preference-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 32px;\n min-height: 88px;\n padding: 18px 0;\n border-top: 1px solid var(--settings-divider);\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-preference-row__copy {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n min-width: 0;\n gap: 5px;\n}\n\n.settings-preference-row__copy strong {\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 600;\n line-height: 22px;\n}\n\n.settings-preference-row__copy span {\n max-width: 680px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-preference-switch {\n display: inline-flex;\n align-items: center;\n width: 42px;\n height: 24px;\n flex: none;\n padding: 2px;\n border: 0;\n border-radius: 999px;\n background: rgb(255 255 255 / 16%);\n cursor: pointer;\n transition: background 120ms ease;\n}\n\n.settings-preference-switch[aria-checked="true"] {\n justify-content: flex-end;\n background: var(--settings-focus);\n}\n\n.settings-preference-switch:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 2px;\n}\n\n.settings-preference-switch__thumb {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n background: white;\n box-shadow: 0 1px 3px rgb(0 0 0 / 35%);\n}\n\n.settings-connection-page-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 24px;\n margin-bottom: 24px;\n}\n\n.settings-connection-page-header .settings-section-label {\n padding-bottom: 0;\n}\n\n.settings-connection-page-header .settings-page-description {\n margin: 8px 0 0;\n}\n\n.settings-connections-content,\n.settings-connections-layout,\n.settings-connection-list,\n.settings-connection-rows {\n min-width: 0;\n}\n\n.settings-connections-layout {\n display: grid;\n grid-template-columns: minmax(0, 1.55fr) minmax(280px, 0.85fr);\n align-items: start;\n gap: 18px;\n}\n\n.settings-connection-list,\n.settings-connection-inspector {\n overflow: hidden;\n background: rgb(255 255 255 / 2%);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n}\n\n.settings-connection-host-strip {\n display: grid;\n grid-template-columns: 30px minmax(0, 1fr) 30px;\n align-items: center;\n min-width: 0;\n gap: 6px;\n padding: 12px;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-host-scroll {\n display: none;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 34px;\n padding: 0;\n color: var(--settings-muted);\n background: rgb(0 0 0 / 18%);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n cursor: pointer;\n}\n\n.settings-connection-host-strip[data-connection-host-overflow="true"]\n .settings-connection-host-scroll {\n display: inline-flex;\n}\n\n.settings-connection-host-strip:not([data-connection-host-overflow="true"]) {\n grid-template-columns: minmax(0, 1fr);\n}\n\n.settings-connection-host-scroll:hover:not(:disabled) {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-connection-host-scroll:disabled {\n cursor: default;\n opacity: 0.35;\n}\n\n.settings-connection-host-scroll:focus-visible,\n.settings-connection-host-tab:focus-visible,\n.settings-connection-row:focus-visible,\n.settings-connection-install-link:focus-visible,\n.settings-connection-view-error:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 1px;\n}\n\n.settings-connection-host-tabs {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 6px;\n overflow-x: auto;\n overflow-y: hidden;\n overscroll-behavior-x: contain;\n scrollbar-width: none;\n scroll-behavior: smooth;\n scroll-snap-type: x proximity;\n touch-action: pan-x;\n}\n\n.settings-connection-host-tabs::-webkit-scrollbar {\n display: none;\n}\n\n.settings-connection-host-tab {\n display: inline-flex;\n align-items: center;\n height: 34px;\n min-width: max-content;\n flex: none;\n padding: 0 11px;\n color: var(--settings-muted);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n white-space: nowrap;\n background: rgb(0 0 0 / 18%);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n cursor: pointer;\n scroll-snap-align: start;\n}\n\n.settings-connection-host-tab:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-connection-host-tab[aria-selected="true"] {\n color: #e8f3ff;\n font-weight: 600;\n background: var(--settings-active);\n border-color: rgb(51 156 255 / 50%);\n}\n\n.settings-connection-table-header,\n.settings-connection-row {\n display: grid;\n grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) 82px;\n align-items: center;\n min-width: 0;\n gap: 14px;\n}\n\n.settings-connection-table-header {\n min-height: 36px;\n padding: 0 12px;\n color: var(--settings-muted);\n font-size: 10px;\n font-weight: 600;\n line-height: 16px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n background: rgb(255 255 255 / 2%);\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-row {\n min-height: 64px;\n padding: 10px 12px;\n cursor: pointer;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-row:last-child {\n border-bottom: 0;\n}\n\n.settings-connection-row:hover {\n background: var(--settings-hover);\n}\n\n.settings-connection-row[data-connection-selected="true"] {\n background: var(--settings-active);\n}\n\n.settings-connection-row__identity,\n.settings-connection-inspector__identity {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n gap: 9px;\n color: var(--settings-text);\n}\n\n.settings-connection-row__identity strong,\n.settings-connection-inspector__identity strong {\n min-width: 0;\n overflow-wrap: anywhere;\n font-size: 13px;\n font-weight: 600;\n line-height: 19px;\n}\n\n.settings-connection-row__mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex: none;\n color: #d4d8de;\n font-size: 10px;\n font-weight: 700;\n background: rgb(255 255 255 / 6%);\n border-radius: 7px;\n}\n\n.settings-connection-row__mark--logo {\n color: var(--settings-text);\n background: transparent;\n}\n\n.settings-connection-row__mark--logo > img,\n.settings-connection-row__mark--logo > svg {\n display: block;\n max-width: 22px;\n max-height: 22px;\n}\n\n.settings-connection-row__status {\n display: inline-flex;\n align-items: center;\n width: fit-content;\n gap: 6px;\n color: var(--settings-muted);\n font-size: 12px;\n font-weight: 500;\n line-height: 18px;\n}\n\n.settings-connection-row__status::before {\n width: 7px;\n height: 7px;\n flex: none;\n content: "";\n background: currentColor;\n border-radius: 50%;\n}\n\n.settings-connection-row__status[data-connection-tone="ready"] {\n color: #4ade80;\n}\n\n.settings-connection-row__status[data-connection-tone="checking"] {\n color: #f5c542;\n}\n\n.settings-connection-row__status[data-connection-tone="setup"] {\n color: var(--settings-muted);\n}\n\n.settings-connection-row__status[data-connection-tone="setup"]::before {\n background: transparent;\n border: 1.5px solid currentColor;\n}\n\n.settings-connection-row__status[data-connection-tone="failed"] {\n color: #f87171;\n}\n\n.settings-connection-row__action {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n min-width: 0;\n}\n\n.settings-connection-install-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n color: var(--settings-focus);\n text-decoration: none;\n background: transparent;\n border: 1px solid transparent;\n border-radius: 7px;\n}\n\n.settings-connection-install-link:hover {\n color: #8cc5ff;\n background: rgb(51 156 255 / 10%);\n border-color: rgb(51 156 255 / 22%);\n}\n\n.settings-connection-view-error {\n padding: 4px 7px;\n color: #f87171;\n font: inherit;\n font-size: 11px;\n line-height: 18px;\n background: transparent;\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.settings-connection-view-error:hover {\n color: #fca5a5;\n background: rgb(239 68 68 / 9%);\n}\n\n.settings-connection-inspector {\n position: sticky;\n top: 24px;\n}\n\n.settings-connection-inspector__header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n min-width: 0;\n gap: 12px;\n padding: 16px;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-connection-inspector__body {\n display: grid;\n min-width: 0;\n gap: 14px;\n padding: 16px;\n}\n\n.settings-connection-install-callout {\n display: grid;\n grid-template-columns: 32px minmax(0, 1fr);\n gap: 10px;\n padding: 14px;\n background: linear-gradient(rgb(51 156 255 / 8%), rgb(51 156 255 / 4%));\n border: 1px solid rgb(51 156 255 / 20%);\n border-radius: 10px;\n}\n\n.settings-connection-install-callout__icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n color: var(--settings-focus);\n background: var(--settings-active);\n border-radius: 8px;\n}\n\n.settings-connection-install-callout strong,\n.settings-connection-error-summary strong,\n.settings-connection-state-summary strong {\n display: block;\n margin: 0 0 4px;\n color: var(--settings-text);\n font-size: 13px;\n font-weight: 600;\n line-height: 19px;\n}\n\n.settings-connection-install-callout p,\n.settings-connection-error-summary p,\n.settings-connection-state-summary p,\n.settings-connection-issue-note {\n margin: 0;\n color: var(--settings-muted);\n font-size: 11px;\n line-height: 17px;\n}\n\n.settings-connection-install-button {\n width: 100%;\n}\n\n.settings-connection-error-summary {\n padding: 13px 14px;\n color: #fecaca;\n background: rgb(239 68 68 / 7%);\n border: 1px solid rgb(239 68 68 / 20%);\n border-radius: 10px;\n}\n\n.settings-connection-error-metadata {\n display: grid;\n gap: 7px;\n padding: 12px;\n background: rgb(0 0 0 / 18%);\n border: 1px solid var(--settings-border);\n border-radius: 9px;\n}\n\n.settings-connection-detail-line {\n display: grid;\n grid-template-columns: 92px minmax(0, 1fr);\n gap: 10px;\n min-width: 0;\n color: var(--settings-muted);\n font-size: 10px;\n line-height: 16px;\n}\n\n.settings-connection-detail-line code {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n white-space: pre-wrap;\n}\n\n.settings-connection-error-log-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n}\n\n.settings-connection-error-log-header strong {\n color: var(--settings-text);\n font-size: 11px;\n font-weight: 600;\n}\n\n.settings-connection-error-log-header .settings-command-button {\n min-height: 30px;\n padding: 5px 9px;\n font-size: 11px;\n}\n\n.settings-connection-stderr {\n max-height: 220px;\n min-width: 0;\n margin: 0;\n padding: 11px;\n overflow: auto;\n color: #e3e5e8;\n font:\n 10px/16px ui-monospace,\n "SFMono-Regular",\n Consolas,\n "Liberation Mono",\n monospace;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n background: rgb(0 0 0 / 28%);\n border: 1px solid var(--settings-border);\n border-radius: 9px;\n}\n\n.settings-connection-error-actions {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: 8px;\n}\n\n.settings-connection-error-actions .settings-command-button {\n width: 100%;\n}\n\n.settings-connection-issue-note {\n margin-top: -4px;\n}\n\n.settings-connection-state-summary {\n padding: 14px;\n background: rgb(255 255 255 / 3%);\n border: 1px solid var(--settings-border);\n border-radius: 10px;\n}\n\n.settings-empty {\n display: flex;\n align-items: center;\n min-height: 72px;\n padding: 12px 16px;\n}\n\n.settings-empty > div {\n display: grid;\n min-width: 0;\n gap: 2px;\n}\n\n.settings-empty strong {\n color: var(--settings-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 19px;\n}\n\n.settings-empty span {\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 19px;\n}\n\n.settings-about-panel {\n display: grid;\n max-width: 720px;\n gap: 18px;\n padding: 28px;\n background: rgb(255 255 255 / 2%);\n border: 1px solid var(--settings-border);\n border-radius: 12px;\n}\n\n.settings-about-product {\n color: var(--settings-text);\n font-size: 24px;\n font-weight: 700;\n line-height: 32px;\n}\n\n.settings-about-tagline {\n color: var(--settings-text);\n font-size: 16px;\n font-weight: 600;\n line-height: 24px;\n}\n\n.settings-about-copy {\n display: grid;\n max-width: 660px;\n gap: 12px;\n}\n\n.settings-about-copy p,\n.settings-about-repository p {\n margin: 0;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 21px;\n}\n\n.settings-about-star-callout {\n margin: 2px 0;\n color: #f5c542;\n font-size: 14px;\n font-weight: 600;\n line-height: 22px;\n}\n\n.settings-about-repository {\n display: grid;\n gap: 10px;\n padding-top: 18px;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-about-repository-link {\n display: flex;\n align-items: center;\n width: fit-content;\n min-width: 0;\n max-width: 100%;\n gap: 8px;\n color: #7cb8ff;\n font-size: 13px;\n font-weight: 600;\n text-decoration: none;\n}\n\n.settings-about-repository-link:hover {\n text-decoration: underline;\n}\n\n.settings-about-repository-link code {\n min-width: 0;\n overflow: hidden;\n color: var(--settings-muted);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 11px;\n font-weight: 400;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-page-error {\n padding: 16px;\n color: var(--settings-text);\n font-size: 13px;\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 20px;\n}\n\n.settings-update-metadata {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 24px;\n margin-bottom: 24px;\n padding: 0 4px 20px;\n border-bottom: 1px solid var(--settings-divider);\n}\n\n.settings-update-metadata__item {\n display: grid;\n min-width: 0;\n gap: 4px;\n}\n\n.settings-update-metadata__item span {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-metadata__item strong {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 500;\n line-height: 22px;\n}\n\n/* The only emphasis on this screen: a latest version newer than the installed one. */\n.settings-update-metadata__value--newer {\n color: #4aa3f0;\n}\n\n.settings-update-panel {\n display: grid;\n gap: 12px;\n padding: 20px;\n color: var(--settings-text);\n background: var(--settings-panel);\n border: 1px solid var(--settings-border);\n border-radius: 8px;\n}\n\n.settings-update-panel__head {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-update-panel__head .codexhost-settings-icon {\n color: var(--settings-muted);\n}\n\n.settings-update-panel[data-update-state="available"] .codexhost-settings-icon,\n.settings-update-panel[data-update-state="downloading"] .codexhost-settings-icon,\n.settings-update-panel[data-update-state="installing"] .codexhost-settings-icon,\n.settings-update-panel[data-update-state="restarting"] .codexhost-settings-icon {\n color: #4aa3f0;\n}\n\n.settings-update-panel[data-update-state="current"] .codexhost-settings-icon {\n color: #4ade80;\n}\n\n.settings-update-panel[data-update-state="failed"] .codexhost-settings-icon,\n.settings-update-panel[data-update-state="error"] .codexhost-settings-icon {\n color: #ef4444;\n}\n\n.settings-update-panel__title {\n min-width: 0;\n overflow-wrap: anywhere;\n color: var(--settings-text);\n font-size: 14px;\n font-weight: 600;\n line-height: 22px;\n}\n\n.settings-update-summary,\n.settings-update-error {\n margin: 0;\n overflow-wrap: anywhere;\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-summary {\n color: var(--settings-muted);\n}\n\n.settings-update-error {\n color: #ef4444;\n}\n\n.settings-update-progress {\n width: 100%;\n height: 8px;\n accent-color: #238be8;\n}\n\n.settings-update-progress-detail {\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n/* Release notes scroll with the page; a scroller inside a scroller traps the wheel. */\n.settings-update-notes-section {\n min-width: 0;\n margin-top: 24px;\n padding-top: 24px;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-update-notes-section:empty {\n display: none;\n}\n\n.settings-update-notes {\n display: grid;\n min-width: 0;\n gap: 12px;\n color: var(--settings-muted);\n font-size: 14px;\n line-height: 22px;\n overflow-wrap: anywhere;\n}\n\n.settings-update-notes > :first-child {\n margin-top: 0;\n}\n\n.settings-update-notes > :last-child {\n margin-bottom: 0;\n}\n\n.settings-update-notes h1,\n.settings-update-notes h2,\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n margin: 6px 0 0;\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes h1 {\n font-size: 18px;\n line-height: 26px;\n}\n\n.settings-update-notes h2 {\n font-size: 17px;\n line-height: 24px;\n}\n\n.settings-update-notes h3,\n.settings-update-notes h4,\n.settings-update-notes h5,\n.settings-update-notes h6 {\n font-size: 14px;\n line-height: 22px;\n}\n\n.settings-update-notes p,\n.settings-update-notes ul,\n.settings-update-notes ol,\n.settings-update-notes pre,\n.settings-update-notes blockquote {\n margin: 0;\n}\n\n.settings-update-notes ul,\n.settings-update-notes ol {\n padding-left: 24px;\n}\n\n.settings-update-notes li {\n padding-left: 4px;\n}\n\n.settings-update-notes li::marker {\n color: var(--settings-muted);\n font-size: 0.85em;\n}\n\n.settings-update-notes li + li {\n margin-top: 14px;\n}\n\n.settings-update-notes .release-note-translation {\n display: block;\n margin-top: 3px;\n color: var(--settings-muted);\n font-size: 13px;\n line-height: 20px;\n}\n\n.settings-update-notes blockquote {\n padding: 8px 12px;\n color: var(--settings-muted);\n border-left: 3px solid var(--settings-focus);\n background: rgb(255 255 255 / 4%);\n}\n\n.settings-update-notes hr {\n width: 100%;\n margin: 2px 0;\n border: 0;\n border-top: 1px solid var(--settings-divider);\n}\n\n.settings-update-notes strong {\n color: var(--settings-text);\n font-weight: 600;\n}\n\n.settings-update-notes a {\n color: #7cb8ff;\n text-decoration: none;\n}\n\n.settings-update-notes a:hover {\n text-decoration: underline;\n}\n\n.settings-update-notes code {\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n}\n\n.settings-update-notes :not(pre) > code {\n padding: 1px 5px;\n background: rgb(255 255 255 / 8%);\n border-radius: 4px;\n}\n\n.settings-update-notes pre {\n min-width: 0;\n max-width: 100%;\n padding: 10px 12px;\n overflow-x: auto;\n overflow-y: hidden;\n background: rgb(0 0 0 / 28%);\n border-radius: 6px;\n}\n\n.settings-update-notes pre code {\n line-height: 18px;\n white-space: pre-wrap;\n}\n\n/* Manual update sits directly under the status panel, not behind a disclosure:\n automatic updates can fail for machine-local reasons and this is the way out. */\n.settings-update-controls {\n display: grid;\n min-width: 0;\n gap: 10px;\n margin-top: 24px;\n}\n\n.settings-update-manual-title {\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 600;\n line-height: 22px;\n}\n\n.settings-update-manual {\n display: grid;\n min-width: 0;\n gap: 8px;\n}\n\n.settings-update-manual[hidden] {\n display: none;\n}\n\n.settings-update-manual-description {\n margin: 0;\n color: var(--settings-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.settings-update-manual-description.is-fallback {\n color: #ef4444;\n}\n\n.settings-update-command {\n display: flex;\n align-items: stretch;\n min-width: 0;\n overflow: hidden;\n border: 1px solid var(--settings-border);\n border-radius: 6px;\n}\n\n.settings-update-command code {\n min-width: 0;\n flex: 1;\n padding: 9px 12px;\n overflow-x: auto;\n color: var(--settings-text);\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\n font-size: 12px;\n line-height: 20px;\n white-space: nowrap;\n}\n\n.settings-update-command__copy {\n display: inline-flex;\n align-items: center;\n flex: none;\n gap: 6px;\n padding: 0 12px;\n color: var(--settings-muted);\n font-size: 12px;\n background: transparent;\n border: 0;\n border-left: 1px solid var(--settings-border);\n cursor: pointer;\n}\n\n.settings-update-command__copy:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n\n.settings-update-command__copy:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: -2px;\n}\n\n.settings-update-actions {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.settings-command-button,\n.settings-update-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: fit-content;\n min-height: 36px;\n gap: 8px;\n padding: 8px 12px;\n color: white;\n font: inherit;\n font-size: 13px;\n text-decoration: none;\n background: #1677d2;\n border: 1px solid #238be8;\n border-radius: 6px;\n cursor: pointer;\n}\n\n.settings-command-button--secondary,\n.settings-update-link {\n color: var(--settings-text);\n background: transparent;\n border-color: var(--settings-border);\n}\n\n.settings-command-button:hover,\n.settings-update-link:hover {\n filter: brightness(1.08);\n}\n\n.settings-command-button:focus-visible,\n.settings-update-link:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: 2px;\n}\n\n.codexhost-settings-icon {\n display: block;\n flex: none;\n stroke: currentColor;\n}\n\n@media (max-width: 720px) {\n .codexhost-settings-dialog {\n width: calc(100vw - 16px);\n height: calc(100vh - 16px);\n border-radius: 10px;\n }\n\n .settings-layout {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr);\n }\n\n .settings-sidebar {\n border-right: 0;\n border-bottom: 1px solid var(--settings-border);\n }\n\n .settings-header {\n height: 56px;\n padding-inline: 14px;\n }\n\n .settings-sidebar {\n padding-top: 20px;\n }\n\n .settings-nav {\n flex: none;\n flex-direction: row;\n gap: 4px;\n padding: 7px 8px 8px;\n overflow-x: auto;\n overflow-y: hidden;\n }\n\n .settings-nav-section-label {\n display: none;\n }\n\n .settings-nav-button {\n width: auto;\n min-width: max-content;\n min-height: 34px;\n grid-template-columns: 16px auto;\n border-radius: 10px;\n }\n\n .settings-nav-star-link {\n margin-top: 0;\n }\n\n .settings-page__content {\n width: calc(100% - 40px);\n }\n\n .settings-page__content {\n padding-top: 32px;\n padding-bottom: 32px;\n }\n\n .settings-section-label {\n padding-bottom: 20px;\n font-size: 20px;\n line-height: 26px;\n }\n\n .settings-update-metadata {\n grid-template-columns: minmax(0, 1fr);\n gap: 16px;\n }\n\n .settings-status-row {\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 16px;\n }\n\n .settings-connection-page-header {\n flex-direction: column;\n }\n\n .settings-connection-page-header > .settings-command-button {\n width: 100%;\n }\n\n .settings-connections-layout {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .settings-connection-inspector {\n position: static;\n }\n\n .settings-status-row__detail {\n grid-column: 1 / -1;\n margin: -8px 0 0 58px;\n }\n}\n\n@media (forced-colors: active) {\n :host,\n :host([data-theme="dark"]) {\n --settings-bg: Canvas;\n --settings-sidebar: Canvas;\n --settings-panel: Canvas;\n --settings-text: CanvasText;\n --settings-muted: GrayText;\n --settings-border: ButtonBorder;\n --settings-divider: ButtonBorder;\n --settings-hover: Highlight;\n --settings-active: Highlight;\n --settings-focus: Highlight;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n }\n}\n';
20972
21733
 
20973
21734
  // src/settings/shell.ts
20974
21735
  var SETTINGS_SHELL_ATTRIBUTE = "data-codexhost-settings-shell";
20975
- var CODEXHOST_GITHUB_REPOSITORY_URL = "https://github.com/BytePioneer-AI/codex-host";
20976
21736
  function isRendererSettingsDialogSupported(dialog) {
20977
21737
  return typeof dialog.showModal === "function" && typeof dialog.close === "function";
20978
21738
  }
@@ -21019,23 +21779,13 @@ ${error51.stderrTail}`] : []
21019
21779
  brand.append(brandMark, brandCopy);
21020
21780
  const headerActions = ownerDocument.createElement("div");
21021
21781
  headerActions.className = "settings-header-actions";
21022
- const starLink = ownerDocument.createElement("a");
21023
- starLink.className = "settings-icon-button settings-star-link";
21024
- starLink.href = CODEXHOST_GITHUB_REPOSITORY_URL;
21025
- starLink.target = "_blank";
21026
- starLink.rel = "noopener noreferrer";
21027
- starLink.setAttribute("aria-label", messages.starOnGitHub);
21028
- starLink.title = messages.starOnGitHub;
21029
- const starLabel = ownerDocument.createElement("span");
21030
- starLabel.textContent = messages.starOnGitHub;
21031
- starLink.append(createRendererSettingsIcon("star", 16), starLabel);
21032
21782
  const closeButton = ownerDocument.createElement("button");
21033
21783
  closeButton.type = "button";
21034
21784
  closeButton.className = "settings-icon-button";
21035
21785
  closeButton.setAttribute("aria-label", messages.close);
21036
21786
  closeButton.title = messages.close;
21037
21787
  closeButton.append(createRendererSettingsIcon("close", 18));
21038
- headerActions.append(starLink, closeButton);
21788
+ headerActions.append(closeButton);
21039
21789
  header.append(brand, headerActions);
21040
21790
  const layout = ownerDocument.createElement("div");
21041
21791
  layout.className = "settings-layout";
@@ -21101,7 +21851,19 @@ ${error51.stderrTail}`] : []
21101
21851
  renderMountFailure();
21102
21852
  }
21103
21853
  };
21854
+ const appendNavigationSection = (label) => {
21855
+ const section = ownerDocument.createElement("div");
21856
+ section.className = "settings-nav-section-label";
21857
+ section.textContent = label;
21858
+ navigation.append(section);
21859
+ };
21860
+ let otherSectionAdded = false;
21861
+ appendNavigationSection(messages.generalSection);
21104
21862
  for (const definition of resolvedRegistry.pages) {
21863
+ if (definition.id === "about" && !otherSectionAdded) {
21864
+ appendNavigationSection(messages.otherSection);
21865
+ otherSectionAdded = true;
21866
+ }
21105
21867
  const button = ownerDocument.createElement("button");
21106
21868
  button.type = "button";
21107
21869
  button.className = "settings-nav-button";
@@ -21113,6 +21875,18 @@ ${error51.stderrTail}`] : []
21113
21875
  navigationButtons.set(definition.id, button);
21114
21876
  navigation.append(button);
21115
21877
  }
21878
+ const starLink = ownerDocument.createElement("a");
21879
+ starLink.className = "settings-nav-button settings-nav-star-link";
21880
+ starLink.href = CODEXHOST_GITHUB_REPOSITORY_URL;
21881
+ starLink.target = "_blank";
21882
+ starLink.rel = "noopener noreferrer";
21883
+ starLink.setAttribute("aria-label", messages.starOnGitHub);
21884
+ starLink.title = messages.starOnGitHub;
21885
+ starLink.append(createRendererSettingsIcon("star", 17));
21886
+ const starLabel = ownerDocument.createElement("span");
21887
+ starLabel.textContent = messages.starOnGitHub;
21888
+ starLink.append(starLabel);
21889
+ navigation.append(starLink);
21116
21890
  const supported = isRendererSettingsDialogSupported(dialog);
21117
21891
  const focusActiveNavigation = () => {
21118
21892
  navigationButtons.get(navigationState.activePageId)?.focus();
@@ -21454,6 +22228,7 @@ ${error51.stderrTail}`] : []
21454
22228
  trigger = null;
21455
22229
  shell = null;
21456
22230
  const mounted = mount();
22231
+ options.onLocaleChange?.(locale);
21457
22232
  if (reopen) {
21458
22233
  const opener = mounted.trigger.root?.querySelector("button") ?? void 0;
21459
22234
  mounted.shell.openSettings(opener, activePageId);
@@ -21595,6 +22370,9 @@ ${error51.stderrTail}`] : []
21595
22370
  );
21596
22371
  }
21597
22372
  var rendererUsageRefreshDelays = [250, 500, 1e3, 2e3, 4e3, 8e3];
22373
+ function refreshConnectionHosts(hostIds, refreshHost) {
22374
+ return Promise.all([...hostIds].map((hostId) => refreshHost(hostId))).then(() => void 0);
22375
+ }
21598
22376
  function rendererUsageRefreshDelay(attempt) {
21599
22377
  const index = Math.max(0, Math.min(Math.trunc(attempt), rendererUsageRefreshDelays.length - 1));
21600
22378
  return rendererUsageRefreshDelays[index] ?? rendererUsageRefreshDelays[0];
@@ -21619,6 +22397,13 @@ ${error51.stderrTail}`] : []
21619
22397
  function draftPermissionMode(catalog, requested) {
21620
22398
  return catalog.modes.find(({ id }) => id === requested)?.id ?? catalog.modes.find(({ id }) => id === catalog.defaultModeId)?.id ?? catalog.defaultModeId;
21621
22399
  }
22400
+ function lockedPermissionMode(catalog, effective, carrier) {
22401
+ const restored = effective ?? carrier;
22402
+ if (restored && !catalog.modes.some(({ id }) => id === restored)) {
22403
+ throw new Error("Existing Thread Permission Mode is absent from the current Catalog");
22404
+ }
22405
+ return restored;
22406
+ }
21622
22407
  function shouldPersistNewThreadConfigurationSelection(phase) {
21623
22408
  return phase === "draft";
21624
22409
  }
@@ -21685,7 +22470,12 @@ ${error51.stderrTail}`] : []
21685
22470
  throw new Error("DeepSeek Harness Thread reported an incompatible transport Model");
21686
22471
  }
21687
22472
  const model = inspection.effectiveModel ?? transportSelection.model;
21688
- return { agent: "deepseek-harness", ...model ? { model } : {} };
22473
+ const permissionModeId = inspection.effectivePermissionModeId ?? transportSelection.permissionModeId;
22474
+ return {
22475
+ agent: "deepseek-harness",
22476
+ ...model ? { model } : {},
22477
+ ...permissionModeId ? { permissionModeId } : {}
22478
+ };
21689
22479
  }
21690
22480
  throw new Error("Thread owner is not a Renderer Agent");
21691
22481
  }
@@ -21792,7 +22582,10 @@ ${error51.stderrTail}`] : []
21792
22582
  let connectionDiagnostics = null;
21793
22583
  const settingsLifecycle = installRendererSettingsLifecycle(window, {
21794
22584
  getUpdateClient: () => modelControl,
21795
- getConnectionDiagnostics: () => connectionDiagnostics
22585
+ getConnectionDiagnostics: () => connectionDiagnostics,
22586
+ onLocaleChange() {
22587
+ for (const mounted of mountedByComposer.values()) renderMounted(mounted);
22588
+ }
21796
22589
  });
21797
22590
  let adapterStatus = {
21798
22591
  state: "installing",
@@ -21871,7 +22664,8 @@ ${error51.stderrTail}`] : []
21871
22664
  mounted.modelView,
21872
22665
  mounted.permissionModeView,
21873
22666
  mounted.usage,
21874
- mounted.accountCredits
22667
+ mounted.accountCredits,
22668
+ settingsLifecycle.locale
21875
22669
  );
21876
22670
  if (mounted.control.usage) {
21877
22671
  mounted.control.usage.onOpen = () => {
@@ -22159,11 +22953,15 @@ ${error51.stderrTail}`] : []
22159
22953
  throw new Error("External Harness omitted its Permission Mode catalog");
22160
22954
  }
22161
22955
  mounted.permissionModeView = { status: "loading", catalog: permissionModes };
22162
- const effectivePermissionModeId = current.phase === "locked" ? mounted.threadConfiguration?.effectivePermissionModeId : void 0;
22956
+ const restoredPermissionModeId = current.phase === "locked" ? lockedPermissionMode(
22957
+ permissionModes,
22958
+ mounted.threadConfiguration?.effectivePermissionModeId,
22959
+ previousPermissionModeId
22960
+ ) : void 0;
22163
22961
  const preferredPermissionModeId = preferredConfiguration?.permissionModeId ?? (agent === "claude-code" ? readClaudePermissionModePreference(permissionModes) : void 0);
22164
22962
  selectedPermissionModeId = draftPermissionMode(
22165
22963
  permissionModes,
22166
- effectivePermissionModeId ?? previousPermissionModeId ?? preferredPermissionModeId
22964
+ restoredPermissionModeId ?? previousPermissionModeId ?? preferredPermissionModeId
22167
22965
  );
22168
22966
  mounted.permissionModeView = {
22169
22967
  status: "loading",
@@ -22871,10 +23669,10 @@ ${error51.stderrTail}`] : []
22871
23669
  };
22872
23670
  },
22873
23671
  refresh() {
22874
- for (const hostId of harnessAvailabilityByHost.keys()) {
22875
- void refreshHarnessAvailabilityForHost(hostId, true, false, true);
22876
- }
22877
- return Promise.resolve();
23672
+ return refreshConnectionHosts(
23673
+ harnessAvailabilityByHost.keys(),
23674
+ (hostId) => refreshHarnessAvailabilityForHost(hostId, true, false, true)
23675
+ );
22878
23676
  },
22879
23677
  subscribe(listener) {
22880
23678
  connectionListeners.add(listener);
@@ -23382,10 +24180,14 @@ lucide/dist/esm/icons/chevron-down.mjs:
23382
24180
  lucide/dist/esm/icons/shield.mjs:
23383
24181
  lucide/dist/esm/icons/shield-alert.mjs:
23384
24182
  lucide/dist/esm/icons/boxes.mjs:
24183
+ lucide/dist/esm/icons/circle-check.mjs:
24184
+ lucide/dist/esm/icons/chevron-left.mjs:
24185
+ lucide/dist/esm/icons/chevron-right.mjs:
23385
24186
  lucide/dist/esm/icons/circle-off.mjs:
23386
24187
  lucide/dist/esm/icons/copy.mjs:
23387
24188
  lucide/dist/esm/icons/download.mjs:
23388
24189
  lucide/dist/esm/icons/external-link.mjs:
24190
+ lucide/dist/esm/icons/info.mjs:
23389
24191
  lucide/dist/esm/icons/languages.mjs:
23390
24192
  lucide/dist/esm/icons/network.mjs:
23391
24193
  lucide/dist/esm/icons/plug-zap.mjs:
@@ -23394,6 +24196,7 @@ lucide/dist/esm/icons/route.mjs:
23394
24196
  lucide/dist/esm/icons/settings.mjs:
23395
24197
  lucide/dist/esm/icons/stethoscope.mjs:
23396
24198
  lucide/dist/esm/icons/star.mjs:
24199
+ lucide/dist/esm/icons/triangle-alert.mjs:
23397
24200
  lucide/dist/esm/icons/x.mjs:
23398
24201
  (**
23399
24202
  * @license lucide v1.28.0 - ISC