@codexhost/cli-win32-arm64 0.13.3 → 0.15.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 (33) hide show
  1. package/README.md +1 -1
  2. package/app/codexhost-distribution.json +1 -1
  3. package/app/console-server.mjs +109 -11
  4. package/app/console-web.js +649 -132
  5. package/app/desktop-controller.mjs +120 -12
  6. package/app/host-runtime.mjs +1124 -293
  7. package/app/plugins/antigravity/plugin.mjs +431 -66
  8. package/app/plugins/claude-code/plugin.mjs +761 -139
  9. package/app/plugins/codebuddy/plugin.mjs +322 -68
  10. package/app/plugins/codex-usage/plugin.mjs +249 -102
  11. package/app/plugins/copilot/assets/icon.png +0 -0
  12. package/app/plugins/copilot/manifest.json +26 -0
  13. package/app/plugins/copilot/plugin.mjs +34591 -0
  14. package/app/plugins/cursor-cli/plugin.mjs +21011 -44970
  15. package/app/plugins/deepseek-harness/plugin.mjs +160 -43
  16. package/app/plugins/enabled.json +1 -0
  17. package/app/plugins/grok/plugin.mjs +565 -107
  18. package/app/plugins/hermes/plugin.mjs +128 -12
  19. package/app/plugins/kimi-code/plugin.mjs +136 -22
  20. package/app/plugins/kiro-cli/plugin.mjs +136 -22
  21. package/app/plugins/omp/plugin.mjs +2236 -2036
  22. package/app/plugins/opencode/plugin.mjs +187 -68
  23. package/app/plugins/pi/plugin.mjs +2621 -2304
  24. package/app/plugins/qoder/plugin.mjs +153 -36
  25. package/app/plugins/qoder-cn/plugin.mjs +153 -36
  26. package/app/plugins/workbuddy/plugin.mjs +583 -92
  27. package/app/plugins/zcode/plugin.mjs +183 -40
  28. package/app/renderer-extension.js +1758 -392
  29. package/bin/codexhost.exe +0 -0
  30. package/libexec/codexhost-node-repl.exe +0 -0
  31. package/libexec/codexhost-shim.exe +0 -0
  32. package/libexec/codexhost-updater.exe +0 -0
  33. package/package.json +1 -1
@@ -3105,10 +3105,10 @@
3105
3105
  const shape = def.shape;
3106
3106
  const propValues = {};
3107
3107
  for (const key in shape) {
3108
- const field = shape[key]._zod;
3109
- if (field.values) {
3108
+ const field2 = shape[key]._zod;
3109
+ if (field2.values) {
3110
3110
  propValues[key] ?? (propValues[key] = /* @__PURE__ */ new Set());
3111
- for (const v of field.values)
3111
+ for (const v of field2.values)
3112
3112
  propValues[key].add(v);
3113
3113
  }
3114
3114
  }
@@ -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),
@@ -14887,6 +14896,51 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14887
14896
  var threadPrewarmDiscardParamsSchema = external_exports.object({ threadId: hostThreadIdSchema });
14888
14897
  var threadPrewarmDiscardResultSchema = external_exports.object({ discarded: external_exports.boolean() });
14889
14898
 
14899
+ // ../shared-contracts/dist/thread-questions.js
14900
+ var field = external_exports.discriminatedUnion("type", [
14901
+ external_exports.object({
14902
+ id: external_exports.string().min(1),
14903
+ type: external_exports.literal("choice"),
14904
+ prompt: external_exports.string(),
14905
+ options: external_exports.array(external_exports.object({
14906
+ value: external_exports.string().min(1),
14907
+ label: external_exports.string(),
14908
+ description: external_exports.string().optional()
14909
+ })).min(1),
14910
+ multiple: external_exports.boolean(),
14911
+ allowOther: external_exports.boolean(),
14912
+ optional: external_exports.boolean()
14913
+ }),
14914
+ external_exports.object({
14915
+ id: external_exports.string().min(1),
14916
+ type: external_exports.literal("text"),
14917
+ prompt: external_exports.string(),
14918
+ multiline: external_exports.boolean(),
14919
+ secret: external_exports.literal(false),
14920
+ optional: external_exports.boolean(),
14921
+ placeholder: external_exports.string().optional(),
14922
+ prefill: external_exports.string().optional()
14923
+ })
14924
+ ]);
14925
+ var threadQuestionSchema = external_exports.object({
14926
+ interactionId: external_exports.string().min(1),
14927
+ turnId: external_exports.string().min(1),
14928
+ title: external_exports.string().optional(),
14929
+ questions: external_exports.array(field).min(1)
14930
+ }).refine(({ questions }) => questions.some((q) => q.type === "choice" && q.multiple), {
14931
+ message: "Shared cards require at least one multi-select question"
14932
+ });
14933
+ var threadQuestionsListParamsSchema = external_exports.strictObject({ threadId: external_exports.string().min(1) });
14934
+ var threadQuestionsResultSchema = external_exports.object({ questions: external_exports.array(threadQuestionSchema) });
14935
+ var threadQuestionRespondParamsSchema = external_exports.strictObject({
14936
+ threadId: external_exports.string().min(1),
14937
+ turnId: external_exports.string().min(1),
14938
+ interactionId: external_exports.string().min(1),
14939
+ answers: external_exports.record(external_exports.string(), external_exports.array(external_exports.string())),
14940
+ cancelled: external_exports.boolean().default(false)
14941
+ });
14942
+ var threadQuestionRespondResultSchema = external_exports.object({ accepted: external_exports.literal(true) });
14943
+
14890
14944
  // ../shared-contracts/dist/console.js
14891
14945
  var consoleAnnouncementSchema = external_exports.strictObject({
14892
14946
  title: external_exports.string().trim().min(1).max(200),
@@ -14908,6 +14962,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14908
14962
  "codexhost/harness/display-settings/set",
14909
14963
  "codexhost/harness/inspect",
14910
14964
  "codexhost/harness/installation",
14965
+ "codexhost/harness/installation/start",
14966
+ "codexhost/harness/installation/status",
14911
14967
  "codexhost/harness/launch-settings/get",
14912
14968
  "codexhost/harness/launch-settings/set",
14913
14969
  "codexhost/harness/web-ui/open",
@@ -14985,7 +15041,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14985
15041
  var nonNegativeSafeIntegerSchema = external_exports.number().int().safe().nonnegative();
14986
15042
  var finiteNonNegativeNumberSchema = external_exports.number().finite().nonnegative();
14987
15043
  var cacheHitRatePercentSchema = external_exports.number().finite().min(0).max(100);
15044
+ var indicatorTextSchema = (maximum) => harnessLocalizedTextSchema.refine((entries) => Object.values(entries).every((text) => text.length <= maximum));
15045
+ var composerIndicatorsSchema = external_exports.array(external_exports.object({
15046
+ kind: external_exports.literal("countdown"),
15047
+ id: external_exports.string().min(1).max(64),
15048
+ expiresAtUnix: nonNegativeSafeIntegerSchema,
15049
+ label: indicatorTextSchema(256).optional(),
15050
+ tooltip: indicatorTextSchema(1024).optional()
15051
+ }).strict()).max(2).refine((entries) => new Set(entries.map(({ id }) => id)).size === entries.length, {
15052
+ message: "Composer indicator IDs must be unique"
15053
+ });
14988
15054
  var threadUsageSnapshotSchema = external_exports.object({
15055
+ composerIndicators: composerIndicatorsSchema.optional(),
14989
15056
  inputTokens: nonNegativeSafeIntegerSchema.optional(),
14990
15057
  cachedInputTokens: nonNegativeSafeIntegerSchema.optional(),
14991
15058
  cacheWriteInputTokens: nonNegativeSafeIntegerSchema.optional(),
@@ -15393,7 +15460,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15393
15460
  }).strict().optional(),
15394
15461
  autonomousTurns: external_exports.object({
15395
15462
  observe: external_exports.boolean()
15396
- }).strict().optional()
15463
+ }).strict().optional(),
15464
+ /** True only when `turn.steer` delivers input into the active Turn through a native primitive. */
15465
+ steer: external_exports.boolean().optional()
15397
15466
  }).strict();
15398
15467
  var harnessConfigurationStateSchema = external_exports.object({
15399
15468
  effectiveModel: harnessModelRefSchema.optional(),
@@ -15441,7 +15510,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15441
15510
  var harnessInspectParamsSchema = external_exports.object({
15442
15511
  harnessId: harnessIdSchema,
15443
15512
  cwd: nonBlankTextSchema2.max(16384).optional(),
15444
- refresh: external_exports.boolean().optional()
15513
+ refresh: external_exports.boolean().optional(),
15514
+ /** `availability` asks only whether the Harness can run; see `InspectHarnessInput.purpose`. */
15515
+ purpose: external_exports.literal("availability").optional()
15445
15516
  }).strict();
15446
15517
  var harnessWebUiOpenParamsSchema = external_exports.object({
15447
15518
  harnessId: harnessIdSchema
@@ -15524,6 +15595,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15524
15595
  seen.add(thread.threadId);
15525
15596
  }
15526
15597
  });
15598
+ var threadSteeringInspectParamsSchema = external_exports.object({
15599
+ threadId: hostThreadIdSchema,
15600
+ /** The text about to be steered; a Harness command always stops and restarts. */
15601
+ text: external_exports.string().optional()
15602
+ }).strict();
15603
+ var threadSteerDeliverySchema = external_exports.enum(["activeTurn", "newTurn"]);
15604
+ var threadSteeringInspectResultSchema = external_exports.object({
15605
+ delivery: external_exports.enum(["official", "activeTurn", "newTurn"])
15606
+ }).strict();
15527
15607
 
15528
15608
  // ../shared-contracts/dist/harness-plugins.js
15529
15609
  var HARNESS_PLUGIN_MANIFEST_MAX_BYTES = 32 * 1024;
@@ -15618,7 +15698,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15618
15698
  }).strict();
15619
15699
 
15620
15700
  // ../shared-contracts/dist/harness-installation.js
