@karlorz/codexhost-cli-win32-arm64 0.13.3-7 → 0.14.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/app/codexhost-distribution.json +1 -1
  3. package/app/console-server.mjs +55 -4
  4. package/app/console-web.js +344 -54
  5. package/app/cpa-live-voice-hook.js +55 -4
  6. package/app/desktop-controller.mjs +69 -8
  7. package/app/host-runtime.mjs +743 -230
  8. package/app/plugins/antigravity/plugin.mjs +88 -12
  9. package/app/plugins/claude-code/plugin.mjs +397 -21
  10. package/app/plugins/codebuddy/plugin.mjs +107 -17
  11. package/app/plugins/codex-usage/plugin.mjs +67 -7
  12. package/app/plugins/copilot/assets/icon.png +0 -0
  13. package/app/plugins/copilot/manifest.json +26 -0
  14. package/app/plugins/copilot/plugin.mjs +34645 -0
  15. package/app/plugins/cursor-cli/plugin.mjs +88 -6
  16. package/app/plugins/deepseek-harness/plugin.mjs +73 -6
  17. package/app/plugins/enabled.json +1 -0
  18. package/app/plugins/grok/plugin.mjs +272 -60
  19. package/app/plugins/hermes/plugin.mjs +70 -4
  20. package/app/plugins/kimi-code/plugin.mjs +73 -6
  21. package/app/plugins/kiro-cli/plugin.mjs +73 -6
  22. package/app/plugins/omp/plugin.mjs +73 -112
  23. package/app/plugins/opencode/plugin.mjs +75 -6
  24. package/app/plugins/pi/plugin.mjs +204 -44
  25. package/app/plugins/qoder/plugin.mjs +73 -6
  26. package/app/plugins/qoder-cn/plugin.mjs +73 -6
  27. package/app/plugins/workbuddy/plugin.mjs +368 -41
  28. package/app/plugins/zcode/plugin.mjs +74 -6
  29. package/app/renderer-extension.js +749 -157
  30. package/bin/codexhost.exe +0 -0
  31. package/libexec/codexhost-node-repl.exe +0 -0
  32. package/libexec/codexhost-shim.exe +0 -0
  33. package/libexec/codexhost-updater.exe +0 -0
  34. package/package.json +1 -1
@@ -14768,10 +14768,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14768
14768
  var project = external_exports.string().min(1).max(4096);
14769
14769
  var usageStatisticsParamsSchema = external_exports.strictObject({
14770
14770
  range: external_exports.enum(USAGE_STATISTICS_RANGES),
14771
+ /** Display timezone for ranges, dates and hourly buckets; omitted means the Host's local zone. */
14772
+ timeZone: external_exports.string().min(1).max(128).refine((value) => {
14773
+ try {
14774
+ new Intl.DateTimeFormat("en", { timeZone: value });
14775
+ return true;
14776
+ } catch {
14777
+ return false;
14778
+ }
14779
+ }, "Unsupported usage statistics timezone").optional(),
14771
14780
  harness: harness.optional(),
14772
14781
  model: model.nullable().optional(),
14773
14782
  project: project.nullable().optional(),
14774
- /** One local date inside the range. */
14783
+ /** One date in the query timezone inside the range. */
14775
14784
  date: localDate.optional()
14776
14785
  });
14777
14786
  var usageStatisticsTotalsSchema = external_exports.strictObject({
@@ -14807,7 +14816,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14807
14816
  });
14808
14817
  var usageStatisticsResultSchema = external_exports.strictObject({
14809
14818
  range: external_exports.enum(USAGE_STATISTICS_RANGES),
14810
- /** First local date of the range; null for all time. */
14819
+ /** First date in the query timezone (Host local when omitted); null for all time. */
14811
14820
  from: localDate.nullable(),
14812
14821
  to: localDate,
14813
14822
  /** The filters these figures apply, echoed so a late answer to older filters is recognised. */
@@ -14847,7 +14856,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14847
14856
  totals: usageStatisticsTotalsSchema,
14848
14857
  /** Each date and Harness of the range: every filter except the day. */
14849
14858
  daily: external_exports.array(external_exports.strictObject({ date: localDate, harness, ...totals })).max(2e5),
14850
- /** Local weekday (0 = Sunday) and hour, all filters applied. */
14859
+ /** Weekday (0 = Sunday) and hour in the query timezone, all filters applied. */
14851
14860
  hourly: external_exports.array(external_exports.strictObject({
14852
14861
  weekday: external_exports.number().int().min(0).max(6),
14853
14862
  hour: external_exports.number().int().min(0).max(23),
@@ -14939,6 +14948,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14939
14948
  "codexhost/harness/display-settings/set",
14940
14949
  "codexhost/harness/inspect",
14941
14950
  "codexhost/harness/installation",
14951
+ "codexhost/harness/installation/start",
14952
+ "codexhost/harness/installation/status",
14942
14953
  "codexhost/harness/launch-settings/get",
14943
14954
  "codexhost/harness/launch-settings/set",
14944
14955
  "codexhost/harness/grok-computer-use/get",
@@ -15018,7 +15029,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15018
15029
  var nonNegativeSafeIntegerSchema = external_exports.number().int().safe().nonnegative();
15019
15030
  var finiteNonNegativeNumberSchema = external_exports.number().finite().nonnegative();
15020
15031
  var cacheHitRatePercentSchema = external_exports.number().finite().min(0).max(100);
15032
+ var indicatorTextSchema = (maximum) => harnessLocalizedTextSchema.refine((entries) => Object.values(entries).every((text) => text.length <= maximum));
15033
+ var composerIndicatorsSchema = external_exports.array(external_exports.object({
15034
+ kind: external_exports.literal("countdown"),
15035
+ id: external_exports.string().min(1).max(64),
15036
+ expiresAtUnix: nonNegativeSafeIntegerSchema,
15037
+ label: indicatorTextSchema(256).optional(),
15038
+ tooltip: indicatorTextSchema(1024).optional()
15039
+ }).strict()).max(2).refine((entries) => new Set(entries.map(({ id }) => id)).size === entries.length, {
15040
+ message: "Composer indicator IDs must be unique"
15041
+ });
15021
15042
  var threadUsageSnapshotSchema = external_exports.object({
15043
+ composerIndicators: composerIndicatorsSchema.optional(),
15022
15044
  inputTokens: nonNegativeSafeIntegerSchema.optional(),
15023
15045
  cachedInputTokens: nonNegativeSafeIntegerSchema.optional(),
15024
15046
  cacheWriteInputTokens: nonNegativeSafeIntegerSchema.optional(),
@@ -15426,7 +15448,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15426
15448
  }).strict().optional(),
15427
15449
  autonomousTurns: external_exports.object({
15428
15450
  observe: external_exports.boolean()
15429
- }).strict().optional()
15451
+ }).strict().optional(),
15452
+ /** True only when `turn.steer` delivers input into the active Turn through a native primitive. */
15453
+ steer: external_exports.boolean().optional()
15430
15454
  }).strict();
15431
15455
  var harnessConfigurationStateSchema = external_exports.object({
15432
15456
  effectiveModel: harnessModelRefSchema.optional(),
@@ -15559,6 +15583,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15559
15583
  seen.add(thread.threadId);
15560
15584
  }
15561
15585
  });