15621
- var HARNESS_INSTALLATION_METHOD = "codexhost/harness/installation";
15701
+ var HARNESS_INSTALLATION_START_METHOD = "codexhost/harness/installation/start";
15702
+ var HARNESS_INSTALLATION_STATUS_METHOD = "codexhost/harness/installation/status";
15622
15703
  var harnessInstallationParamsSchema = external_exports.object({
15623
15704
  harnessId: harnessIdSchema,
15624
15705
  action: external_exports.enum(["check", "update", "install"])
@@ -15628,6 +15709,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15628
15709
  latestVersion: external_exports.string().min(1).max(128),
15629
15710
  updateAvailable: external_exports.boolean(),
15630
15711
  canUpdate: external_exports.boolean(),
15712
+ /** Defaults to CLI; some Harnesses ship only inside a desktop application. */
15713
+ versionKind: external_exports.enum(["cli", "application"]).optional(),
15714
+ /** Official download opened by the user; does not imply an automatic installation. */
15715
+ manualUpdateUrl: external_exports.url().max(2048).refine((value) => /^https:\/\/[^/?#@]+(?:[/?#]|$)/iu.test(value), "Manual updates require a credential-free HTTPS URL").optional(),
15631
15716
  // Adapter-owned diagnostic text; UI uses messageCode rather than displaying it verbatim.
15632
15717
  message: external_exports.string().max(2048).optional(),
15633
15718
  messageCode: external_exports.string().min(1).max(128).optional(),
@@ -15635,6 +15720,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15635
15720
  notice: harnessLocalizedTextSchema.optional(),
15636
15721
  latestVersionKind: external_exports.enum(["unknown", "tracking-branch"]).optional()
15637
15722
  }).strict();
15723
+ var harnessInstallationTaskParamsSchema = external_exports.object({ taskId: external_exports.string().uuid() }).strict();
15724
+ var harnessInstallationTaskSchema = external_exports.discriminatedUnion("status", [
15725
+ external_exports.object({ taskId: external_exports.string().uuid(), status: external_exports.literal("running") }).strict(),
15726
+ external_exports.object({
15727
+ taskId: external_exports.string().uuid(),
15728
+ status: external_exports.literal("succeeded"),
15729
+ result: harnessInstallationStateSchema
15730
+ }).strict(),
15731
+ external_exports.object({
15732
+ taskId: external_exports.string().uuid(),
15733
+ status: external_exports.literal("failed"),
15734
+ error: external_exports.object({ code: external_exports.number().int(), message: external_exports.string().min(1).max(2048) }).strict()
15735
+ }).strict()
15736
+ ]);
15638
15737
 
15639
15738
  // ../shared-contracts/dist/codex-accounts.js
15640
15739
  var accountIdSchema = external_exports.string().min(1).max(256).regex(/^[A-Za-z0-9._~-]+$/u);
@@ -18298,6 +18397,8 @@ th[aria-sort] .console-usage-sort__mark {
18298
18397
  harness: "Harness",
18299
18398
  model: "Model",
18300
18399
  project: "Project",
18400
+ host: "Machine",
18401
+ localHost: "This machine",
18301
18402
  filterModel: "Filter by model",
18302
18403
  filterProject: "Filter by project",
18303
18404
  unknownModel: "Unknown model",
@@ -18481,6 +18582,8 @@ th[aria-sort] .console-usage-sort__mark {
18481
18582
  harness: "Harness",
18482
18583
  model: "\u6A21\u578B",
18483
18584
  project: "\u9879\u76EE",
18585
+ host: "\u673A\u5668",
18586
+ localHost: "\u672C\u673A",
18484
18587
  filterModel: "\u6309\u6A21\u578B\u7B5B\u9009",
18485
18588
  filterProject: "\u6309\u9879\u76EE\u7B5B\u9009",
18486
18589
  unknownModel: "\u672A\u77E5\u6A21\u578B",
@@ -19245,6 +19348,26 @@ th[aria-sort] .console-usage-sort__mark {
19245
19348
  };
19246
19349
  }
19247
19350
 
19351
+ // src/renderer-installation-client.ts
19352
+ async function runRendererInstallation(send, input) {
19353
+ const params = harnessInstallationParamsSchema.parse(input);
19354
+ const options = { priority: "background", timeoutMs: 15e3 };
19355
+ let task = harnessInstallationTaskSchema.parse(
19356
+ await send(HARNESS_INSTALLATION_START_METHOD, params, options)
19357
+ );
19358
+ const taskId = task.taskId;
19359
+ while (task.status === "running") {
19360
+ await new Promise((resolve) => globalThis.setTimeout(resolve, 500));
19361
+ task = harnessInstallationTaskSchema.parse(
19362
+ await send(HARNESS_INSTALLATION_STATUS_METHOD, { taskId }, options)
19363
+ );
19364
+ if (task.taskId !== taskId) throw new Error("Harness installation task identity changed");
19365
+ }
19366
+ if (task.status === "failed")
19367
+ throw Object.assign(new Error(task.error.message), { code: task.error.code });
19368
+ return task.result;
19369
+ }
19370
+
19248
19371
  // src/renderer-request-sender.ts
19249
19372
  var RendererMethodUnavailableError = class extends Error {
19250
19373
  constructor(method, cause) {
@@ -19393,7 +19516,15 @@ th[aria-sort] .console-usage-sort__mark {
19393
19516
  };
19394
19517
  const inspectHarness = async (input, options) => {
19395
19518
  const params = harnessInspectParamsSchema.parse(input);
19396
- const result = await manager.sendRequest(HARNESS_INSPECT_METHOD, params, options);
19519
+ let result;
19520
+ try {
19521
+ result = await manager.sendRequest(HARNESS_INSPECT_METHOD, params, options);
19522
+ } catch (error51) {
19523
+ if (!params.purpose || !isInvalidParams(error51)) throw error51;
19524
+ const full = { ...params };
19525
+ delete full.purpose;
19526
+ result = await manager.sendRequest(HARNESS_INSPECT_METHOD, full, options);
19527
+ }
19397
19528
  return harnessInspectionSchema.parse(result);
19398
19529
  };
19399
19530
  const inspectHarnessCommands = async (input) => {
@@ -19433,13 +19564,7 @@ th[aria-sort] .console-usage-sort__mark {
19433
19564
  };
19434
19565
  return Object.freeze({
19435
19566
  async installation(input) {
19436
- return harnessInstallationStateSchema.parse(
19437
- await manager.sendRequest(
19438
- HARNESS_INSTALLATION_METHOD,
19439
- harnessInstallationParamsSchema.parse(input),
19440
- { priority: "interactive" }
19441
- )
19442
- );
19567
+ return runRendererInstallation(manager.sendRequest, input);
19443
19568
  },
19444
19569
  async getHarnessDisplaySettings() {
19445
19570
  return harnessDisplaySettingsSchema.parse(
@@ -19655,6 +19780,9 @@ th[aria-sort] .console-usage-sort__mark {
19655
19780
  }
19656
19781
  });
19657
19782
  }
19783
+ function isInvalidParams(error51) {
19784
+ return typeof error51 === "object" && error51 !== null && "code" in error51 && error51.code === -32602;
19785
+ }
19658
19786
 
19659
19787
  // src/settings/accounts.css
19660
19788
  var accounts_default = `.settings-harness-account__logo {
@@ -20712,12 +20840,13 @@ th[aria-sort] .console-usage-sort__mark {
20712
20840
  notAvailable: "Not available",
20713
20841
  runtimeCapabilityNotInstalled: "This runtime capability is not installed yet.",
20714
20842
  sessionImportHarness: "Harness",
20843
+ sessionImportSelectHost: "Select the Host to import sessions from",
20715
20844
  sessionImportDescription: "Sessions keep their original project path. If a folder is not in the Codex sidebar, add it as a project first. Original history remains managed by the Harness.",
20716
- sessionImportAvailabilityNote: "Available Harnesses come from the local Host. If activity is unknown, close the session in its native client before importing to avoid concurrent writes.",
20845
+ sessionImportAvailabilityNote: "Available Harnesses come from the selected Host. If activity is unknown, close the session in its native client before importing to avoid concurrent writes.",
20717
20846
  sessionImportRefresh: "Refresh",
20718
- sessionImportRefreshing: "Loading local sessions...",
20719
- sessionImportUnavailable: "Session import is unavailable for this local Harness or its current protocol. Update the Host/plugin or choose another Harness.",
20720
- sessionImportEmpty: "No local sessions are available to import.",
20847
+ sessionImportRefreshing: "Loading sessions from the selected Host...",
20848
+ sessionImportUnavailable: "Session import is unavailable for this Harness or the selected Host protocol. Update the Host/plugin or choose another Harness.",
20849
+ sessionImportEmpty: "No sessions are available to import on the selected Host.",
20721
20850
  sessionImportSearch: "Search",
20722
20851
  sessionImportSearchPlaceholder: "Search titles, session IDs or project paths",
20723
20852
  sessionImportNoMatches: "No sessions match your search.",
@@ -20725,7 +20854,7 @@ th[aria-sort] .console-usage-sort__mark {
20725
20854
  sessionImportPrevious: "Previous",
20726
20855
  sessionImportNext: "Next",
20727
20856
  sessionImportPageSummary: "Page {page} of {pages} \xB7 {total} sessions",
20728
- sessionImportLoadFailed: "Local sessions could not be loaded. Check directory access or duplicate session IDs, then retry.",
20857
+ sessionImportLoadFailed: "Sessions could not be loaded from the selected Host. Check directory access or duplicate session IDs, then retry.",
20729
20858
  sessionImportFailed: "The session could not be imported.",
20730
20859
  sessionImportUntitled: "Untitled session",
20731
20860
  sessionImportUpdatedAt: "Updated",
@@ -20830,10 +20959,17 @@ th[aria-sort] .console-usage-sort__mark {
20830
20959
  connectionInstallRunning: "Installing on the selected Host. You can leave this page; login or setup may still be required afterward.",
20831
20960
  harnessVersion: harnessVersionEnglish,
20832
20961
  harnessVersionTitle: "Harness CLI version",
20962
+ harnessVersionApplicationTitle: "Application version",
20833
20963
  harnessVersionCurrent: "Current version",
20834
20964
  harnessVersionLatest: "Latest version",
20835
20965
  harnessVersionChecking: "Checking versions\u2026",
20966
+ harnessVersionAvailable: "Update available",
20836
20967
  harnessVersionUpdate: "Update",
20968
+ harnessVersionDownloadUpdate: "Download update",
20969
+ harnessUpdateAll: "Update all",
20970
+ harnessUpdateAllProgress: "Updating",
20971
+ harnessUpdateAllDescription: "Update all eligible Harnesses on the selected Host.",
20972
+ harnessUpdateAllResult: "Updated {succeeded}; failed {failed}.",
20837
20973
  harnessVersionUpdating: "Updating\u2026",
20838
20974
  harnessVersionUpToDate: "Up to date",
20839
20975
  harnessVersionUpdated: "Update verified. New sessions will use the new version.",
@@ -20985,12 +21121,13 @@ th[aria-sort] .console-usage-sort__mark {
20985
21121
  notAvailable: "\u6682\u4E0D\u53EF\u7528",
20986
21122
  runtimeCapabilityNotInstalled: "\u8FD0\u884C\u65F6\u5C1A\u672A\u5B89\u88C5\u8BE5\u9879\u80FD\u529B\uFF0C\u56E0\u6B64\u6682\u4E0D\u53EF\u7528\u3002",
20987
21123
  sessionImportHarness: "Harness",
21124
+ sessionImportSelectHost: "\u8BF7\u9009\u62E9\u8981\u5BFC\u5165\u4F1A\u8BDD\u7684 Host",
20988
21125
  sessionImportDescription: "\u4F1A\u8BDD\u5C06\u4FDD\u7559\u539F\u59CB\u9879\u76EE\u8DEF\u5F84\uFF1B\u82E5\u8BE5\u6587\u4EF6\u5939\u5C1A\u672A\u51FA\u73B0\u5728 Codex \u4FA7\u680F\uFF0C\u8BF7\u5148\u5C06\u5176\u6DFB\u52A0\u4E3A\u9879\u76EE\u3002\u539F\u59CB\u5386\u53F2\u4ECD\u7531 Harness \u7BA1\u7406\u3002",
20989
- sessionImportAvailabilityNote: "\u53EF\u9009 Harness \u6765\u81EA\u672C\u5730 Host\u3002\u8FD0\u884C\u72B6\u6001\u672A\u77E5\u65F6\uFF0C\u8BF7\u5148\u5728\u539F\u751F\u5BA2\u6237\u7AEF\u5173\u95ED\u8BE5\u4F1A\u8BDD\u518D\u5BFC\u5165\uFF0C\u907F\u514D\u540C\u65F6\u5199\u5165\u3002",
21126
+ sessionImportAvailabilityNote: "\u53EF\u9009 Harness \u6765\u81EA\u5F53\u524D Host\u3002\u8FD0\u884C\u72B6\u6001\u672A\u77E5\u65F6\uFF0C\u8BF7\u5148\u5728\u539F\u751F\u5BA2\u6237\u7AEF\u5173\u95ED\u8BE5\u4F1A\u8BDD\u518D\u5BFC\u5165\uFF0C\u907F\u514D\u540C\u65F6\u5199\u5165\u3002",
20990
21127
  sessionImportRefresh: "\u5237\u65B0",
20991
- sessionImportRefreshing: "\u6B63\u5728\u8BFB\u53D6\u672C\u5730\u4F1A\u8BDD\u2026\u2026",
20992
- sessionImportUnavailable: "\u672C\u5730 Harness \u6216\u5F53\u524D\u534F\u8BAE\u6682\u4E0D\u652F\u6301\u4F1A\u8BDD\u5BFC\u5165\uFF0C\u8BF7\u66F4\u65B0 Host/\u63D2\u4EF6\u6216\u9009\u62E9\u5176\u4ED6 Harness\u3002",
20993
- sessionImportEmpty: "\u6CA1\u6709\u53EF\u5BFC\u5165\u7684\u672C\u5730\u4F1A\u8BDD\u3002",
21128
+ sessionImportRefreshing: "\u6B63\u5728\u8BFB\u53D6\u5F53\u524D Host \u4E0A\u7684\u4F1A\u8BDD\u2026\u2026",
21129
+ sessionImportUnavailable: "\u8BE5 Harness \u6216\u5F53\u524D Host \u534F\u8BAE\u6682\u4E0D\u652F\u6301\u4F1A\u8BDD\u5BFC\u5165\uFF0C\u8BF7\u66F4\u65B0 Host/\u63D2\u4EF6\u6216\u9009\u62E9\u5176\u4ED6 Harness\u3002",
21130
+ sessionImportEmpty: "\u5F53\u524D Host \u4E0A\u6CA1\u6709\u53EF\u5BFC\u5165\u7684\u4F1A\u8BDD\u3002",
20994
21131
  sessionImportSearch: "\u641C\u7D22",
20995
21132
  sessionImportSearchPlaceholder: "\u641C\u7D22\u6807\u9898\u3001\u4F1A\u8BDD ID \u6216\u9879\u76EE\u8DEF\u5F84",
20996
21133
  sessionImportNoMatches: "\u6CA1\u6709\u5339\u914D\u7684\u4F1A\u8BDD\u3002",
@@ -20998,7 +21135,7 @@ th[aria-sort] .console-usage-sort__mark {
20998
21135
  sessionImportPrevious: "\u4E0A\u4E00\u9875",
20999
21136
  sessionImportNext: "\u4E0B\u4E00\u9875",
21000
21137
  sessionImportPageSummary: "\u7B2C {page} / {pages} \u9875 \xB7 \u5171 {total} \u6761",
21001
- sessionImportLoadFailed: "\u65E0\u6CD5\u8BFB\u53D6\u672C\u5730\u4F1A\u8BDD\uFF0C\u8BF7\u68C0\u67E5\u76EE\u5F55\u8BBF\u95EE\u6743\u9650\u6216\u91CD\u590D\u7684\u4F1A\u8BDD ID \u540E\u91CD\u8BD5\u3002",
21138
+ sessionImportLoadFailed: "\u65E0\u6CD5\u8BFB\u53D6\u5F53\u524D Host \u4E0A\u7684\u4F1A\u8BDD\uFF0C\u8BF7\u68C0\u67E5\u76EE\u5F55\u8BBF\u95EE\u6743\u9650\u6216\u91CD\u590D\u7684\u4F1A\u8BDD ID \u540E\u91CD\u8BD5\u3002",
21002
21139
  sessionImportFailed: "\u65E0\u6CD5\u5BFC\u5165\u8BE5\u4F1A\u8BDD\u3002",
21003
21140
  sessionImportUntitled: "\u672A\u547D\u540D\u4F1A\u8BDD",
21004
21141
  sessionImportUpdatedAt: "\u66F4\u65B0\u65F6\u95F4",
@@ -21103,10 +21240,17 @@ th[aria-sort] .console-usage-sort__mark {
21103
21240
  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",
21104
21241
  harnessVersion: harnessVersionChinese,
21105
21242
  harnessVersionTitle: "Harness CLI \u7248\u672C",
21243
+ harnessVersionApplicationTitle: "\u5E94\u7528\u7248\u672C",
21106
21244
  harnessVersionCurrent: "\u5F53\u524D\u7248\u672C",
21107
21245
  harnessVersionLatest: "\u6700\u65B0\u7248\u672C",
21108
21246
  harnessVersionChecking: "\u6B63\u5728\u68C0\u67E5\u7248\u672C\u2026",
21247
+ harnessVersionAvailable: "\u6709\u66F4\u65B0",
21109
21248
  harnessVersionUpdate: "\u66F4\u65B0",
21249
+ harnessVersionDownloadUpdate: "\u4E0B\u8F7D\u66F4\u65B0",
21250
+ harnessUpdateAll: "\u5168\u90E8\u66F4\u65B0",
21251
+ harnessUpdateAllProgress: "\u66F4\u65B0\u4E2D",
21252
+ harnessUpdateAllDescription: "\u66F4\u65B0\u5F53\u524D Host \u4E0A\u652F\u6301\u81EA\u52A8\u66F4\u65B0\u4E14\u6709\u65B0\u7248\u672C\u7684 Harness\u3002",
21253
+ harnessUpdateAllResult: "\u5DF2\u66F4\u65B0 {succeeded} \u9879\uFF0C{failed} \u9879\u5931\u8D25\u3002",
21110
21254
  harnessVersionUpdating: "\u6B63\u5728\u66F4\u65B0\u2026",
21111
21255
  harnessVersionUpToDate: "\u5DF2\u662F\u6700\u65B0",
21112
21256
  harnessVersionUpdated: "\u5DF2\u786E\u8BA4\u66F4\u65B0\u6210\u529F\uFF0C\u65B0\u4F1A\u8BDD\u5C06\u4F7F\u7528\u65B0\u7248\u672C\u3002",
@@ -21225,6 +21369,34 @@ th[aria-sort] .console-usage-sort__mark {
21225
21369
  return names.size === 1 ? names.values().next().value ?? model2 : model2;
21226
21370
  }
21227
21371
 
21372
+ // src/console/usage/usage-hosts.ts
21373
+ var LOCAL_USAGE_HOST_ID = "local";
21374
+ function usageHostOptions(hosts, localLabel, selected) {
21375
+ const options = [{ key: LOCAL_USAGE_HOST_ID, label: localLabel }];
21376
+ const seen = /* @__PURE__ */ new Set([LOCAL_USAGE_HOST_ID]);
21377
+ const add = (hostId2, label) => {
21378
+ if (hostId2.length === 0 || seen.has(hostId2)) return;
21379
+ seen.add(hostId2);
21380
+ options.push({ key: hostId2, label: label !== void 0 && label.length > 0 ? label : hostId2 });
21381
+ };
21382
+ for (const host of hosts ?? []) add(host.hostId, host.label);
21383
+ if (!seen.has(selected)) add(selected, void 0);
21384
+ return options;
21385
+ }
21386
+ function createLatestKeyGuard() {
21387
+ let generation = 0;
21388
+ let key;
21389
+ return {
21390
+ begin(next) {
21391
+ generation += 1;
21392
+ key = next;
21393
+ const mine = generation;
21394
+ const selected = next;
21395
+ return () => generation === mine && Object.is(key, selected);
21396
+ }
21397
+ };
21398
+ }
21399
+
21228
21400
  // src/renderer-usage-control.ts
21229
21401
  var ENGLISH_USAGE_MESSAGES = Object.freeze({
21230
21402
  usage: "Usage",
@@ -21543,7 +21715,7 @@ th[aria-sort] .console-usage-sort__mark {
21543
21715
  var DECIMAL = /^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/iu;
21544
21716
  function modelPriceDraft(key, price) {
21545
21717
  const prices = Object.fromEntries(
21546
- MODEL_PRICE_FIELDS.map((field) => [field, price?.[field]?.toString() ?? ""])
21718
+ MODEL_PRICE_FIELDS.map((field2) => [field2, price?.[field2]?.toString() ?? ""])
21547
21719
  );
21548
21720
  return { model: key, prices };
21549
21721
  }
@@ -21554,18 +21726,18 @@ th[aria-sort] .console-usage-sort__mark {
21554
21726
  return { error: { kind: "duplicate", key } };
21555
21727
  }
21556
21728
  const price = {};
21557
- for (const field of MODEL_PRICE_FIELDS) {
21558
- const text = draft.prices[field].trim();
21729
+ for (const field2 of MODEL_PRICE_FIELDS) {
21730
+ const text = draft.prices[field2].trim();
21559
21731
  if (text === "") {
21560
- if (field === "input" || field === "output")
21561
- return { error: { kind: "priceRequired", field } };
21732
+ if (field2 === "input" || field2 === "output")
21733
+ return { error: { kind: "priceRequired", field: field2 } };
21562
21734
  continue;
21563
21735
  }
21564
21736
  const value = Number(text);
21565
21737
  if (!DECIMAL.test(text) || !Number.isFinite(value)) {
21566
- return { error: { kind: "priceInvalid", field } };
21738
+ return { error: { kind: "priceInvalid", field: field2 } };
21567
21739
  }
21568
- price[field] = value;
21740
+ price[field2] = value;
21569
21741
  }
21570
21742
  return {
21571
21743
  change: {
@@ -21585,7 +21757,7 @@ th[aria-sort] .console-usage-sort__mark {
21585
21757
  }
21586
21758
  function formatPrice(messages, price) {
21587
21759
  return MODEL_PRICE_FIELDS.map(
21588
- (field) => `${messages.fields[field]} ${price[field] ?? messages.unset}`
21760
+ (field2) => `${messages.fields[field2]} ${price[field2] ?? messages.unset}`
21589
21761
  ).join(" \xB7 ");
21590
21762
  }
21591
21763
  function draftErrorText(messages, error51) {
@@ -21653,18 +21825,18 @@ th[aria-sort] .console-usage-sort__mark {
21653
21825
  function renderEditor(modelId, view, isCurrent) {
21654
21826
  const existing = view.entries.find((entry) => entry.key === modelId);
21655
21827
  const draft = modelPriceDraft(modelId, existing?.price ?? null);
21656
- const field = (label, value, placeholder = "") => h(document2, "input", {
21828
+ const field2 = (label, value, placeholder = "") => h(document2, "input", {
21657
21829
  type: "text",
21658
21830
  className: "console-input",
21659
21831
  value,
21660
21832
  placeholder,
21661
21833
  "aria-label": label
21662
21834
  });
21663
- const model2 = field(messages.model, modelId);
21835
+ const model2 = field2(messages.model, modelId);
21664
21836
  model2.readOnly = true;
21665
21837
  const prices = Object.fromEntries(
21666
21838
  MODEL_PRICE_FIELDS.map((name) => {
21667
- const input = field(
21839
+ const input = field2(
21668
21840
  messages.fields[name],
21669
21841
  draft.prices[name],
21670
21842
  name === "input" || name === "output" ? messages.required : messages.notSet
@@ -22476,8 +22648,8 @@ th[aria-sort] .console-usage-sort__mark {
22476
22648
  for (const cell of options.cells) {
22477
22649
  const key = options.singleDay ? cell.hour : cell.weekday * 24 + cell.hour;
22478
22650
  const totals2 = byHour.get(key) ?? emptyTotals();
22479
- for (const field of Object.keys(totals2))
22480
- totals2[field] += cell.totals[field];
22651
+ for (const field2 of Object.keys(totals2))
22652
+ totals2[field2] += cell.totals[field2];
22481
22653
  byHour.set(key, totals2);
22482
22654
  }
22483
22655
  const max = Math.max(0, ...[...byHour.values()].map((totals2) => measured(totals2, measure)));
@@ -22583,6 +22755,7 @@ th[aria-sort] .console-usage-sort__mark {
22583
22755
  const range = text("range");
22584
22756
  const measure = text("measure");
22585
22757
  const granularity = text("granularity");
22758
+ const hostId2 = text("hostId");
22586
22759
  const harness2 = text("harness");
22587
22760
  const model2 = text("model");
22588
22761
  const project2 = text("project");
@@ -22591,6 +22764,7 @@ th[aria-sort] .console-usage-sort__mark {
22591
22764
  ...range && USAGE_STATISTICS_RANGES.includes(range) ? { range } : {},
22592
22765
  ...measure && MEASURES.includes(measure) ? { measure } : {},
22593
22766
  ...granularity === "day" || granularity === "week" || granularity === "month" ? { granularity } : {},
22767
+ ...hostId2 !== void 0 && hostId2.length > 0 ? { hostId: hostId2 } : {},
22594
22768
  ...harness2 !== void 0 ? { harness: harness2 } : {},
22595
22769
  ...model2 !== void 0 ? { model: model2 } : {},
22596
22770
  ...project2 !== void 0 ? { project: project2 } : {},
@@ -22629,7 +22803,7 @@ th[aria-sort] .console-usage-sort__mark {
22629
22803
  day.setDate(day.getDate() - (day.getDay() + 6) % 7);
22630
22804
  return isoDate(day);
22631
22805
  }
22632
- function createUsageStatisticsPage(consoleMessages2, request, pageLocale) {
22806
+ function createUsageStatisticsPage(consoleMessages2, request, pageLocale, usageHosts) {
22633
22807
  const messages = consoleMessages2.usageStatistics;
22634
22808
  return Object.freeze({
22635
22809
  id: "usage-statistics",
@@ -22638,10 +22812,12 @@ th[aria-sort] .console-usage-sort__mark {
22638
22812
  mount(context) {
22639
22813
  const document2 = context.content.ownerDocument;
22640
22814
  const locale = pageLocale ?? (document2.documentElement.lang || "en");
22815
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
22641
22816
  const count3 = (value) => count2(value, locale);
22642
22817
  const tokenCount2 = (totals2, value) => tokenCount(totals2, value, locale);
22643
22818
  const saved = readView();
22644
22819
  const view = {
22820
+ hostId: saved.hostId ?? LOCAL_USAGE_HOST_ID,
22645
22821
  range: saved.range ?? "30d",
22646
22822
  measure: saved.measure ?? "cost",
22647
22823
  granularity: saved.granularity ?? null,
@@ -22672,7 +22848,7 @@ th[aria-sort] .console-usage-sort__mark {
22672
22848
  let projectLabel = /* @__PURE__ */ new Map();
22673
22849
  const projectName = (key) => key === UNKNOWN ? messages.unknownProject : projectLabel.get(key) ?? baseName(key);
22674
22850
  const measureLabel = (measure) => measure === "cost" ? messages.cost : messages.tokens;
22675
- const dateFormat = (options) => new Intl.DateTimeFormat(locale, options);
22851
+ const dateFormat = (options) => new Intl.DateTimeFormat(locale, { ...options, timeZone });
22676
22852
  const shortDate = dateFormat({ month: "short", day: "numeric" });
22677
22853
  const longDate = dateFormat({
22678
22854
  year: "numeric",
@@ -22691,10 +22867,51 @@ th[aria-sort] .console-usage-sort__mark {
22691
22867
  const prices = createModelPricesDialog(
22692
22868
  document2,
22693
22869
  consoleMessages2,
22694
- request,
22870
+ (method, params2) => request(method, params2, view.hostId),
22695
22871
  context.signal,
22696
22872
  load
22697
22873
  );
22874
+ let offeredHosts = usageHosts ? [] : void 0;
22875
+ const hostCombo = new ComboBox(document2, {
22876
+ label: messages.host,
22877
+ searchLabel: messages.search,
22878
+ emptyLabel: messages.noMatches,
22879
+ signal: context.signal,
22880
+ searchable: false,
22881
+ display: (option) => `${messages.host} \xB7 ${option.label}`,
22882
+ onChange: (key) => {
22883
+ if (key === view.hostId) return;
22884
+ view.hostId = key;
22885
+ view.harness = "";
22886
+ view.model = "";
22887
+ view.project = "";
22888
+ date5 = null;
22889
+ unpricedOnly = false;
22890
+ result = null;
22891
+ error51 = null;
22892
+ loadedAt = 0;
22893
+ names.clear();
22894
+ save();
22895
+ hostOptions();
22896
+ render();
22897
+ void refreshHarnessNames();
22898
+ load();
22899
+ }
22900
+ });
22901
+ const hostOptions = () => {
22902
+ const options = usageHostOptions(offeredHosts, messages.localHost, view.hostId);
22903
+ hostCombo.element.hidden = options.length < 2;
22904
+ hostCombo.update(options, view.hostId === LOCAL_USAGE_HOST_ID ? "" : view.hostId);
22905
+ };
22906
+ const offerHosts = () => {
22907
+ if (!usageHosts) return;
22908
+ void usageHosts().then((hosts) => {
22909
+ if (context.signal.aborted) return;
22910
+ offeredHosts = hosts;
22911
+ hostOptions();
22912
+ }).catch(() => void 0);
22913
+ };
22914
+ hostOptions();
22698
22915
  const updated = h(document2, "span", { className: "console-muted console-usage-updated" });
22699
22916
  const refresh = h(
22700
22917
  document2,
@@ -22707,7 +22924,10 @@ th[aria-sort] .console-usage-sort__mark {
22707
22924
  },
22708
22925
  createRendererSettingsIcon("refresh", 16)
22709
22926
  );
22710
- refresh.addEventListener("click", () => load());
22927
+ refresh.addEventListener("click", () => {
22928
+ offerHosts();
22929
+ load();
22930
+ });
22711
22931
  const auto = new ComboBox(document2, {
22712
22932
  label: messages.autoRefresh,
22713
22933
  searchLabel: messages.search,
@@ -22746,6 +22966,7 @@ th[aria-sort] .console-usage-sort__mark {
22746
22966
  document2,
22747
22967
  "div",
22748
22968
  { className: "console-usage-toolbar" },
22969
+ hostCombo.element,
22749
22970
  updated,
22750
22971
  refresh,
22751
22972
  auto.element,
@@ -22791,8 +23012,8 @@ th[aria-sort] .console-usage-sort__mark {
22791
23012
  save();
22792
23013
  load();
22793
23014
  }
22794
- function toggleFilter(field, key) {
22795
- view[field] = view[field] === key ? "" : key;
23015
+ function toggleFilter(field2, key) {
23016
+ view[field2] = view[field2] === key ? "" : key;
22796
23017
  changed();
22797
23018
  }
22798
23019
  function selectDate(next) {
@@ -23208,8 +23429,8 @@ ${messages.creditsCoverage}: ${value.reportedRequests} / ${row.requests}` : void
23208
23429
  }
23209
23430
  ];
23210
23431
  }
23211
- function rowFilter(field, key, label, detail) {
23212
- const selected = view[field] === key;
23432
+ function rowFilter(field2, key, label, detail) {
23433
+ const selected = view[field2] === key;
23213
23434
  const button2 = focusKey(
23214
23435
  h(
23215
23436
  document2,
@@ -23223,9 +23444,9 @@ ${detail}` : ""}`
23223
23444
  },
23224
23445
  label
23225
23446
  ),
23226
- `${field}:row:${key}`
23447
+ `${field2}:row:${key}`
23227
23448
  );
23228
- button2.addEventListener("click", () => toggleFilter(field, key));
23449
+ button2.addEventListener("click", () => toggleFilter(field2, key));
23229
23450
  return button2;
23230
23451
  }
23231
23452
  function panel(title, content, extra = null, wide = false) {
@@ -23384,7 +23605,6 @@ ${detail}` : ""}`
23384
23605
  }
23385
23606
  function hourlyPanel(data) {
23386
23607
  const singleDay = data.filters.date !== null || data.range === "today";
23387
- const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
23388
23608
  return panel(
23389
23609
  singleDay ? messages.hourlyDay : messages.hourly,
23390
23610
  hourlyChart(document2, {
@@ -23399,7 +23619,7 @@ ${detail}` : ""}`
23399
23619
  weekStartsOnMonday: !locale.startsWith("en"),
23400
23620
  labels: { cost: messages.cost, tokens: messages.tokens }
23401
23621
  }),
23402
- h(document2, "span", { className: "console-muted console-usage-zone" }, zone)
23622
+ h(document2, "span", { className: "console-muted console-usage-zone" }, timeZone)
23403
23623
  );
23404
23624
  }
23405
23625
  function sessionsPanel(title, rows, tableKey) {
@@ -23546,6 +23766,7 @@ ${detail}` : ""}`
23546
23766
  const project2 = filterValue(view.project);
23547
23767
  return {
23548
23768
  range: view.range,
23769
+ timeZone,
23549
23770
  ...view.harness ? { harness: view.harness } : {},
23550
23771
  ...model2 !== void 0 ? { model: model2 } : {},
23551
23772
  ...project2 !== void 0 ? { project: project2 } : {},
@@ -23558,10 +23779,26 @@ ${detail}` : ""}`
23558
23779
  if (result && !result.reading.complete) poll = window.setTimeout(load, POLL_MS);
23559
23780
  else if (view.autoRefreshMs > 0) poll = window.setTimeout(load, view.autoRefreshMs);
23560
23781
  }
23782
+ const harnessNamesRead = createLatestKeyGuard();
23783
+ async function refreshHarnessNames() {
23784
+ const hostId2 = view.hostId;
23785
+ const isCurrent = harnessNamesRead.begin(hostId2);
23786
+ names.clear();
23787
+ try {
23788
+ const value = await request(HARNESS_PLUGIN_LIST_METHOD2, {}, hostId2);
23789
+ if (!isCurrent()) return;
23790
+ for (const plugin of harnessPluginListResultSchema.parse(value).plugins) {
23791
+ names.set(plugin.id, plugin.name);
23792
+ }
23793
+ } catch {
23794
+ if (!isCurrent()) return;
23795
+ }
23796
+ if (!context.signal.aborted) render();
23797
+ }
23561
23798
  function load() {
23562
23799
  window.clearTimeout(poll);
23563
23800
  const requested = params();
23564
- void context.runLatest(() => request(USAGE_STATISTICS_METHOD, requested), {
23801
+ void context.runLatest(() => request(USAGE_STATISTICS_METHOD, requested, view.hostId), {
23565
23802
  success(value) {
23566
23803
  const parsed = usageStatisticsResultSchema.safeParse(value);
23567
23804
  if (parsed.success) {
@@ -23586,12 +23823,8 @@ ${detail}` : ""}`
23586
23823
  }
23587
23824
  context.content.replaceChildren(header, prices.element, filters, notices, body);
23588
23825
  render();
23589
- void request(HARNESS_PLUGIN_LIST_METHOD2, {}).then((value) => {
23590
- for (const plugin of harnessPluginListResultSchema.parse(value).plugins) {
23591
- names.set(plugin.id, plugin.name);
23592
- }
23593
- if (!context.signal.aborted) render();
23594
- }).catch(() => void 0);
23826
+ offerHosts();
23827
+ void refreshHarnessNames();
23595
23828
  load();
23596
23829
  return () => {
23597
23830
  window.clearTimeout(poll);
@@ -24052,8 +24285,8 @@ ${detail}` : ""}`
24052
24285
  return input;
24053
24286
  }
24054
24287
  function createNumberField(document2, input) {
24055
- const field = document2.createElement("div");
24056
- field.className = "flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-settings-border bg-settings-surface px-2.5 transition-colors focus-within:border-settings-focus data-[invalid=true]:border-settings-danger";
24288
+ const field2 = document2.createElement("div");
24289
+ field2.className = "flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-settings-border bg-settings-surface px-2.5 transition-colors focus-within:border-settings-focus data-[invalid=true]:border-settings-danger";
24057
24290
  const control = document2.createElement("input");
24058
24291
  control.type = "number";
24059
24292
  control.id = input.id;
@@ -24066,8 +24299,8 @@ ${detail}` : ""}`
24066
24299
  unit.className = "text-xs text-settings-muted";
24067
24300
  unit.setAttribute("aria-hidden", "true");
24068
24301
  unit.textContent = input.unit;
24069
- field.append(control, unit);
24070
- return { field, control };
24302
+ field2.append(control, unit);
24303
+ return { field: field2, control };
24071
24304
  }
24072
24305
 
24073
24306
  // src/settings/remote-connections-dialogs.ts
@@ -24151,7 +24384,7 @@ ${detail}` : ""}`
24151
24384
  );
24152
24385
  if (!dialog) return;
24153
24386
  const document2 = options.container.ownerDocument;
24154
- const field = (label, value, placeholder = "", hint = "") => {
24387
+ const field2 = (label, value, placeholder = "", hint = "") => {
24155
24388
  const wrapper = remoteElement(document2, "div", "settings-credential-dialog__field");
24156
24389
  const input = remoteElement(document2, "input");
24157
24390
  input.id = preferenceId("remote-field");
@@ -24171,13 +24404,13 @@ ${detail}` : ""}`
24171
24404
  dialog.body.append(wrapper);
24172
24405
  return input;
24173
24406
  };
24174
- const name = field(t("\u540D\u79F0", "Name"), previous?.displayName ?? "");
24407
+ const name = field2(t("\u540D\u79F0", "Name"), previous?.displayName ?? "");
24175
24408
  name.required = true;
24176
24409
  const addressHint = t(
24177
24410
  "user@hostname\uFF0C\u6216 ~/.ssh/config \u4E2D\u7684\u4E3B\u673A\u522B\u540D",
24178
24411
  "user@hostname, or a host alias from ~/.ssh/config"
24179
24412
  );
24180
- const hostname3 = field(
24413
+ const hostname3 = field2(
24181
24414
  t("SSH \u5730\u5740", "SSH address"),
24182
24415
  previous?.sshHost ?? "",
24183
24416
  "user@hostname",
@@ -24186,7 +24419,7 @@ ${detail}` : ""}`
24186
24419
  hostname3.required = true;
24187
24420
  hostname3.pattern = "[^\\s\\-]\\S*";
24188
24421
  hostname3.title = addressHint;
24189
- const port = field(
24422
+ const port = field2(
24190
24423
  t("\u7AEF\u53E3\uFF08\u53EF\u9009\uFF09", "Port (optional)"),
24191
24424
  previous?.sshPort?.toString() ?? "",
24192
24425
  "22"
@@ -24195,7 +24428,7 @@ ${detail}` : ""}`
24195
24428
  port.min = "1";
24196
24429
  port.max = "65535";
24197
24430
  port.step = "1";
24198
- const identity = field(
24431
+ const identity = field2(
24199
24432
  t("\u79C1\u94A5\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09", "Identity file (optional)"),
24200
24433
  previous?.identity ?? "",
24201
24434
  "~/.ssh/id_ed25519"
@@ -25068,7 +25301,7 @@ ${detail}` : ""}`
25068
25301
  }
25069
25302
 
25070
25303
  // src/settings/harness-version-panel.ts
25071
- function createHarnessVersionPanel(document2, messages, signal, agent, client) {
25304
+ function createHarnessVersionPanel(document2, messages, signal, agent, client, onChange = () => void 0) {
25072
25305
  const panel = document2.createElement("section");
25073
25306
  panel.className = "settings-harness-version";
25074
25307
  panel.dataset.harnessVersion = agent;
@@ -25094,37 +25327,67 @@ ${detail}` : ""}`
25094
25327
  panel.append(heading, versions, status, actions, note);
25095
25328
  let state;
25096
25329
  let busy = false;
25330
+ let updating = false;
25097
25331
  let unsupported = false;
25332
+ let updatePromise = null;
25098
25333
  const render = () => {
25334
+ heading.textContent = state?.versionKind === "application" ? messages.harnessVersionApplicationTitle : messages.harnessVersionTitle;
25099
25335
  current.textContent = `${messages.harnessVersionCurrent}: ${state?.currentVersion ?? "\u2014"}`;
25100
25336
  latest.textContent = `${messages.harnessVersionLatest}: ${harnessVersionLabel(state, messages.harnessVersion)}`;
25101
25337
  update.disabled = busy || signal.aborted || !state?.canUpdate || !state.updateAvailable;
25338
+ const manualUrl = !state?.canUpdate && state?.updateAvailable ? state.manualUpdateUrl : void 0;
25339
+ actions.replaceChildren(update);
25340
+ update.hidden = !busy && !signal.aborted && Boolean(manualUrl);
25341
+ if (update.hidden && manualUrl) {
25342
+ const download2 = document2.createElement("a");
25343
+ download2.className = "settings-command-button settings-command-button--secondary";
25344
+ download2.dataset.harnessVersionAction = "download-update";
25345
+ download2.textContent = messages.harnessVersionDownloadUpdate;
25346
+ download2.href = manualUrl;
25347
+ download2.target = "_blank";
25348
+ download2.rel = "noopener noreferrer";
25349
+ actions.append(download2);
25350
+ }
25102
25351
  update.textContent = state?.canUpdate && state.latestVersion !== "Unknown" && !state.updateAvailable ? messages.harnessVersionUpToDate : messages.harnessVersionUpdate;
25103
25352
  const detail = harnessVersionNote(state, messages.harnessVersion);
25104
- note.textContent = detail ? `${detail} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
25353
+ note.textContent = detail ? !state?.canUpdate ? detail : `${detail} ${messages.harnessVersionNote}` : messages.harnessVersionNote;
25105
25354
  };
25106
25355
  const run = async (action) => {
25107
- if (busy || unsupported || signal.aborted) return;
25108
- if (action === "update" && (!state?.canUpdate || !state.updateAvailable)) return;
25356
+ if (busy || unsupported || signal.aborted) return false;
25357
+ if (action === "update" && (!state?.canUpdate || !state.updateAvailable)) return false;
25109
25358
  busy = true;
25359
+ updating = action === "update";
25360
+ onChange();
25110
25361
  status.textContent = action === "check" ? messages.harnessVersionChecking : messages.harnessVersionUpdating;
25111
25362
  render();
25112
25363
  try {
25113
25364
  const result = await client.run(action);
25114
- if (signal.aborted) return;
25365
+ if (signal.aborted) return false;
25115
25366
  state = result;
25116
25367
  status.textContent = !result.canUpdate ? messages.harnessVersionManual : action === "update" ? result.updateAvailable ? messages.harnessVersionFailed : messages.harnessVersionUpdated : "";
25368
+ return result.canUpdate && !result.updateAvailable;
25117
25369
  } catch (error51) {
25118
- if (signal.aborted) return;
25370
+ if (signal.aborted) return false;
25119
25371
  unsupported = typeof error51 === "object" && error51 !== null && "code" in error51 && (error51.code === -32601 || error51.code === -32078);
25120
25372
  status.textContent = unsupported ? messages.harnessVersionUnsupported : messages.harnessVersionFailed;
25121
25373
  if (state) state = { ...state, canUpdate: false };
25374
+ return false;
25122
25375
  } finally {
25123
25376
  busy = false;
25124
- if (!signal.aborted) render();
25377
+ updating = false;
25378
+ if (!signal.aborted) {
25379
+ render();
25380
+ onChange();
25381
+ }
25125
25382
  }
25126
25383
  };
25127
- update.addEventListener("click", () => void run("update"));
25384
+ const updateVersion = () => {
25385
+ updatePromise ??= run("update").finally(() => {
25386
+ updatePromise = null;
25387
+ });
25388
+ return updatePromise;
25389
+ };
25390
+ update.addEventListener("click", () => void updateVersion());
25128
25391
  const owner = document2.defaultView;
25129
25392
  let checkFrame = null;
25130
25393
  let checkTimer = null;
@@ -25151,7 +25414,95 @@ ${detail}` : ""}`
25151
25414
  } else {
25152
25415
  void run("check");
25153
25416
  }
25154
- return panel;
25417
+ return {
25418
+ element: panel,
25419
+ get state() {
25420
+ return state;
25421
+ },
25422
+ get updating() {
25423
+ return updating;
25424
+ },
25425
+ update: updateVersion
25426
+ };
25427
+ }
25428
+
25429
+ // src/settings/connection-versions.ts
25430
+ function createConnectionVersions(document2, messages, signal, diagnostics, onChange) {
25431
+ const panels = /* @__PURE__ */ new Map();
25432
+ const installation = diagnostics?.installation?.bind(diagnostics);
25433
+ return {
25434
+ get(hostId2, agent) {
25435
+ if (!installation || signal.aborted) return void 0;
25436
+ const key = JSON.stringify([hostId2, agent]);
25437
+ let panel = panels.get(key);
25438
+ if (!panel) {
25439
+ panel = createHarnessVersionPanel(
25440
+ document2,
25441
+ messages,
25442
+ signal,
25443
+ agent,
25444
+ {
25445
+ run: (action) => installation(hostId2, agent, action)
25446
+ },
25447
+ onChange
25448
+ );
25449
+ panels.set(key, panel);
25450
+ }
25451
+ return panel;
25452
+ }
25453
+ };
25454
+ }
25455
+
25456
+ // src/settings/connection-updates.ts
25457
+ function createConnectionUpdates(document2, messages, signal, onChange) {
25458
+ const batches = /* @__PURE__ */ new Map();
25459
+ const eligible = (panels) => panels.filter((panel) => panel.state?.updateAvailable && panel.state.canUpdate);
25460
+ const start = async (hostId2, panels) => {
25461
+ if (signal.aborted || batches.get(hostId2)?.running) return;
25462
+ const targets = eligible(panels);
25463
+ if (!targets.length) return;
25464
+ const batch = { running: true, total: targets.length, succeeded: 0, failed: 0 };
25465
+ batches.set(hostId2, batch);
25466
+ onChange();
25467
+ await Promise.all(
25468
+ targets.map(async (panel) => {
25469
+ try {
25470
+ if (await panel.update()) batch.succeeded++;
25471
+ else batch.failed++;
25472
+ } catch {
25473
+ batch.failed++;
25474
+ }
25475
+ if (!signal.aborted) onChange();
25476
+ })
25477
+ );
25478
+ batch.running = false;
25479
+ if (!signal.aborted) onChange();
25480
+ };
25481
+ return {
25482
+ createControls(hostId2, panels) {
25483
+ const batch = batches.get(hostId2);
25484
+ const count3 = eligible(panels).length;
25485
+ const button2 = document2.createElement("button");
25486
+ button2.type = "button";
25487
+ button2.className = "settings-command-button settings-command-button--secondary settings-connection-update-all";
25488
+ button2.dataset.connectionAction = "update-all";
25489
+ button2.disabled = signal.aborted || Boolean(batch?.running) || !count3;
25490
+ button2.title = messages.harnessUpdateAllDescription;
25491
+ const label = batch?.running ? `${messages.harnessUpdateAllProgress} ${batch.succeeded + batch.failed}/${batch.total}` : count3 ? `${messages.harnessUpdateAll} (${count3})` : messages.harnessUpdateAll;
25492
+ button2.append(createRendererSettingsIcon("download", 15), label);
25493
+ button2.addEventListener("click", () => {
25494
+ if (!button2.disabled) void start(hostId2, panels);
25495
+ });
25496
+ const result = document2.createElement("p");
25497
+ result.className = "settings-connection-update-result";
25498
+ result.setAttribute("role", "status");
25499
+ result.hidden = !batch || batch.running;
25500
+ if (batch && !batch.running) {
25501
+ result.textContent = messages.harnessUpdateAllResult.replace("{succeeded}", String(batch.succeeded)).replace("{failed}", String(batch.failed));
25502
+ }
25503
+ return { button: button2, result };
25504
+ }
25505
+ };
25155
25506
  }
25156
25507
 
25157
25508
  // src/settings/connections-page.ts
@@ -25319,6 +25670,7 @@ ${error51.stderrTail}`] : []
25319
25670
  status.dataset.connectionTone = connectionStatusTone(item.availability, item.error !== null);
25320
25671
  status.setAttribute("role", "cell");
25321
25672
  status.textContent = item.error?.code === "authenticationRequired" ? messages.connectionLoginRequired : item.error?.code === "configurationRequired" ? messages.connectionConfigurationRequired : connectionStatusLabel(item.availability, messages, item.error !== null);
25673
+ const version2 = item.version;
25322
25674
  const action = document2.createElement("div");
25323
25675
  action.className = "settings-connection-row__action";
25324
25676
  action.setAttribute("role", "cell");
@@ -25348,6 +25700,31 @@ ${error51.stderrTail}`] : []
25348
25700
  });
25349
25701
  action.append(viewError);
25350
25702
  }
25703
+ if (version2?.state?.updateAvailable && !version2.updating) {
25704
+ const available = document2.createElement("span");
25705
+ available.className = "settings-connection-version-available";
25706
+ available.textContent = messages.harnessVersionAvailable;
25707
+ available.title = `${messages.harnessVersionCurrent}: ${version2.state.currentVersion ?? "\u2014"}
25708
+ ${messages.harnessVersionLatest}: ${harnessVersionLabel(version2.state, messages.harnessVersion)}`;
25709
+ action.append(available);
25710
+ }
25711
+ if (version2?.state?.updateAvailable && version2.state.canUpdate) {
25712
+ const update = document2.createElement("button");
25713
+ update.type = "button";
25714
+ update.className = "settings-connection-install-link";
25715
+ update.dataset.connectionAction = "update";
25716
+ update.disabled = version2.updating;
25717
+ update.title = messages.harnessVersionUpdate;
25718
+ update.setAttribute("aria-label", `${messages.harnessVersionUpdate}: ${item.name}`);
25719
+ update.append(createRendererSettingsIcon("download", 17));
25720
+ update.addEventListener("click", (event) => {
25721
+ event.stopPropagation();
25722
+ if (update.disabled) return;
25723
+ void version2.update();
25724
+ select();
25725
+ });
25726
+ action.append(update);
25727
+ }
25351
25728
  if (group) {
25352
25729
  const toggle = document2.createElement("button");
25353
25730
  toggle.type = "button";
@@ -25636,8 +26013,6 @@ ${error51.stderrTail}`] : []
25636
26013
  let selectedHostId = null;
25637
26014
  let selectedItemKey = null;
25638
26015
  let launchControls = null;
25639
- const versionPanels = /* @__PURE__ */ new Map();
25640
- const updatingVersions = /* @__PURE__ */ new Set();
25641
26016
  const hostNames = /* @__PURE__ */ new Map();
25642
26017
  let latestSnapshot = null;
25643
26018
  let disposeHostScroller = () => void 0;
@@ -25666,7 +26041,15 @@ ${error51.stderrTail}`] : []
25666
26041
  renderFrame = null;
25667
26042
  };
25668
26043
  const diagnostics = getDiagnostics();
26044
+ const versions = createConnectionVersions(
26045
+ document2,
26046
+ messages,
26047
+ context.signal,
26048
+ diagnostics,
26049
+ scheduleRender
26050
+ );
25669
26051
  const installs = diagnostics ? harnessInstallStore(diagnostics) : null;
26052
+ const updates = createConnectionUpdates(document2, messages, context.signal, scheduleRender);
25670
26053
  const runRefresh = () => {
25671
26054
  if (pending || !diagnostics) return;
25672
26055
  pending = true;
@@ -25802,10 +26185,13 @@ ${error51.stderrTail}`] : []
25802
26185
  const agent = item.agentSnapshot?.agent;
25803
26186
  if (!agent) return item;
25804
26187
  const state = installs?.get(selectedHost.hostId, agent);
26188
+ const availability = state?.status ?? item.availability;
26189
+ const version2 = item.agentSnapshot?.availability !== "notInstalled" && availability !== "installing" && availability !== "checking" ? versions.get(selectedHost.hostId, agent) : void 0;
25805
26190
  return {
25806
26191
  ...item,
25807
26192
  ...state ? { availability: state.status } : {},
25808
- ...updatingVersions.has(JSON.stringify([selectedHost.hostId, agent])) ? { availability: "updating" } : {},
26193
+ ...version2 ? { version: version2 } : {},
26194
+ ...version2?.updating ? { availability: "updating" } : {},
25809
26195
  ...state?.error ? { installError: state.error } : {},
25810
26196
  ...diagnostics?.installation && harnessHasInstallCommands(item.agentSnapshot?.plugin) ? {
25811
26197
  install: () => {
@@ -25842,30 +26228,7 @@ ${error51.stderrTail}`] : []
25842
26228
  runRefresh
25843
26229
  );
25844
26230
  launchControls = { hostId: selectedHost.hostId, itemKey: item.key, element };
25845
- const agent = item.agentSnapshot?.agent;
25846
- const installation = diagnostics?.installation?.bind(diagnostics);
25847
- if (agent && item.agentSnapshot?.availability !== "notInstalled" && item.availability !== "installing" && item.availability !== "checking" && installation) {
25848
- const key = JSON.stringify([selectedHost.hostId, agent]);
25849
- let panel = versionPanels.get(key);
25850
- if (!panel) {
25851
- const hostId2 = selectedHost.hostId;
25852
- panel = createHarnessVersionPanel(document2, messages, context.signal, agent, {
25853
- async run(action) {
25854
- if (action !== "update") return installation(hostId2, agent, action);
25855
- updatingVersions.add(key);
25856
- if (diagnostics) render(diagnostics.snapshot());
25857
- try {
25858
- return await installation(hostId2, agent, action);
25859
- } finally {
25860
- updatingVersions.delete(key);
25861
- if (!context.signal.aborted && diagnostics) render(diagnostics.snapshot());
25862
- }
25863
- }
25864
- });
25865
- versionPanels.set(key, panel);
25866
- }
25867
- inspector.append(panel);
25868
- }
26231
+ if (item.version) inspector.append(item.version.element);
25869
26232
  };
25870
26233
  const pinnedItem = items.find((item) => item.key === "renderer-adapter");
25871
26234
  if (pinnedItem) {
@@ -26021,7 +26384,14 @@ ${error51.stderrTail}`] : []
26021
26384
  }
26022
26385
  const selectedItem = items.find((item) => item.key === selectedItemKey) ?? items[0];
26023
26386
  if (selectedItem) selectItem(selectedItem);
26024
- list.append(hostStrip, tableHeader, rows);
26387
+ const updateControls = updates.createControls(
26388
+ selectedHost.hostId,
26389
+ items.flatMap((item) => item.version ? [item.version] : [])
26390
+ );
26391
+ const hostHeader = document2.createElement("div");
26392
+ hostHeader.className = "settings-connection-host-header";
26393
+ hostHeader.append(hostStrip, updateControls.button);
26394
+ list.append(hostHeader, updateControls.result, tableHeader, rows);
26025
26395
  layout.append(list, inspector);
26026
26396
  content.replaceChildren(layout);
26027
26397
  disposeHostScroller = configureHostScroller(
@@ -26303,6 +26673,50 @@ ${error51.stderrTail}`] : []
26303
26673
  };
26304
26674
  setRefreshLabel(false);
26305
26675
  header.append(heading, refresh);
26676
+ let selectedHostId;
26677
+ let availableHostIds = [];
26678
+ let displayedHostId = "";
26679
+ let hostsLocked = false;
26680
+ const hostButtons = [];
26681
+ const hostLabel = document2.createElement("div");
26682
+ hostLabel.className = "settings-session-import-host";
26683
+ const hostCaption = document2.createElement("span");
26684
+ hostCaption.textContent = "Host";
26685
+ const hostOptions = document2.createElement("div");
26686
+ hostOptions.className = "settings-connection-host-tabs";
26687
+ hostOptions.setAttribute("role", "group");
26688
+ hostOptions.setAttribute("aria-label", "Host");
26689
+ hostLabel.append(hostCaption, hostOptions);
26690
+ const lockHosts = (locked) => {
26691
+ hostsLocked = locked;
26692
+ for (const button2 of hostButtons) button2.disabled = locked;
26693
+ };
26694
+ const renderHosts = (client) => {
26695
+ const current = selectedHostId ?? (client ? client.hostId ?? "local" : "");
26696
+ availableHostIds = [
26697
+ .../* @__PURE__ */ new Set([
26698
+ ...getDiagnostics()?.snapshot().hosts.map(({ hostId: hostId2 }) => hostId2) ?? [],
26699
+ ...current ? [current] : []
26700
+ ])
26701
+ ];
26702
+ displayedHostId = current;
26703
+ const scrollLeft = hostOptions.scrollLeft;
26704
+ hostOptions.replaceChildren();
26705
+ hostButtons.length = 0;
26706
+ for (const hostId2 of availableHostIds) {
26707
+ const button2 = document2.createElement("button");
26708
+ button2.type = "button";
26709
+ button2.className = "settings-connection-host-tab";
26710
+ button2.dataset.sessionImportHost = hostId2;
26711
+ button2.textContent = connectionHostName(hostId2, messages);
26712
+ button2.setAttribute("aria-pressed", String(hostId2 === current));
26713
+ button2.disabled = hostsLocked;
26714
+ button2.addEventListener("click", () => selectHost(hostId2));
26715
+ hostButtons.push(button2);
26716
+ hostOptions.append(button2);
26717
+ }
26718
+ hostOptions.scrollLeft = scrollLeft;
26719
+ };
26306
26720
  const harness2 = document2.createElement("div");
26307
26721
  harness2.className = "settings-session-import-harness";
26308
26722
  const harnessLabel = document2.createElement("span");
@@ -26363,6 +26777,7 @@ ${error51.stderrTail}`] : []
26363
26777
  const listControls = createSessionImportListControls(document2, messages, () => load());
26364
26778
  context.content.append(
26365
26779
  header,
26780
+ hostLabel,
26366
26781
  harness2,
26367
26782
  description,
26368
26783
  availabilityNote,
@@ -26372,9 +26787,11 @@ ${error51.stderrTail}`] : []
26372
26787
  listControls.pagination
26373
26788
  );
26374
26789
  let candidates = [];
26790
+ let loadedClient = null;
26375
26791
  let importingId = null;
26376
26792
  let actions = [];
26377
26793
  const updateImportActions = () => {
26794
+ lockHosts(importingId !== null);
26378
26795
  listControls.setBusy(importingId !== null, importingId !== null);
26379
26796
  for (const button2 of sourceButtons) button2.disabled = importingId !== null;
26380
26797
  for (const { button: button2, candidate } of actions) {
@@ -26395,7 +26812,10 @@ ${error51.stderrTail}`] : []
26395
26812
  listControls.setTotal(0);
26396
26813
  candidates = [];
26397
26814
  actions = [];
26398
- const status = createStatus(document2, messages.sessionImportUnavailable);
26815
+ const status = createStatus(
26816
+ document2,
26817
+ !displayedHostId && availableHostIds.length > 1 ? messages.sessionImportSelectHost : messages.sessionImportUnavailable
26818
+ );
26399
26819
  content.replaceChildren(status);
26400
26820
  if (focus) {
26401
26821
  status.tabIndex = -1;
@@ -26420,7 +26840,7 @@ ${error51.stderrTail}`] : []
26420
26840
  status.focus();
26421
26841
  }
26422
26842
  };
26423
- const renderOpenRecovery = (candidate, threadId) => {
26843
+ const renderOpenRecovery = (candidate, threadId, hostId2) => {
26424
26844
  if (!openImportedThread) return;
26425
26845
  actions = [];
26426
26846
  refresh.disabled = false;
@@ -26473,6 +26893,7 @@ ${error51.stderrTail}`] : []
26473
26893
  retry.className = "settings-command-button";
26474
26894
  retry.dataset.sessionImportAction = "retry-open";
26475
26895
  const setRetrying = (pending) => {
26896
+ lockHosts(pending);
26476
26897
  listControls.setBusy(pending, pending);
26477
26898
  for (const button2 of sourceButtons) button2.disabled = pending;
26478
26899
  retry.disabled = pending;
@@ -26487,12 +26908,12 @@ ${error51.stderrTail}`] : []
26487
26908
  retry.addEventListener("click", () => {
26488
26909
  if (retry.disabled) return;
26489
26910
  setRetrying(true);
26490
- void context.runLatest((signal) => openImportedThread(threadId, signal), {
26911
+ void context.runLatest((signal) => openImportedThread(threadId, signal, hostId2), {
26491
26912
  success() {
26492
26913
  setRetrying(false);
26493
26914
  },
26494
26915
  failure() {
26495
- renderOpenRecovery(candidate, threadId);
26916
+ renderOpenRecovery(candidate, threadId, hostId2);
26496
26917
  }
26497
26918
  });
26498
26919
  });
@@ -26569,7 +26990,7 @@ ${error51.stderrTail}`] : []
26569
26990
  if (candidate.running === true || importingId !== null || selectedHarness === null)
26570
26991
  return;
26571
26992
  const harnessId = selectedHarness;
26572
- const client = getClient();
26993
+ const client = loadedClient;
26573
26994
  if (!client) {
26574
26995
  renderUnavailable(true);
26575
26996
  return;
@@ -26587,7 +27008,7 @@ ${error51.stderrTail}`] : []
26587
27008
  });
26588
27009
  committedThreadId = result.threadId;
26589
27010
  if (!signal.aborted && openImportedThread)
26590
- await openImportedThread(result.threadId, signal);
27011
+ await openImportedThread(result.threadId, signal, client.hostId);
26591
27012
  },
26592
27013
  {
26593
27014
  success() {
@@ -26605,7 +27026,7 @@ ${error51.stderrTail}`] : []
26605
27026
  updateImportActions();
26606
27027
  refresh.disabled = false;
26607
27028
  if (committedThreadId) {
26608
- renderOpenRecovery(candidate, committedThreadId);
27029
+ renderOpenRecovery(candidate, committedThreadId, client.hostId);
26609
27030
  } else {
26610
27031
  renderFailure(error51, "import");
26611
27032
  }
@@ -26623,23 +27044,22 @@ ${error51.stderrTail}`] : []
26623
27044
  };
26624
27045
  const load = () => {
26625
27046
  if (importingId !== null || context.signal.aborted) return;
26626
- const client = getClient();
26627
- if (!client) {
26628
- refresh.disabled = false;
26629
- setRefreshLabel(false);
26630
- renderUnavailable();
26631
- return;
26632
- }
26633
- listControls.setBusy(true);
26634
- refresh.disabled = true;
26635
- setRefreshLabel(true);
26636
- content.replaceChildren(createStatus(document2, messages.sessionImportRefreshing));
27047
+ loadedClient = null;
27048
+ const client = getClient(selectedHostId);
27049
+ renderHosts(client);
27050
+ listControls.setBusy(client !== null);
27051
+ refresh.disabled = client !== null;
27052
+ setRefreshLabel(client !== null);
27053
+ if (client)
27054
+ content.replaceChildren(createStatus(document2, messages.sessionImportRefreshing));
27055
+ else renderUnavailable();
26637
27056
  const requestedHarness = selectedHarness;
26638
27057
  const pageParams = listControls.params();
26639
27058
  void context.runLatest(
26640
27059
  async (signal) => {
27060
+ if (!client) throw new RendererSessionImportUnavailableError();
26641
27061
  const missing = new Set(
26642
- getDiagnostics()?.snapshot().hosts.find(({ hostId: hostId2 }) => hostId2 === "local")?.agents.filter(({ availability }) => availability === "notInstalled").map(({ agent }) => agent)
27062
+ getDiagnostics()?.snapshot().hosts.find(({ hostId: hostId2 }) => hostId2 === (client.hostId ?? "local"))?.agents.filter(({ availability }) => availability === "notInstalled").map(({ agent }) => agent)
26643
27063
  );
26644
27064
  const harnesses = (await client.listSessionImportSources()).harnesses.filter(
26645
27065
  ({ harnessId }) => !missing.has(harnessId)
@@ -26657,6 +27077,7 @@ ${error51.stderrTail}`] : []
26657
27077
  },
26658
27078
  {
26659
27079
  success(result) {
27080
+ loadedClient = client;
26660
27081
  listControls.setBusy(false);
26661
27082
  if (listControls.setTotal(result.total)) {
26662
27083
  load();
@@ -26669,6 +27090,7 @@ ${error51.stderrTail}`] : []
26669
27090
  else renderCandidates();
26670
27091
  },
26671
27092
  failure(error51) {
27093
+ loadedClient = null;
26672
27094
  listControls.setBusy(false);
26673
27095
  candidates = [];
26674
27096
  refresh.disabled = false;
@@ -26678,6 +27100,19 @@ ${error51.stderrTail}`] : []
26678
27100
  }
26679
27101
  );
26680
27102
  };
27103
+ const selectHost = (hostId2) => {
27104
+ if (hostsLocked || !availableHostIds.includes(hostId2) || hostId2 === selectedHostId) return;
27105
+ selectedHostId = hostId2;
27106
+ selectedHarness = null;
27107
+ sources = [];
27108
+ candidates = [];
27109
+ actions = [];
27110
+ importStatus.replaceChildren();
27111
+ listControls.reset();
27112
+ renderHarnessOptions();
27113
+ load();
27114
+ hostButtons.find((button2) => button2.dataset.sessionImportHost === hostId2)?.focus();
27115
+ };
26681
27116
  refresh.addEventListener("click", load);
26682
27117
  load();
26683
27118
  return void 0;
@@ -26854,14 +27289,14 @@ ${error51.stderrTail}`] : []
26854
27289
  description: messages.idleReleaseTimeoutDescription,
26855
27290
  controlId: minutesId
26856
27291
  });
26857
- const { field, control: minutes } = createNumberField(document2, {
27292
+ const { field: field2, control: minutes } = createNumberField(document2, {
26858
27293
  id: minutesId,
26859
27294
  describedBy: timeout.description.id,
26860
27295
  unit: messages.idleReleaseMinutes,
26861
27296
  min: IDLE_RELEASE_TIMEOUT_MINUTES_MIN,
26862
27297
  max: IDLE_RELEASE_TIMEOUT_MINUTES_MAX
26863
27298
  });
26864
- timeout.item.append(field);
27299
+ timeout.item.append(field2);
26865
27300
  timeout.item.id = preferenceId("idle-release-timeout");
26866
27301
  enabled.setAttribute("aria-controls", timeout.item.id);
26867
27302
  card.append(toggle.item, timeout.item);
@@ -26882,7 +27317,7 @@ ${error51.stderrTail}`] : []
26882
27317
  statusText.textContent = value === "applied" ? "" : statusMessages[value];
26883
27318
  };
26884
27319
  const setInvalid = (invalid) => {
26885
- field.dataset.invalid = String(invalid);
27320
+ field2.dataset.invalid = String(invalid);
26886
27321
  timeout.description.dataset.invalid = String(invalid);
26887
27322
  timeout.description.textContent = invalid ? messages.idleReleaseInvalid : messages.idleReleaseTimeoutDescription;
26888
27323
  minutes.setAttribute("aria-invalid", String(invalid));
@@ -29147,7 +29582,7 @@ ${error51.stderrTail}`] : []
29147
29582
  }
29148
29583
  });
29149
29584
  }
29150
- 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"))) {
29585
+ 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) {
29151
29586
  return Object.freeze([
29152
29587
  createConnectionsSettingsPage(messages, getDiagnostics, void 0, getRemoteConnections),
29153
29588
  createRemoteConnectionsPage(messages, getRemoteConnections),
@@ -29161,7 +29596,8 @@ ${error51.stderrTail}`] : []
29161
29596
  createUsageStatisticsPage(
29162
29597
  consoleMessages(messages.locale),
29163
29598
  requestUsageStatistics,
29164
- messages.locale
29599
+ messages.locale,
29600
+ usageStatisticsHosts
29165
29601
  ),
29166
29602
  createAppearanceSettingsPage(messages, getLoadedSessionsClient),
29167
29603
  updatesPage(messages, getUpdateClient),
@@ -29720,6 +30156,41 @@ ${error51.stderrTail}`] : []
29720
30156
  margin-top: 0;
29721
30157
  }
29722
30158
 
30159
+ .settings-session-import-host {
30160
+ display: flex;
30161
+ align-items: center;
30162
+ min-width: 0;
30163
+ gap: 12px;
30164
+ margin: -12px 0 12px;
30165
+ color: var(--settings-muted);
30166
+ font-size: 13px;
30167
+ }
30168
+
30169
+ .settings-session-import-host > span,
30170
+ .settings-session-import-host + .settings-session-import-harness > span {
30171
+ flex: none;
30172
+ min-width: 60px;
30173
+ }
30174
+
30175
+ .settings-session-import-host > .settings-connection-host-tabs {
30176
+ flex: 1;
30177
+ }
30178
+
30179
+ .settings-session-import-host .settings-connection-host-tab {
30180
+ height: 34px;
30181
+ padding: 0 12px;
30182
+ font-size: 13px;
30183
+ }
30184
+
30185
+ .settings-session-import-host .settings-connection-host-tab:disabled {
30186
+ opacity: 0.38;
30187
+ cursor: not-allowed;
30188
+ }
30189
+
30190
+ .settings-session-import-host + .settings-session-import-harness {
30191
+ margin-top: 0;
30192
+ }
30193
+
29723
30194
  .settings-session-import-search,
29724
30195
  .settings-session-import-pagination {
29725
30196
  display: flex;
@@ -29938,6 +30409,20 @@ ${error51.stderrTail}`] : []
29938
30409
  gap: 18px;
29939
30410
  }
29940
30411
 
30412
+ .settings-connections-content {
30413
+ container: settings-connections / inline-size;
30414
+ }
30415
+
30416
+ @container settings-connections (max-width: 780px) {
30417
+ .settings-connections-layout {
30418
+ grid-template-columns: minmax(0, 1fr);
30419
+ }
30420
+
30421
+ .settings-connection-inspector {
30422
+ position: static;
30423
+ }
30424
+ }
30425
+
29941
30426
  .settings-connection-list,
29942
30427
  .settings-connection-inspector {
29943
30428
  overflow: hidden;
@@ -29946,14 +30431,36 @@ ${error51.stderrTail}`] : []
29946
30431
  border-radius: 10px;
29947
30432
  }
29948
30433
 
30434
+ .settings-connection-host-header {
30435
+ display: flex;
30436
+ align-items: center;
30437
+ min-width: 0;
30438
+ border-bottom: 1px solid var(--settings-divider);
30439
+ }
30440
+
30441
+ .settings-connection-update-all {
30442
+ flex: none;
30443
+ margin-right: 10px;
30444
+ white-space: nowrap;
30445
+ }
30446
+
30447
+ .settings-connection-update-result {
30448
+ margin: 0;
30449
+ padding: 7px 10px;
30450
+ color: var(--settings-muted);
30451
+ font-size: 11px;
30452
+ line-height: 18px;
30453
+ border-bottom: 1px solid var(--settings-divider);
30454
+ }
30455
+
29949
30456
  .settings-connection-host-strip {
29950
30457
  display: grid;
30458
+ flex: 1;
29951
30459
  grid-template-columns: 30px minmax(0, 1fr) 30px;
29952
30460
  align-items: center;
29953
30461
  min-width: 0;
29954
30462
  gap: 5px;
29955
30463
  padding: 9px 10px;
29956
- border-bottom: 1px solid var(--settings-divider);
29957
30464
  }
29958
30465
 
29959
30466
  .settings-connection-host-scroll {
@@ -30040,7 +30547,8 @@ ${error51.stderrTail}`] : []
30040
30547
  background: var(--settings-hover);
30041
30548
  }
30042
30549
 
30043
- .settings-connection-host-tab[aria-selected="true"] {
30550
+ .settings-connection-host-tab[aria-selected="true"],
30551
+ .settings-connection-host-tab[aria-pressed="true"] {
30044
30552
  color: var(--settings-text);
30045
30553
  font-weight: 600;
30046
30554
  background: var(--settings-active);
@@ -30050,7 +30558,7 @@ ${error51.stderrTail}`] : []
30050
30558
  .settings-connection-table-header,
30051
30559
  .settings-connection-row {
30052
30560
  display: grid;
30053
- grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) 108px;
30561
+ grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) minmax(108px, max-content);
30054
30562
  align-items: center;
30055
30563
  min-width: 0;
30056
30564
  gap: 10px;
@@ -30143,6 +30651,14 @@ ${error51.stderrTail}`] : []
30143
30651
  line-height: 18px;
30144
30652
  }
30145
30653
 
30654
+ .settings-connection-version-available {
30655
+ flex: none;
30656
+ color: var(--settings-muted);
30657
+ font-size: 10px;
30658
+ line-height: 16px;
30659
+ white-space: nowrap;
30660
+ }
30661
+
30146
30662
  .settings-connection-row__status::before {
30147
30663
  width: 6px;
30148
30664
  height: 6px;
@@ -30179,6 +30695,7 @@ ${error51.stderrTail}`] : []
30179
30695
  justify-content: flex-end;
30180
30696
  min-width: 0;
30181
30697
  gap: 4px;
30698
+ white-space: nowrap;
30182
30699
  }
30183
30700
 
30184
30701
  .settings-connection-row__handle {