15586
+ var threadSteeringInspectParamsSchema = external_exports.object({
15587
+ threadId: hostThreadIdSchema,
15588
+ /** The text about to be steered; a Harness command always stops and restarts. */
15589
+ text: external_exports.string().optional()
15590
+ }).strict();
15591
+ var threadSteerDeliverySchema = external_exports.enum(["activeTurn", "newTurn"]);
15592
+ var threadSteeringInspectResultSchema = external_exports.object({
15593
+ delivery: external_exports.enum(["official", "activeTurn", "newTurn"])
15594
+ }).strict();
15562
15595
 
15563
15596
  // ../shared-contracts/dist/harness-plugins.js
15564
15597
  var HARNESS_PLUGIN_MANIFEST_MAX_BYTES = 32 * 1024;
@@ -15675,7 +15708,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15675
15708
  }).strict();
15676
15709
 
15677
15710
  // ../shared-contracts/dist/harness-installation.js
15678
- var HARNESS_INSTALLATION_METHOD = "codexhost/harness/installation";
15711
+ var HARNESS_INSTALLATION_START_METHOD = "codexhost/harness/installation/start";
15712
+ var HARNESS_INSTALLATION_STATUS_METHOD = "codexhost/harness/installation/status";
15679
15713
  var harnessInstallationParamsSchema = external_exports.object({
15680
15714
  harnessId: harnessIdSchema,
15681
15715
  action: external_exports.enum(["check", "update", "install"])
@@ -15685,6 +15719,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15685
15719
  latestVersion: external_exports.string().min(1).max(128),
15686
15720
  updateAvailable: external_exports.boolean(),
15687
15721
  canUpdate: external_exports.boolean(),
15722
+ /** Defaults to CLI; some Harnesses ship only inside a desktop application. */
15723
+ versionKind: external_exports.enum(["cli", "application"]).optional(),
15724
+ /** Official download opened by the user; does not imply an automatic installation. */
15725
+ manualUpdateUrl: external_exports.url().max(2048).refine((value) => /^https:\/\/[^/?#@]+(?:[/?#]|$)/iu.test(value), "Manual updates require a credential-free HTTPS URL").optional(),
15688
15726
  // Adapter-owned diagnostic text; UI uses messageCode rather than displaying it verbatim.
15689
15727
  message: external_exports.string().max(2048).optional(),
15690
15728
  messageCode: external_exports.string().min(1).max(128).optional(),
@@ -15692,6 +15730,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15692
15730
  notice: harnessLocalizedTextSchema.optional(),
15693
15731
  latestVersionKind: external_exports.enum(["unknown", "tracking-branch"]).optional()
15694
15732
  }).strict();
15733
+ var harnessInstallationTaskParamsSchema = external_exports.object({ taskId: external_exports.string().uuid() }).strict();
15734
+ var harnessInstallationTaskSchema = external_exports.discriminatedUnion("status", [
15735
+ external_exports.object({ taskId: external_exports.string().uuid(), status: external_exports.literal("running") }).strict(),
15736
+ external_exports.object({
15737
+ taskId: external_exports.string().uuid(),
15738
+ status: external_exports.literal("succeeded"),
15739
+ result: harnessInstallationStateSchema
15740
+ }).strict(),
15741
+ external_exports.object({
15742
+ taskId: external_exports.string().uuid(),
15743
+ status: external_exports.literal("failed"),
15744
+ error: external_exports.object({ code: external_exports.number().int(), message: external_exports.string().min(1).max(2048) }).strict()
15745
+ }).strict()
15746
+ ]);
15695
15747
 
15696
15748
  // ../shared-contracts/dist/codex-accounts.js
15697
15749
  var accountIdSchema = external_exports.string().min(1).max(256).regex(/^[A-Za-z0-9._~-]+$/u);
@@ -18433,6 +18485,8 @@ th[aria-sort] .console-usage-sort__mark {
18433
18485
  harness: "Harness",
18434
18486
  model: "Model",
18435
18487
  project: "Project",
18488
+ host: "Machine",
18489
+ localHost: "This machine",
18436
18490
  filterModel: "Filter by model",
18437
18491
  filterProject: "Filter by project",
18438
18492
  unknownModel: "Unknown model",
@@ -18629,6 +18683,8 @@ th[aria-sort] .console-usage-sort__mark {
18629
18683
  harness: "Harness",
18630
18684
  model: "\u6A21\u578B",
18631
18685
  project: "\u9879\u76EE",
18686
+ host: "\u673A\u5668",
18687
+ localHost: "\u672C\u673A",
18632
18688
  filterModel: "\u6309\u6A21\u578B\u7B5B\u9009",
18633
18689
  filterProject: "\u6309\u9879\u76EE\u7B5B\u9009",
18634
18690
  unknownModel: "\u672A\u77E5\u6A21\u578B",
@@ -20082,6 +20138,26 @@ th[aria-sort] .console-usage-sort__mark {
20082
20138
  };
20083
20139
  }
20084
20140
 
20141
+ // src/renderer-installation-client.ts
20142
+ async function runRendererInstallation(send, input) {
20143
+ const params = harnessInstallationParamsSchema.parse(input);
20144
+ const options = { priority: "background", timeoutMs: 15e3 };
20145
+ let task = harnessInstallationTaskSchema.parse(
20146
+ await send(HARNESS_INSTALLATION_START_METHOD, params, options)
20147
+ );
20148
+ const taskId = task.taskId;
20149
+ while (task.status === "running") {
20150
+ await new Promise((resolve) => globalThis.setTimeout(resolve, 500));
20151
+ task = harnessInstallationTaskSchema.parse(
20152
+ await send(HARNESS_INSTALLATION_STATUS_METHOD, { taskId }, options)
20153
+ );
20154
+ if (task.taskId !== taskId) throw new Error("Harness installation task identity changed");
20155
+ }
20156
+ if (task.status === "failed")
20157
+ throw Object.assign(new Error(task.error.message), { code: task.error.code });
20158
+ return task.result;
20159
+ }
20160
+
20085
20161
  // src/renderer-request-sender.ts
20086
20162
  var RendererMethodUnavailableError = class extends Error {
20087
20163
  constructor(method, cause) {
@@ -20278,13 +20354,7 @@ th[aria-sort] .console-usage-sort__mark {
20278
20354
  };
20279
20355
  return Object.freeze({
20280
20356
  async installation(input) {
20281
- return harnessInstallationStateSchema.parse(
20282
- await manager.sendRequest(
20283
- HARNESS_INSTALLATION_METHOD,
20284
- harnessInstallationParamsSchema.parse(input),
20285
- { priority: "interactive" }
20286
- )
20287
- );
20357
+ return runRendererInstallation(manager.sendRequest, input);
20288
20358
  },
20289
20359
  async getHarnessDisplaySettings() {
20290
20360
  return harnessDisplaySettingsSchema.parse(
@@ -21719,11 +21789,17 @@ th[aria-sort] .console-usage-sort__mark {
21719
21789
  connectionInstallRunning: "Installing on the selected Host. You can leave this page; login or setup may still be required afterward.",
21720
21790
  harnessVersion: harnessVersionEnglish,
21721
21791
  harnessVersionTitle: "Harness CLI version",
21792
+ harnessVersionApplicationTitle: "Application version",
21722
21793
  harnessVersionCurrent: "Current version",
21723
21794
  harnessVersionLatest: "Latest version",
21724
21795
  harnessVersionChecking: "Checking versions\u2026",
21725
21796
  harnessVersionAvailable: "Update available",
21726
21797
  harnessVersionUpdate: "Update",
21798
+ harnessVersionDownloadUpdate: "Download update",
21799
+ harnessUpdateAll: "Update all",
21800
+ harnessUpdateAllProgress: "Updating",
21801
+ harnessUpdateAllDescription: "Update all eligible Harnesses on the selected Host.",
21802
+ harnessUpdateAllResult: "Updated {succeeded}; failed {failed}.",
21727
21803
  harnessVersionUpdating: "Updating\u2026",
21728
21804
  harnessVersionUpToDate: "Up to date",
21729
21805
  harnessVersionUpdated: "Update verified. New sessions will use the new version.",
@@ -22022,11 +22098,17 @@ th[aria-sort] .console-usage-sort__mark {
22022
22098
  connectionInstallRunning: "\u6B63\u5728\u6240\u9009 Host \u4E0A\u5B89\u88C5\uFF0C\u79BB\u5F00\u9875\u9762\u4E0D\u4F1A\u4E2D\u65AD\u3002\u5B89\u88C5\u540E\u53EF\u80FD\u4ECD\u9700\u767B\u5F55\u6216\u914D\u7F6E\u3002",
22023
22099
  harnessVersion: harnessVersionChinese,
22024
22100
  harnessVersionTitle: "Harness CLI \u7248\u672C",
22101
+ harnessVersionApplicationTitle: "\u5E94\u7528\u7248\u672C",
22025
22102
  harnessVersionCurrent: "\u5F53\u524D\u7248\u672C",
22026
22103
  harnessVersionLatest: "\u6700\u65B0\u7248\u672C",
22027
22104
  harnessVersionChecking: "\u6B63\u5728\u68C0\u67E5\u7248\u672C\u2026",
22028
- harnessVersionAvailable: "\u6709\u65B0\u7248\u672C",
22105
+ harnessVersionAvailable: "\u6709\u66F4\u65B0",
22029
22106
  harnessVersionUpdate: "\u66F4\u65B0",
22107
+ harnessVersionDownloadUpdate: "\u4E0B\u8F7D\u66F4\u65B0",
22108
+ harnessUpdateAll: "\u5168\u90E8\u66F4\u65B0",
22109
+ harnessUpdateAllProgress: "\u66F4\u65B0\u4E2D",
22110
+ harnessUpdateAllDescription: "\u66F4\u65B0\u5F53\u524D Host \u4E0A\u652F\u6301\u81EA\u52A8\u66F4\u65B0\u4E14\u6709\u65B0\u7248\u672C\u7684 Harness\u3002",
22111
+ harnessUpdateAllResult: "\u5DF2\u66F4\u65B0 {succeeded} \u9879\uFF0C{failed} \u9879\u5931\u8D25\u3002",
22030
22112
  harnessVersionUpdating: "\u6B63\u5728\u66F4\u65B0\u2026",
22031
22113
  harnessVersionUpToDate: "\u5DF2\u662F\u6700\u65B0",
22032
22114
  harnessVersionUpdated: "\u5DF2\u786E\u8BA4\u66F4\u65B0\u6210\u529F\uFF0C\u65B0\u4F1A\u8BDD\u5C06\u4F7F\u7528\u65B0\u7248\u672C\u3002",
@@ -22146,6 +22228,34 @@ th[aria-sort] .console-usage-sort__mark {
22146
22228
  return names.size === 1 ? names.values().next().value ?? model2 : model2;
22147
22229
  }
22148
22230
 
22231
+ // src/console/usage/usage-hosts.ts
22232
+ var LOCAL_USAGE_HOST_ID = "local";
22233
+ function usageHostOptions(hosts, localLabel, selected) {
22234
+ const options = [{ key: LOCAL_USAGE_HOST_ID, label: localLabel }];
22235
+ const seen = /* @__PURE__ */ new Set([LOCAL_USAGE_HOST_ID]);
22236
+ const add = (hostId2, label) => {
22237
+ if (hostId2.length === 0 || seen.has(hostId2)) return;
22238
+ seen.add(hostId2);
22239
+ options.push({ key: hostId2, label: label !== void 0 && label.length > 0 ? label : hostId2 });
22240
+ };
22241
+ for (const host of hosts ?? []) add(host.hostId, host.label);
22242
+ if (!seen.has(selected)) add(selected, void 0);
22243
+ return options;
22244
+ }
22245
+ function createLatestKeyGuard() {
22246
+ let generation = 0;
22247
+ let key;
22248
+ return {
22249
+ begin(next) {
22250
+ generation += 1;
22251
+ key = next;
22252
+ const mine = generation;
22253
+ const selected = next;
22254
+ return () => generation === mine && Object.is(key, selected);
22255
+ }
22256
+ };
22257
+ }
22258
+
22149
22259
  // src/renderer-usage-control.ts
22150
22260
  var ENGLISH_USAGE_MESSAGES = Object.freeze({
22151
22261
  usage: "Usage",
@@ -23504,6 +23614,7 @@ th[aria-sort] .console-usage-sort__mark {
23504
23614
  const range = text("range");
23505
23615
  const measure = text("measure");
23506
23616
  const granularity = text("granularity");
23617
+ const hostId2 = text("hostId");
23507
23618
  const harness2 = text("harness");
23508
23619
  const model2 = text("model");
23509
23620
  const project2 = text("project");
@@ -23512,6 +23623,7 @@ th[aria-sort] .console-usage-sort__mark {
23512
23623
  ...range && USAGE_STATISTICS_RANGES.includes(range) ? { range } : {},
23513
23624
  ...measure && MEASURES.includes(measure) ? { measure } : {},
23514
23625
  ...granularity === "day" || granularity === "week" || granularity === "month" ? { granularity } : {},
23626
+ ...hostId2 !== void 0 && hostId2.length > 0 ? { hostId: hostId2 } : {},
23515
23627
  ...harness2 !== void 0 ? { harness: harness2 } : {},
23516
23628
  ...model2 !== void 0 ? { model: model2 } : {},
23517
23629
  ...project2 !== void 0 ? { project: project2 } : {},
@@ -23550,7 +23662,7 @@ th[aria-sort] .console-usage-sort__mark {
23550
23662
  day.setDate(day.getDate() - (day.getDay() + 6) % 7);
23551
23663
  return isoDate(day);
23552
23664
  }
23553
- function createUsageStatisticsPage(consoleMessages2, request, pageLocale) {
23665
+ function createUsageStatisticsPage(consoleMessages2, request, pageLocale, usageHosts) {
23554
23666
  const messages = consoleMessages2.usageStatistics;
23555
23667
  return Object.freeze({
23556
23668
  id: "usage-statistics",
@@ -23559,10 +23671,12 @@ th[aria-sort] .console-usage-sort__mark {
23559
23671
  mount(context) {
23560
23672
  const document2 = context.content.ownerDocument;
23561
23673
  const locale = pageLocale ?? (document2.documentElement.lang || "en");
23674
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
23562
23675
  const count3 = (value) => count2(value, locale);
23563
23676
  const tokenCount2 = (totals2, value) => tokenCount(totals2, value, locale);
23564
23677
  const saved = readView();
23565
23678
  const view = {
23679
+ hostId: saved.hostId ?? LOCAL_USAGE_HOST_ID,
23566
23680
  range: saved.range ?? "30d",
23567
23681
  measure: saved.measure ?? "cost",
23568
23682
  granularity: saved.granularity ?? null,
@@ -23593,7 +23707,7 @@ th[aria-sort] .console-usage-sort__mark {
23593
23707
  let projectLabel = /* @__PURE__ */ new Map();
23594
23708
  const projectName = (key) => key === UNKNOWN ? messages.unknownProject : projectLabel.get(key) ?? baseName(key);
23595
23709
  const measureLabel = (measure) => measure === "cost" ? messages.cost : messages.tokens;
23596
- const dateFormat = (options) => new Intl.DateTimeFormat(locale, options);
23710
+ const dateFormat = (options) => new Intl.DateTimeFormat(locale, { ...options, timeZone });
23597
23711
  const shortDate = dateFormat({ month: "short", day: "numeric" });
23598
23712
  const longDate = dateFormat({
23599
23713
  year: "numeric",
@@ -23612,10 +23726,51 @@ th[aria-sort] .console-usage-sort__mark {
23612
23726
  const prices = createModelPricesDialog(
23613
23727
  document2,
23614
23728
  consoleMessages2,
23615
- request,
23729
+ (method, params2) => request(method, params2, view.hostId),
23616
23730
  context.signal,
23617
23731
  load
23618
23732
  );
23733
+ let offeredHosts = usageHosts ? [] : void 0;
23734
+ const hostCombo = new ComboBox(document2, {
23735
+ label: messages.host,
23736
+ searchLabel: messages.search,
23737
+ emptyLabel: messages.noMatches,
23738
+ signal: context.signal,
23739
+ searchable: false,
23740
+ display: (option) => `${messages.host} \xB7 ${option.label}`,
23741
+ onChange: (key) => {
23742
+ if (key === view.hostId) return;
23743
+ view.hostId = key;
23744
+ view.harness = "";
23745
+ view.model = "";
23746
+ view.project = "";
23747
+ date5 = null;
23748
+ unpricedOnly = false;
23749
+ result = null;
23750
+ error51 = null;
23751
+ loadedAt = 0;
23752
+ names.clear();
23753
+ save();
23754
+ hostOptions();
23755
+ render();
23756
+ void refreshHarnessNames();
23757
+ load();
23758
+ }
23759
+ });
23760
+ const hostOptions = () => {
23761
+ const options = usageHostOptions(offeredHosts, messages.localHost, view.hostId);
23762
+ hostCombo.element.hidden = options.length < 2;
23763
+ hostCombo.update(options, view.hostId === LOCAL_USAGE_HOST_ID ? "" : view.hostId);
23764
+ };
23765
+ const offerHosts = () => {
23766
+ if (!usageHosts) return;
23767
+ void usageHosts().then((hosts) => {
23768
+ if (context.signal.aborted) return;
23769
+ offeredHosts = hosts;
23770
+ hostOptions();
23771
+ }).catch(() => void 0);
23772
+ };
23773
+ hostOptions();
23619
23774
  const updated = h(document2, "span", { className: "console-muted console-usage-updated" });
23620
23775
  const refresh = h(
23621
23776
  document2,
@@ -23628,7 +23783,10 @@ th[aria-sort] .console-usage-sort__mark {
23628
23783
  },
23629
23784
  createRendererSettingsIcon("refresh", 16)
23630
23785
  );
23631
- refresh.addEventListener("click", () => load());
23786
+ refresh.addEventListener("click", () => {
23787
+ offerHosts();
23788
+ load();
23789
+ });
23632
23790
  const auto = new ComboBox(document2, {
23633
23791
  label: messages.autoRefresh,
23634
23792
  searchLabel: messages.search,
@@ -23667,6 +23825,7 @@ th[aria-sort] .console-usage-sort__mark {
23667
23825
  document2,
23668
23826
  "div",
23669
23827
  { className: "console-usage-toolbar" },
23828
+ hostCombo.element,
23670
23829
  updated,
23671
23830
  refresh,
23672
23831
  auto.element,
@@ -24305,7 +24464,6 @@ ${detail}` : ""}`
24305
24464
  }
24306
24465
  function hourlyPanel(data) {
24307
24466
  const singleDay = data.filters.date !== null || data.range === "today";
24308
- const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
24309
24467
  return panel(
24310
24468
  singleDay ? messages.hourlyDay : messages.hourly,
24311
24469
  hourlyChart(document2, {
@@ -24320,7 +24478,7 @@ ${detail}` : ""}`
24320
24478
  weekStartsOnMonday: !locale.startsWith("en"),
24321
24479
  labels: { cost: messages.cost, tokens: messages.tokens }
24322
24480
  }),
24323
- h(document2, "span", { className: "console-muted console-usage-zone" }, zone)
24481
+ h(document2, "span", { className: "console-muted console-usage-zone" }, timeZone)
24324
24482
  );
24325
24483
  }
24326
24484
  function sessionsPanel(title, rows, tableKey) {
@@ -24467,6 +24625,7 @@ ${detail}` : ""}`
24467
24625
  const project2 = filterValue(view.project);
24468
24626
  return {
24469
24627
  range: view.range,
24628
+ timeZone,
24470
24629
  ...view.harness ? { harness: view.harness } : {},
24471
24630
  ...model2 !== void 0 ? { model: model2 } : {},
24472
24631
  ...project2 !== void 0 ? { project: project2 } : {},
@@ -24479,10 +24638,26 @@ ${detail}` : ""}`
24479
24638
  if (result && !result.reading.complete) poll = window.setTimeout(load, POLL_MS);
24480
24639
  else if (view.autoRefreshMs > 0) poll = window.setTimeout(load, view.autoRefreshMs);
24481
24640
  }
24641
+ const harnessNamesRead = createLatestKeyGuard();
24642
+ async function refreshHarnessNames() {
24643
+ const hostId2 = view.hostId;
24644
+ const isCurrent = harnessNamesRead.begin(hostId2);
24645
+ names.clear();
24646
+ try {
24647
+ const value = await request(HARNESS_PLUGIN_LIST_METHOD2, {}, hostId2);
24648
+ if (!isCurrent()) return;
24649
+ for (const plugin of harnessPluginListResultSchema.parse(value).plugins) {
24650
+ names.set(plugin.id, plugin.name);
24651
+ }
24652
+ } catch {
24653
+ if (!isCurrent()) return;
24654
+ }
24655
+ if (!context.signal.aborted) render();
24656
+ }
24482
24657
  function load() {
24483
24658
  window.clearTimeout(poll);
24484
24659
  const requested = params();
24485
- void context.runLatest(() => request(USAGE_STATISTICS_METHOD, requested), {
24660
+ void context.runLatest(() => request(USAGE_STATISTICS_METHOD, requested, view.hostId), {
24486
24661
  success(value) {
24487
24662
  const parsed = usageStatisticsResultSchema.safeParse(value);
24488
24663
  if (parsed.success) {
@@ -24507,12 +24682,8 @@ ${detail}` : ""}`
24507
24682
  }
24508
24683
  context.content.replaceChildren(header, prices.element, filters, notices, body);
24509
24684
  render();
24510
- void request(HARNESS_PLUGIN_LIST_METHOD2, {}).then((value) => {
24511
- for (const plugin of harnessPluginListResultSchema.parse(value).plugins) {
24512
- names.set(plugin.id, plugin.name);
24513
- }
24514
- if (!context.signal.aborted) render();
24515
- }).catch(() => void 0);
24685
+ offerHosts();
24686
+ void refreshHarnessNames();
24516
24687
  load();
24517
24688
  return () => {
24518
24689
  window.clearTimeout(poll);
@@ -26057,17 +26228,32 @@ ${detail}` : ""}`
26057
26228
  let busy = false;
26058
26229
  let updating = false;
26059
26230
  let unsupported = false;
26231
+ let updatePromise = null;
26060
26232
  const render = () => {
26233
+ heading.textContent = state?.versionKind === "application" ? messages.harnessVersionApplicationTitle : messages.harnessVersionTitle;
26061
26234
  current.textContent = `${messages.harnessVersionCurrent}: ${state?.currentVersion ?? "\u2014"}`;
26062
26235
  latest.textContent = `${messages.harnessVersionLatest}: ${harnessVersionLabel(state, messages.harnessVersion)}`;
26063
26236
  update.disabled = busy || signal.aborted || !state?.canUpdate || !state.updateAvailable;
26237
+ const manualUrl = !state?.canUpdate && state?.updateAvailable ? state.manualUpdateUrl : void 0;
26238
+ actions.replaceChildren(update);
26239
+ update.hidden = !busy && !signal.aborted && Boolean(manualUrl);
26240
+ if (update.hidden && manualUrl) {
26241
+ const download2 = document2.createElement("a");
26242
+ download2.className = "settings-command-button settings-command-button--secondary";
26243
+ download2.dataset.harnessVersionAction = "download-update";
26244
+ download2.textContent = messages.harnessVersionDownloadUpdate;
26245
+ download2.href = manualUrl;
26246
+ download2.target = "_blank";
26247
+ download2.rel = "noopener noreferrer";
26248
+ actions.append(download2);
26249
+ }
26064
26250
  update.textContent = state?.canUpdate && state.latestVersion !== "Unknown" && !state.updateAvailable ? messages.harnessVersionUpToDate : messages.harnessVersionUpdate;
26065
26251
  const detail = harnessVersionNote(state, messages.harnessVersion);
26066
- note.textContent = detail ? `${detail} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
26252
+ note.textContent = detail ? !state?.canUpdate ? detail : `${detail} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
26067
26253
  };
26068
26254
  const run = async (action) => {
26069
- if (busy || unsupported || signal.aborted) return;
26070
- if (action === "update" && (!state?.canUpdate || !state.updateAvailable)) return;
26255
+ if (busy || unsupported || signal.aborted) return false;
26256
+ if (action === "update" && (!state?.canUpdate || !state.updateAvailable)) return false;
26071
26257
  busy = true;
26072
26258
  updating = action === "update";
26073
26259
  onChange();
@@ -26075,14 +26261,16 @@ ${detail}` : ""}`
26075
26261
  render();
26076
26262
  try {
26077
26263
  const result = await client.run(action);
26078
- if (signal.aborted) return;
26264
+ if (signal.aborted) return false;
26079
26265
  state = result;
26080
26266
  status.textContent = !result.canUpdate ? messages.harnessVersionManual : action === "update" ? result.updateAvailable ? messages.harnessVersionFailed : messages.harnessVersionUpdated : "";
26267
+ return result.canUpdate && !result.updateAvailable;
26081
26268
  } catch (error51) {
26082
- if (signal.aborted) return;
26269
+ if (signal.aborted) return false;
26083
26270
  unsupported = typeof error51 === "object" && error51 !== null && "code" in error51 && (error51.code === -32601 || error51.code === -32078);
26084
26271
  status.textContent = unsupported ? messages.harnessVersionUnsupported : messages.harnessVersionFailed;
26085
26272
  if (state) state = { ...state, canUpdate: false };
26273
+ return false;
26086
26274
  } finally {
26087
26275
  busy = false;
26088
26276
  updating = false;
@@ -26092,7 +26280,13 @@ ${detail}` : ""}`
26092
26280
  }
26093
26281
  }
26094
26282
  };
26095
- update.addEventListener("click", () => void run("update"));
26283
+ const updateVersion = () => {
26284
+ updatePromise ??= run("update").finally(() => {
26285
+ updatePromise = null;
26286
+ });
26287
+ return updatePromise;
26288
+ };
26289
+ update.addEventListener("click", () => void updateVersion());
26096
26290
  const owner = document2.defaultView;
26097
26291
  let checkFrame = null;
26098
26292
  let checkTimer = null;
@@ -26127,7 +26321,7 @@ ${detail}` : ""}`
26127
26321
  get updating() {
26128
26322
  return updating;
26129
26323
  },
26130
- update: () => void run("update")
26324
+ update: updateVersion
26131
26325
  };
26132
26326
  }
26133
26327
 
@@ -26158,6 +26352,58 @@ ${detail}` : ""}`
26158
26352
  };
26159
26353
  }
26160
26354
 
26355
+ // src/settings/connection-updates.ts
26356
+ function createConnectionUpdates(document2, messages, signal, onChange) {
26357
+ const batches = /* @__PURE__ */ new Map();
26358
+ const eligible = (panels) => panels.filter((panel) => panel.state?.updateAvailable && panel.state.canUpdate);
26359
+ const start = async (hostId2, panels) => {
26360
+ if (signal.aborted || batches.get(hostId2)?.running) return;
26361
+ const targets = eligible(panels);
26362
+ if (!targets.length) return;
26363
+ const batch = { running: true, total: targets.length, succeeded: 0, failed: 0 };
26364
+ batches.set(hostId2, batch);
26365
+ onChange();
26366
+ await Promise.all(
26367
+ targets.map(async (panel) => {
26368
+ try {
26369
+ if (await panel.update()) batch.succeeded++;
26370
+ else batch.failed++;
26371
+ } catch {
26372
+ batch.failed++;
26373
+ }
26374
+ if (!signal.aborted) onChange();
26375
+ })
26376
+ );
26377
+ batch.running = false;
26378
+ if (!signal.aborted) onChange();
26379
+ };
26380
+ return {
26381
+ createControls(hostId2, panels) {
26382
+ const batch = batches.get(hostId2);
26383
+ const count3 = eligible(panels).length;
26384
+ const button2 = document2.createElement("button");
26385
+ button2.type = "button";
26386
+ button2.className = "settings-command-button settings-command-button--secondary settings-connection-update-all";
26387
+ button2.dataset.connectionAction = "update-all";
26388
+ button2.disabled = signal.aborted || Boolean(batch?.running) || !count3;
26389
+ button2.title = messages.harnessUpdateAllDescription;
26390
+ const label = batch?.running ? `${messages.harnessUpdateAllProgress} ${batch.succeeded + batch.failed}/${batch.total}` : count3 ? `${messages.harnessUpdateAll} (${count3})` : messages.harnessUpdateAll;
26391
+ button2.append(createRendererSettingsIcon("download", 15), label);
26392
+ button2.addEventListener("click", () => {
26393
+ if (!button2.disabled) void start(hostId2, panels);
26394
+ });
26395
+ const result = document2.createElement("p");
26396
+ result.className = "settings-connection-update-result";
26397
+ result.setAttribute("role", "status");
26398
+ result.hidden = !batch || batch.running;
26399
+ if (batch && !batch.running) {
26400
+ result.textContent = messages.harnessUpdateAllResult.replace("{succeeded}", String(batch.succeeded)).replace("{failed}", String(batch.failed));
26401
+ }
26402
+ return { button: button2, result };
26403
+ }
26404
+ };
26405
+ }
26406
+
26161
26407
  // src/settings/connections-page.ts
26162
26408
  var CODEXHOST_GITHUB_ISSUES_NEW_URL = "https://github.com/karlorz/codex-host/issues/new";
26163
26409
  function connectionStatusLabel(availability, messages, hasError = false) {
@@ -26324,13 +26570,6 @@ ${error51.stderrTail}`] : []
26324
26570
  status.setAttribute("role", "cell");
26325
26571
  status.textContent = item.error?.code === "authenticationRequired" ? messages.connectionLoginRequired : item.error?.code === "configurationRequired" ? messages.connectionConfigurationRequired : connectionStatusLabel(item.availability, messages, item.error !== null);
26326
26572
  const version2 = item.version;
26327
- if (version2?.state?.updateAvailable && !version2.updating) {
26328
- const available = document2.createElement("span");
26329
- available.className = "settings-connection-version-available";
26330
- available.textContent = `${messages.harnessVersionAvailable}: ${harnessVersionLabel(version2.state, messages.harnessVersion)}`;
26331
- available.title = `${messages.harnessVersionCurrent}: ${version2.state.currentVersion ?? "\u2014"}`;
26332
- status.append(available);
26333
- }
26334
26573
  const action = document2.createElement("div");
26335
26574
  action.className = "settings-connection-row__action";
26336
26575
  action.setAttribute("role", "cell");
@@ -26360,6 +26599,14 @@ ${error51.stderrTail}`] : []
26360
26599
  });
26361
26600
  action.append(viewError);
26362
26601
  }
26602
+ if (version2?.state?.updateAvailable && !version2.updating) {
26603
+ const available = document2.createElement("span");
26604
+ available.className = "settings-connection-version-available";
26605
+ available.textContent = messages.harnessVersionAvailable;
26606
+ available.title = `${messages.harnessVersionCurrent}: ${version2.state.currentVersion ?? "\u2014"}
26607
+ ${messages.harnessVersionLatest}: ${harnessVersionLabel(version2.state, messages.harnessVersion)}`;
26608
+ action.append(available);
26609
+ }
26363
26610
  if (version2?.state?.updateAvailable && version2.state.canUpdate) {
26364
26611
  const update = document2.createElement("button");
26365
26612
  update.type = "button";
@@ -26372,7 +26619,7 @@ ${error51.stderrTail}`] : []
26372
26619
  update.addEventListener("click", (event) => {
26373
26620
  event.stopPropagation();
26374
26621
  if (update.disabled) return;
26375
- version2.update();
26622
+ void version2.update();
26376
26623
  select();
26377
26624
  });
26378
26625
  action.append(update);
@@ -26711,6 +26958,7 @@ ${error51.stderrTail}`] : []
26711
26958
  scheduleRender
26712
26959
  );
26713
26960
  const installs = diagnostics ? harnessInstallStore(diagnostics) : null;
26961
+ const updates = createConnectionUpdates(document2, messages, context.signal, scheduleRender);
26714
26962
  const runRefresh = () => {
26715
26963
  if (pending || !diagnostics) return;
26716
26964
  pending = true;
@@ -27045,7 +27293,14 @@ ${error51.stderrTail}`] : []
27045
27293
  }
27046
27294
  const selectedItem = items.find((item) => item.key === selectedItemKey) ?? items[0];
27047
27295
  if (selectedItem) selectItem(selectedItem);
27048
- list.append(hostStrip, tableHeader, rows);
27296
+ const updateControls = updates.createControls(
27297
+ selectedHost.hostId,
27298
+ items.flatMap((item) => item.version ? [item.version] : [])
27299
+ );
27300
+ const hostHeader = document2.createElement("div");
27301
+ hostHeader.className = "settings-connection-host-header";
27302
+ hostHeader.append(hostStrip, updateControls.button);
27303
+ list.append(hostHeader, updateControls.result, tableHeader, rows);
27049
27304
  layout.append(list, inspector);
27050
27305
  content.replaceChildren(layout);
27051
27306
  disposeHostScroller = configureHostScroller(
@@ -30218,7 +30473,7 @@ ${error51.stderrTail}`] : []
30218
30473
  }
30219
30474
  });
30220
30475
  }
30221
- function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread = () => Promise.reject(new Error("Imported Thread navigation is unavailable")), getLoadedSessionsClient = () => null, getRemoteConnections = () => null, requestUsageStatistics = () => Promise.reject(new Error("Local Host connection is unavailable")), requestVoiceSettings = () => Promise.reject(new Error("Local Host connection is unavailable"))) {
30476
+ function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread = () => Promise.reject(new Error("Imported Thread navigation is unavailable")), getLoadedSessionsClient = () => null, getRemoteConnections = () => null, requestUsageStatistics = () => Promise.reject(new Error("Local Host connection is unavailable")), usageStatisticsHosts, requestVoiceSettings = () => Promise.reject(new Error("Local Host connection is unavailable"))) {
30222
30477
  return Object.freeze([
30223
30478
  createConnectionsSettingsPage(messages, getDiagnostics, void 0, getRemoteConnections),
30224
30479
  createRemoteConnectionsPage(messages, getRemoteConnections),
@@ -30232,7 +30487,8 @@ ${error51.stderrTail}`] : []
30232
30487
  createUsageStatisticsPage(
30233
30488
  consoleMessages(messages.locale),
30234
30489
  requestUsageStatistics,
30235
- messages.locale
30490
+ messages.locale,
30491
+ usageStatisticsHosts
30236
30492
  ),
30237
30493
  createAppearanceSettingsPage(messages, getLoadedSessionsClient),
30238
30494
  createVoiceSettingsPage(messages.locale, requestVoiceSettings, { desktop: true }),
@@ -31010,6 +31266,20 @@ ${error51.stderrTail}`] : []
31010
31266
  gap: 18px;
31011
31267
  }
31012
31268
 
31269
+ .settings-connections-content {
31270
+ container: settings-connections / inline-size;
31271
+ }
31272
+
31273
+ @container settings-connections (max-width: 780px) {
31274
+ .settings-connections-layout {
31275
+ grid-template-columns: minmax(0, 1fr);
31276
+ }
31277
+
31278
+ .settings-connection-inspector {
31279
+ position: static;
31280
+ }
31281
+ }
31282
+
31013
31283
  .settings-connection-list,
31014
31284
  .settings-connection-inspector {
31015
31285
  overflow: hidden;
@@ -31018,14 +31288,36 @@ ${error51.stderrTail}`] : []
31018
31288
  border-radius: 10px;
31019
31289
  }
31020
31290
 
31291
+ .settings-connection-host-header {
31292
+ display: flex;
31293
+ align-items: center;
31294
+ min-width: 0;
31295
+ border-bottom: 1px solid var(--settings-divider);
31296
+ }
31297
+
31298
+ .settings-connection-update-all {
31299
+ flex: none;
31300
+ margin-right: 10px;
31301
+ white-space: nowrap;
31302
+ }
31303
+
31304
+ .settings-connection-update-result {
31305
+ margin: 0;
31306
+ padding: 7px 10px;
31307
+ color: var(--settings-muted);
31308
+ font-size: 11px;
31309
+ line-height: 18px;
31310
+ border-bottom: 1px solid var(--settings-divider);
31311
+ }
31312
+
31021
31313
  .settings-connection-host-strip {
31022
31314
  display: grid;
31315
+ flex: 1;
31023
31316
  grid-template-columns: 30px minmax(0, 1fr) 30px;
31024
31317
  align-items: center;
31025
31318
  min-width: 0;
31026
31319
  gap: 5px;
31027
31320
  padding: 9px 10px;
31028
- border-bottom: 1px solid var(--settings-divider);
31029
31321
  }
31030
31322
 
31031
31323
  .settings-connection-host-scroll {
@@ -31122,7 +31414,7 @@ ${error51.stderrTail}`] : []
31122
31414
  .settings-connection-table-header,
31123
31415
  .settings-connection-row {
31124
31416
  display: grid;
31125
- grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) 108px;
31417
+ grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) minmax(108px, max-content);
31126
31418
  align-items: center;
31127
31419
  min-width: 0;
31128
31420
  gap: 10px;
@@ -31215,15 +31507,12 @@ ${error51.stderrTail}`] : []
31215
31507
  line-height: 18px;
31216
31508
  }
31217
31509
 
31218
- .settings-connection-row__status:has(.settings-connection-version-available) {
31219
- flex-wrap: wrap;
31220
- max-width: 160px;
31221
- }
31222
-
31223
31510
  .settings-connection-version-available {
31224
- flex-basis: 100%;
31225
- color: var(--settings-focus);
31226
- overflow-wrap: anywhere;
31511
+ flex: none;
31512
+ color: var(--settings-muted);
31513
+ font-size: 10px;
31514
+ line-height: 16px;
31515
+ white-space: nowrap;
31227
31516
  }
31228
31517
 
31229
31518
  .settings-connection-row__status::before {
@@ -31262,6 +31551,7 @@ ${error51.stderrTail}`] : []
31262
31551
  justify-content: flex-end;
31263
31552
  min-width: 0;
31264
31553
  gap: 4px;
31554
+ white-space: nowrap;
31265
31555
  }
31266
31556
 
31267
31557
  .settings-connection-row__handle {