@dotobokuri/fleet-console 1.67.0 → 1.68.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 (68) hide show
  1. package/dist/cli.mjs +379 -48
  2. package/dist/client/assets/{_baseUniq-DG32CEpb.js → _baseUniq-BPBd-J9P.js} +1 -1
  3. package/dist/client/assets/{arc-CoHkSCMF.js → arc-spHqy4X3.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-Q4EWVU46-f0RKHqJg.js → architectureDiagram-Q4EWVU46-7CHqKfxu.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-DXYQGD6D-Dxb3hLAV.js → blockDiagram-DXYQGD6D-YasFDgl_.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AHTNJAMY-C5q_ahSd.js → c4Diagram-AHTNJAMY-CcoPJq56.js} +1 -1
  7. package/dist/client/assets/channel-DfEjadrw.js +1 -0
  8. package/dist/client/assets/{chunk-4BX2VUAB-BS0W4Fpt.js → chunk-4BX2VUAB-MYBJn9BE.js} +1 -1
  9. package/dist/client/assets/{chunk-4TB4RGXK-p03eWKTO.js → chunk-4TB4RGXK-C1wg6GR_.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-BB1ALVX2.js → chunk-55IACEB6-BoufMT1O.js} +1 -1
  11. package/dist/client/assets/{chunk-EDXVE4YY-DV4N-Npm.js → chunk-EDXVE4YY-BzAhAIl3.js} +1 -1
  12. package/dist/client/assets/{chunk-FMBD7UC4-Da8VspnD.js → chunk-FMBD7UC4-yVrLsKzP.js} +1 -1
  13. package/dist/client/assets/{chunk-OYMX7WX6-CWTmAQhy.js → chunk-OYMX7WX6-YiiKaqRQ.js} +1 -1
  14. package/dist/client/assets/{chunk-QZHKN3VN-DE9xUsy3.js → chunk-QZHKN3VN-CqWQlGHT.js} +1 -1
  15. package/dist/client/assets/{chunk-YZCP3GAM-BxriNNdB.js → chunk-YZCP3GAM-9yLOmU5k.js} +1 -1
  16. package/dist/client/assets/classDiagram-6PBFFD2Q-D9vQdv0q.js +1 -0
  17. package/dist/client/assets/classDiagram-v2-HSJHXN6E-D9vQdv0q.js +1 -0
  18. package/dist/client/assets/clone-Ck2x5JX2.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-S5V4N54A-B4sjskIn.js → cose-bilkent-S5V4N54A-XkTuSq7v.js} +1 -1
  20. package/dist/client/assets/{dagre-KV5264BT-oJms5aS3.js → dagre-KV5264BT-DyzXIEAe.js} +1 -1
  21. package/dist/client/assets/{diagram-5BDNPKRD-BAbLGddM.js → diagram-5BDNPKRD-DF24YlCa.js} +1 -1
  22. package/dist/client/assets/{diagram-G4DWMVQ6-BrhRQwe-.js → diagram-G4DWMVQ6-CVhcj9RK.js} +1 -1
  23. package/dist/client/assets/{diagram-MMDJMWI5-Buxu7Ng3.js → diagram-MMDJMWI5-C-kGKk4K.js} +1 -1
  24. package/dist/client/assets/{diagram-TYMM5635-Cnp__7LJ.js → diagram-TYMM5635-mr285WbR.js} +1 -1
  25. package/dist/client/assets/{erDiagram-SMLLAGMA-ctmZHrVO.js → erDiagram-SMLLAGMA-BtEx-XlU.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-DWJPFMVM-DsQXJqF_.js → flowDiagram-DWJPFMVM-DuWy3xzN.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-SAFJNY9M.js → ganttDiagram-T4ZO3ILL-B2-xEikU.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-Bd5Dq0vQ.js → gitGraphDiagram-UUTBAWPF-EM17u_t7.js} +1 -1
  29. package/dist/client/assets/{graph-DzMOPhFp.js → graph-xuE12FIq.js} +1 -1
  30. package/dist/client/assets/index-C5EOZGlC.js +466 -0
  31. package/dist/client/assets/index-CHHE3nPw.css +1 -0
  32. package/dist/client/assets/{infoDiagram-42DDH7IO-BrEfG9P4.js → infoDiagram-42DDH7IO-B3MF4XtW.js} +1 -1
  33. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-Dg22orvH.js → ishikawaDiagram-UXIWVN3A-DoQ2mGyh.js} +1 -1
  34. package/dist/client/assets/{journeyDiagram-VCZTEJTY-DEcjEc6c.js → journeyDiagram-VCZTEJTY-GeR9sFH-.js} +1 -1
  35. package/dist/client/assets/{kanban-definition-6JOO6SKY-C2Xa4znN.js → kanban-definition-6JOO6SKY-DrrYMuvL.js} +1 -1
  36. package/dist/client/assets/{layout-B7E_-0ev.js → layout-DH63A-3u.js} +1 -1
  37. package/dist/client/assets/{linear-BXJsh3FL.js → linear-BIjE6zB1.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-CdZpLnyY.js → mermaid.core-yrrC2Lqo.js} +4 -4
  39. package/dist/client/assets/{min-CEAFFcJY.js → min-CR-H8E6U.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-QFDTVHPH-iXzcnu1C.js → mindmap-definition-QFDTVHPH-BzSC43J-.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-DEJITSTG-CdRN75ad.js → pieDiagram-DEJITSTG-BdghNtCc.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-Dscl0EFH.js → quadrantDiagram-34T5L4WZ-s_zN2epw.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-MS252O5E-CLmSCaQs.js → requirementDiagram-MS252O5E-DuiGG6Tx.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-XADWPNL6-BfPk87jf.js → sankeyDiagram-XADWPNL6-Bp0JBk2g.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-FGHM5R23-DE8sU8ej.js → sequenceDiagram-FGHM5R23-AzQ3M31V.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-FHFEXIEX-BmaQc1Nm.js → stateDiagram-FHFEXIEX-BGgm44ev.js} +1 -1
  47. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-CZDbnuN-.js +1 -0
  48. package/dist/client/assets/{timeline-definition-GMOUNBTQ-C4Z0umzx.js → timeline-definition-GMOUNBTQ-UqfsiBJY.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-DHZGUBPP-BKlcRXlE.js → vennDiagram-DHZGUBPP-PsCGF9nR.js} +1 -1
  50. package/dist/client/assets/{wardley-RL74JXVD-B0RT7bAW.js → wardley-RL74JXVD-BDQbnh-d.js} +1 -1
  51. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-mx28kvam.js → wardleyDiagram-NUSXRM2D-CtVA1JMs.js} +1 -1
  52. package/dist/client/assets/{xychartDiagram-5P7HB3ND-DU6qIPI0.js → xychartDiagram-5P7HB3ND-BsLlZWbc.js} +1 -1
  53. package/dist/client/index.html +2 -2
  54. package/dist/fleet-plugins/file-explorer/routes.mjs +21 -2
  55. package/dist/fleet-plugins/ledger/routes.mjs +9 -2
  56. package/dist/fleet-plugins/quota/routes.mjs +9 -2
  57. package/dist/fleet-plugins/repository/routes.mjs +910 -26
  58. package/dist/fleet-plugins/scuttlebutt/routes.mjs +11 -3
  59. package/dist/fleet-plugins/terminal/routes.mjs +626 -934
  60. package/dist/fleet.mjs +832 -806
  61. package/package.json +1 -1
  62. package/dist/client/assets/channel-C-Qx-Oi-.js +0 -1
  63. package/dist/client/assets/classDiagram-6PBFFD2Q-DA7e8Mft.js +0 -1
  64. package/dist/client/assets/classDiagram-v2-HSJHXN6E-DA7e8Mft.js +0 -1
  65. package/dist/client/assets/clone-Rpb38n4i.js +0 -1
  66. package/dist/client/assets/index-BLA_qnPk.css +0 -1
  67. package/dist/client/assets/index-CGE8u6Es.js +0 -465
  68. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-TcoFpvuT.js +0 -1
package/dist/cli.mjs CHANGED
@@ -917,16 +917,14 @@ function sanitizeGlobalOptionsData(value) {
917
917
  return { data: createEmptyGlobalOptionsData(), changed: true };
918
918
  }
919
919
  const agentIdleDormantMinutes = sanitizeAgentIdleDormantMinutes(value.agentIdleDormantMinutes);
920
- const hasCanonicalMode = "claudeGatewaySystemPromptMode" in value;
921
- const validMode = sanitizeClaudeGatewaySystemPromptMode(value.claudeGatewaySystemPromptMode);
922
- const claudeGatewaySystemPromptMode = validMode ?? (!hasCanonicalMode && value.replaceClaudeGatewaySystemPrompt === true ? "replace" : "append");
920
+ const claudeCodeSystemPrompt = sanitizeClaudeCodeSystemPrompt(value.claudeCodeSystemPrompt);
923
921
  const data = {
924
922
  version: GLOBAL_OPTIONS_VERSION,
925
923
  ...agentIdleDormantMinutes !== void 0 ? { agentIdleDormantMinutes } : {},
926
- claudeGatewaySystemPromptMode
924
+ ...claudeCodeSystemPrompt !== void 0 ? { claudeCodeSystemPrompt } : {}
927
925
  };
928
- const allowedKeys = /* @__PURE__ */ new Set(["version", "agentIdleDormantMinutes", "claudeGatewaySystemPromptMode"]);
929
- const changed = Object.keys(value).some((key) => !allowedKeys.has(key)) || "agentIdleDormantMinutes" in value && agentIdleDormantMinutes === void 0 || validMode === void 0;
926
+ const allowedKeys = /* @__PURE__ */ new Set(["version", "agentIdleDormantMinutes", "claudeCodeSystemPrompt"]);
927
+ const changed = Object.keys(value).some((key) => !allowedKeys.has(key)) || "agentIdleDormantMinutes" in value && agentIdleDormantMinutes === void 0 || "claudeCodeSystemPrompt" in value && claudeCodeSystemPrompt === void 0;
930
928
  return { data, changed };
931
929
  }
932
930
  function sanitizeAgentIdleDormantMinutes(value) {
@@ -934,8 +932,8 @@ function sanitizeAgentIdleDormantMinutes(value) {
934
932
  if (typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value > 0) return value;
935
933
  return void 0;
936
934
  }
937
- function sanitizeClaudeGatewaySystemPromptMode(value) {
938
- return value === "append" || value === "replace" || value === "off" ? value : void 0;
935
+ function sanitizeClaudeCodeSystemPrompt(value) {
936
+ return value === "on" || value === "off" ? value : void 0;
939
937
  }
940
938
  function isRecord(value) {
941
939
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -12259,6 +12257,48 @@ function createDesktopThemeRouter(deps) {
12259
12257
  return true;
12260
12258
  };
12261
12259
  }
12260
+ var DESKTOP_UPDATE_PATH = "/api/v1/desktop/update";
12261
+ var DESKTOP_UPDATE_EVENTS_PATH = "/api/v1/desktop/update/events";
12262
+ var DESKTOP_UPDATE_EVENT = "desktop:update";
12263
+ var emptyDesktopUpdateRequest = () => ({ requestedVersion: null, requestId: null });
12264
+ var DESKTOP_UPDATE_API_CATALOG = [
12265
+ {
12266
+ method: "GET",
12267
+ path: DESKTOP_UPDATE_PATH,
12268
+ summary: "Read whether an update was requested for the shell that owns this window to perform.",
12269
+ category: "Desktop",
12270
+ gate: "origin-strict",
12271
+ transport: "http"
12272
+ },
12273
+ {
12274
+ method: "GET",
12275
+ path: DESKTOP_UPDATE_EVENTS_PATH,
12276
+ summary: "Stream update requests the owning shell must perform.",
12277
+ category: "Desktop",
12278
+ gate: "origin-strict",
12279
+ transport: "sse"
12280
+ }
12281
+ ];
12282
+ function createDesktopUpdateRouter(deps) {
12283
+ return ({ req, res, pathname }) => {
12284
+ if (pathname !== DESKTOP_UPDATE_PATH && pathname !== DESKTOP_UPDATE_EVENTS_PATH) return false;
12285
+ if (req.method !== "GET") {
12286
+ deps.writeJson(res, 405, { error: "Method not allowed" });
12287
+ return true;
12288
+ }
12289
+ if (!deps.isAuthorized(req)) {
12290
+ deps.writeJson(res, 401, { error: "unauthorized" });
12291
+ return true;
12292
+ }
12293
+ const snapshot = deps.getUpdateRequest();
12294
+ if (pathname === DESKTOP_UPDATE_PATH) {
12295
+ deps.writeJson(res, 200, snapshot);
12296
+ return true;
12297
+ }
12298
+ deps.subscribe(res, snapshot);
12299
+ return true;
12300
+ };
12301
+ }
12262
12302
  var REMOTE_AUTO_PORT_MIN = 49152;
12263
12303
  var REMOTE_AUTO_PORT_MAX = 65535;
12264
12304
  var REMOTE_AUTO_PORT_ATTEMPTS = 12;
@@ -13307,6 +13347,7 @@ function buildApiCatalog(extraEntries = []) {
13307
13347
  ...DESKTOP_FULLSCREEN_API_CATALOG,
13308
13348
  ...DESKTOP_SHELL_API_CATALOG,
13309
13349
  ...DESKTOP_THEME_API_CATALOG,
13350
+ ...DESKTOP_UPDATE_API_CATALOG,
13310
13351
  ...GLOBAL_SETTINGS_API_CATALOG,
13311
13352
  ...PLUGIN_SETTINGS_API_CATALOG,
13312
13353
  ...SYSTEM_FONTS_API_CATALOG,
@@ -16882,10 +16923,10 @@ function mergeDefs(...defs) {
16882
16923
  function cloneDef(schema) {
16883
16924
  return mergeDefs(schema._zod.def);
16884
16925
  }
16885
- function getElementAtPath(obj, path42) {
16886
- if (!path42)
16926
+ function getElementAtPath(obj, path43) {
16927
+ if (!path43)
16887
16928
  return obj;
16888
- return path42.reduce((acc, key) => acc?.[key], obj);
16929
+ return path43.reduce((acc, key) => acc?.[key], obj);
16889
16930
  }
16890
16931
  function promiseAllObject(promisesObj) {
16891
16932
  const keys = Object.keys(promisesObj);
@@ -17294,11 +17335,11 @@ function explicitlyAborted(x, startIndex = 0) {
17294
17335
  }
17295
17336
  return false;
17296
17337
  }
17297
- function prefixIssues(path42, issues) {
17338
+ function prefixIssues(path43, issues) {
17298
17339
  return issues.map((iss) => {
17299
17340
  var _a3;
17300
17341
  (_a3 = iss).path ?? (_a3.path = []);
17301
- iss.path.unshift(path42);
17342
+ iss.path.unshift(path43);
17302
17343
  return iss;
17303
17344
  });
17304
17345
  }
@@ -17445,16 +17486,16 @@ function flattenError(error51, mapper = (issue3) => issue3.message) {
17445
17486
  }
17446
17487
  function formatError2(error51, mapper = (issue3) => issue3.message) {
17447
17488
  const fieldErrors = { _errors: [] };
17448
- const processError = (error52, path42 = []) => {
17489
+ const processError = (error52, path43 = []) => {
17449
17490
  for (const issue3 of error52.issues) {
17450
17491
  if (issue3.code === "invalid_union" && issue3.errors.length) {
17451
- issue3.errors.map((issues) => processError({ issues }, [...path42, ...issue3.path]));
17492
+ issue3.errors.map((issues) => processError({ issues }, [...path43, ...issue3.path]));
17452
17493
  } else if (issue3.code === "invalid_key") {
17453
- processError({ issues: issue3.issues }, [...path42, ...issue3.path]);
17494
+ processError({ issues: issue3.issues }, [...path43, ...issue3.path]);
17454
17495
  } else if (issue3.code === "invalid_element") {
17455
- processError({ issues: issue3.issues }, [...path42, ...issue3.path]);
17496
+ processError({ issues: issue3.issues }, [...path43, ...issue3.path]);
17456
17497
  } else {
17457
- const fullpath = [...path42, ...issue3.path];
17498
+ const fullpath = [...path43, ...issue3.path];
17458
17499
  if (fullpath.length === 0) {
17459
17500
  fieldErrors._errors.push(mapper(issue3));
17460
17501
  } else {
@@ -17481,17 +17522,17 @@ function formatError2(error51, mapper = (issue3) => issue3.message) {
17481
17522
  }
17482
17523
  function treeifyError(error51, mapper = (issue3) => issue3.message) {
17483
17524
  const result = { errors: [] };
17484
- const processError = (error52, path42 = []) => {
17525
+ const processError = (error52, path43 = []) => {
17485
17526
  var _a3, _b;
17486
17527
  for (const issue3 of error52.issues) {
17487
17528
  if (issue3.code === "invalid_union" && issue3.errors.length) {
17488
- issue3.errors.map((issues) => processError({ issues }, [...path42, ...issue3.path]));
17529
+ issue3.errors.map((issues) => processError({ issues }, [...path43, ...issue3.path]));
17489
17530
  } else if (issue3.code === "invalid_key") {
17490
- processError({ issues: issue3.issues }, [...path42, ...issue3.path]);
17531
+ processError({ issues: issue3.issues }, [...path43, ...issue3.path]);
17491
17532
  } else if (issue3.code === "invalid_element") {
17492
- processError({ issues: issue3.issues }, [...path42, ...issue3.path]);
17533
+ processError({ issues: issue3.issues }, [...path43, ...issue3.path]);
17493
17534
  } else {
17494
- const fullpath = [...path42, ...issue3.path];
17535
+ const fullpath = [...path43, ...issue3.path];
17495
17536
  if (fullpath.length === 0) {
17496
17537
  result.errors.push(mapper(issue3));
17497
17538
  continue;
@@ -17523,8 +17564,8 @@ function treeifyError(error51, mapper = (issue3) => issue3.message) {
17523
17564
  }
17524
17565
  function toDotPath(_path) {
17525
17566
  const segs = [];
17526
- const path42 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
17527
- for (const seg of path42) {
17567
+ const path43 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
17568
+ for (const seg of path43) {
17528
17569
  if (typeof seg === "number")
17529
17570
  segs.push(`[${seg}]`);
17530
17571
  else if (typeof seg === "symbol")
@@ -30188,13 +30229,13 @@ function resolveRef(ref, ctx) {
30188
30229
  if (!ref.startsWith("#")) {
30189
30230
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
30190
30231
  }
30191
- const path42 = ref.slice(1).split("/").filter(Boolean);
30192
- if (path42.length === 0) {
30232
+ const path43 = ref.slice(1).split("/").filter(Boolean);
30233
+ if (path43.length === 0) {
30193
30234
  return ctx.rootSchema;
30194
30235
  }
30195
30236
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
30196
- if (path42[0] === defsKey) {
30197
- const key = path42[1];
30237
+ if (path43[0] === defsKey) {
30238
+ const key = path43[1];
30198
30239
  if (!key || !ctx.defs[key]) {
30199
30240
  throw new Error(`Reference not found: ${ref}`);
30200
30241
  }
@@ -33182,8 +33223,8 @@ function bootEnumDescriptorProto(init) {
33182
33223
  }
33183
33224
 
33184
33225
  // ../../node_modules/.pnpm/@bufbuild+protobuf@2.13.0/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/message.js
33185
- function messageDesc(file2, path42, ...paths) {
33186
- return paths.reduce((acc, cur) => acc.nestedMessages[cur], file2.messages[path42]);
33226
+ function messageDesc(file2, path43, ...paths) {
33227
+ return paths.reduce((acc, cur) => acc.nestedMessages[cur], file2.messages[path43]);
33187
33228
  }
33188
33229
 
33189
33230
  // ../../node_modules/.pnpm/@bufbuild+protobuf@2.13.0/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/descriptor_pb.js
@@ -33778,7 +33819,7 @@ var benchmarks_default = {
33778
33819
  };
33779
33820
  var models_default = {
33780
33821
  version: 1,
33781
- updatedAt: "2026-08-17T00:00:00Z",
33822
+ updatedAt: "2026-08-19T00:00:00Z",
33782
33823
  providers: {
33783
33824
  codex: {
33784
33825
  name: "Codex",
@@ -34509,7 +34550,7 @@ var models_default = {
34509
34550
  xai: {
34510
34551
  name: "xAI",
34511
34552
  defaultModel: "grok-4.6",
34512
- source: "official Grok CLI 1.0.3 ~/.grok/models_cache.json from GET https://cli-chat-proxy.grok.com/v1/models (observed 2026-08-13)",
34553
+ source: "official Grok CLI 1.0.4 ~/.grok/models_cache.json from GET https://cli-chat-proxy.grok.com/v1/models (grok-4.6 observed 2026-08-13); grok-composer-2.5-fast is the account-verified OpenCodex xAI identity, live POST /v1/responses accepted on the Grok CLI proxy 2026-08-19 (absent from the public docs.x.ai catalog)",
34513
34554
  models: [
34514
34555
  {
34515
34556
  modelId: "grok-4.6",
@@ -34527,6 +34568,13 @@ var models_default = {
34527
34568
  ]
34528
34569
  },
34529
34570
  benchmarkKey: "grok-4.6"
34571
+ },
34572
+ {
34573
+ modelId: "grok-composer-2.5-fast",
34574
+ name: "Grok-Composer-2.5-Fast",
34575
+ capabilityClass: "light",
34576
+ wire: "responses",
34577
+ contextWindow: 2e5
34530
34578
  }
34531
34579
  ]
34532
34580
  }
@@ -36440,13 +36488,14 @@ function vendorFlagSettings(options) {
36440
36488
  return Object.keys(settings2).length > 0 ? { settings: settings2 } : {};
36441
36489
  }
36442
36490
  function vendorSystemPrompt(systemPrompt) {
36491
+ if (systemPrompt?.mode === "preset") return { type: "preset", preset: "claude_code" };
36443
36492
  const text = systemPrompt?.text;
36444
36493
  if (typeof text !== "string" || text.length === 0) {
36445
36494
  throw new TypeError("turn.systemPrompt.text must be a non-empty string.");
36446
36495
  }
36447
36496
  if (systemPrompt.mode === "replace") return text;
36448
36497
  if (systemPrompt.mode === "append") return { type: "preset", preset: "claude_code", append: text };
36449
- throw new TypeError(`turn.systemPrompt.mode must be "replace" or "append", got: ${String(systemPrompt.mode)}`);
36498
+ throw new TypeError(`turn.systemPrompt.mode must be "replace", "append", or "preset", got: ${String(systemPrompt.mode)}`);
36450
36499
  }
36451
36500
  function assertKnownTurnKeys(turn) {
36452
36501
  const unknown2 = Object.keys(turn).filter((key) => !CLAUDE_GATEWAY_TURN_KEYS.includes(key));
@@ -39958,6 +40007,86 @@ function resolveConsolePath(pathname) {
39958
40007
  if (!path18__default.extname(withoutPrefix)) return "index.html";
39959
40008
  return withoutPrefix;
39960
40009
  }
40010
+ var CONSOLE_UPDATE_PROGRESS_FILE = "update-progress.json";
40011
+ var IDLE_CONSOLE_UPDATE_PROGRESS = { state: "idle" };
40012
+ var CONSOLE_UPDATE_PROGRESS_STALE_MS = 10 * 60 * 1e3;
40013
+ var CONSOLE_UPDATE_OUTCOME_TTL_MS = 6 * 60 * 60 * 1e3;
40014
+ var RUNNING_PHASES = /* @__PURE__ */ new Set(["starting", "preflight-ok", "stopping-console", "installing", "starting-daemon"]);
40015
+ var PROGRESS_FILE_MODE = 384;
40016
+ function consoleUpdateProgressPath(dataDir) {
40017
+ return path18__default.join(dataDir, CONSOLE_UPDATE_PROGRESS_FILE);
40018
+ }
40019
+ function readConsoleUpdateProgress(dataDir, deps = {}) {
40020
+ const readFile7 = deps.readFile ?? ((filePath) => fs9__default.readFileSync(filePath, "utf8"));
40021
+ const now = deps.now ?? Date.now;
40022
+ let raw;
40023
+ try {
40024
+ raw = readFile7(consoleUpdateProgressPath(dataDir));
40025
+ } catch {
40026
+ return IDLE_CONSOLE_UPDATE_PROGRESS;
40027
+ }
40028
+ const record4 = parseConsoleUpdateProgressRecord(raw);
40029
+ if (!record4) return IDLE_CONSOLE_UPDATE_PROGRESS;
40030
+ return toConsoleUpdateProgressStatus(record4, now());
40031
+ }
40032
+ function toConsoleUpdateProgressStatus(record4, nowMs) {
40033
+ const shared = {
40034
+ phase: record4.phase,
40035
+ startedAt: record4.startedAt,
40036
+ targetVersion: record4.targetVersion,
40037
+ fromVersion: record4.fromVersion,
40038
+ ...record4.endpointChanged === true ? { endpointChanged: true } : {}
40039
+ };
40040
+ if (record4.phase === "completed" || record4.phase === "failed") {
40041
+ const finishedAtMs = Date.parse(record4.updatedAt);
40042
+ if (Number.isFinite(finishedAtMs) && nowMs - finishedAtMs > CONSOLE_UPDATE_OUTCOME_TTL_MS) return IDLE_CONSOLE_UPDATE_PROGRESS;
40043
+ if (record4.phase === "completed") return { state: "completed", ...shared };
40044
+ return { state: "failed", ...shared, ...record4.error ? { error: record4.error } : {} };
40045
+ }
40046
+ if (!RUNNING_PHASES.has(record4.phase)) return IDLE_CONSOLE_UPDATE_PROGRESS;
40047
+ const updatedAtMs = Date.parse(record4.updatedAt);
40048
+ if (Number.isFinite(updatedAtMs) && nowMs - updatedAtMs > CONSOLE_UPDATE_PROGRESS_STALE_MS) {
40049
+ return { state: "failed", ...shared, error: "update_worker_lost" };
40050
+ }
40051
+ return { state: "running", ...shared };
40052
+ }
40053
+ function parseConsoleUpdateProgressRecord(raw) {
40054
+ let value;
40055
+ try {
40056
+ value = JSON.parse(raw);
40057
+ } catch {
40058
+ return null;
40059
+ }
40060
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
40061
+ const entry = value;
40062
+ if (!isConsoleUpdatePhase(entry.phase)) return null;
40063
+ if (typeof entry.startedAt !== "string" || typeof entry.updatedAt !== "string") return null;
40064
+ if (typeof entry.targetVersion !== "string" || typeof entry.fromVersion !== "string") return null;
40065
+ return {
40066
+ phase: entry.phase,
40067
+ startedAt: entry.startedAt,
40068
+ updatedAt: entry.updatedAt,
40069
+ targetVersion: entry.targetVersion,
40070
+ fromVersion: entry.fromVersion,
40071
+ ...entry.endpointChanged === true ? { endpointChanged: true } : {},
40072
+ ...typeof entry.error === "string" ? { error: entry.error } : {}
40073
+ };
40074
+ }
40075
+ function isConsoleUpdatePhase(value) {
40076
+ return typeof value === "string" && (RUNNING_PHASES.has(value) || value === "completed" || value === "failed");
40077
+ }
40078
+ function writeConsoleUpdateProgress(dataDir, record4, deps = {}) {
40079
+ const makeDir = deps.makeDir ?? ((dirPath, options) => {
40080
+ fs9__default.mkdirSync(dirPath, options);
40081
+ });
40082
+ const writeFile5 = deps.writeFile ?? ((filePath, content, options) => {
40083
+ fs9__default.writeFileSync(filePath, content, { mode: options.mode });
40084
+ });
40085
+ makeDir(dataDir, { recursive: true, mode: 448 });
40086
+ writeFile5(consoleUpdateProgressPath(dataDir), JSON.stringify(record4, null, 2), { mode: PROGRESS_FILE_MODE });
40087
+ }
40088
+
40089
+ // core/host/update-apply.ts
39961
40090
  var PACKAGE_NAMES = ["@dotobokuri/fleet-console"];
39962
40091
  var WORKER_FILE_PREFIX = "fleet-console-update-";
39963
40092
  var WORKER_FILE_SUFFIX = ".mjs";
@@ -39988,21 +40117,34 @@ function createConsoleUpdateApplyService(deps = {}) {
39988
40117
  makeDir(request.dataDir, { recursive: true, mode: 448 });
39989
40118
  const statusFile = path18__default.join(request.dataDir, `${WORKER_FILE_PREFIX}${stamp}${STATUS_FILE_SUFFIX}`);
39990
40119
  const logFile = path18__default.join(request.dataDir, `${WORKER_FILE_PREFIX}${stamp}${LOG_FILE_SUFFIX}`);
40120
+ const progressFile = path18__default.join(request.dataDir, CONSOLE_UPDATE_PROGRESS_FILE);
40121
+ const startedAt = new Date(now()).toISOString();
39991
40122
  const script = emitConsoleUpdateWorkerScript({
39992
40123
  currentEndpoint: request.currentEndpoint,
39993
40124
  currentPackageRoot: request.currentPackageRoot,
39994
40125
  currentPid: request.currentPid,
40126
+ fromVersion: request.fromVersion,
39995
40127
  lockFile: request.lockFile,
39996
40128
  logFile,
39997
40129
  packageManager,
39998
40130
  packageNames: PACKAGE_NAMES,
40131
+ progressFile,
40132
+ resumePort: readEndpointPort(request.currentEndpoint),
39999
40133
  serverModulePath,
40000
40134
  statusFile,
40135
+ startedAt,
40001
40136
  targetVersion: request.targetVersion,
40002
40137
  workerPath
40003
40138
  });
40004
40139
  writeFile5(workerPath, script, { mode: TEMP_FILE_MODE });
40005
40140
  await spawnDetachedWorker(spawnWorker, execPath, [workerPath], childEnv);
40141
+ writeConsoleUpdateProgress(request.dataDir, {
40142
+ phase: "starting",
40143
+ startedAt,
40144
+ updatedAt: startedAt,
40145
+ targetVersion: request.targetVersion,
40146
+ fromVersion: request.fromVersion
40147
+ }, { makeDir, writeFile: writeFile5 });
40006
40148
  return { accepted: true };
40007
40149
  }
40008
40150
  return { start };
@@ -40020,6 +40162,8 @@ const stopTimeoutMs = 60000;
40020
40162
  const startTimeoutMs = 60000;
40021
40163
  const sleepMs = 250;
40022
40164
 
40165
+ let consoleStopped = false;
40166
+
40023
40167
  async function main() {
40024
40168
  writeStatus("starting");
40025
40169
  cleanupStaleWorkers();
@@ -40032,14 +40176,21 @@ async function main() {
40032
40176
  await installPackages(manager);
40033
40177
  writeStatus("starting-daemon");
40034
40178
  const lock = await startNewDaemon();
40035
- openBrowser(new URL("console/", lock.endpoint).toString());
40036
- writeStatus("completed");
40179
+ // \uAC19\uC740 \uC8FC\uC18C\uB85C \uB3CC\uC544\uC654\uC73C\uBA74 \uC5F4\uB824 \uC788\uB358 \uD654\uBA74\uC774 \uC2A4\uC2A4\uB85C \uB2E4\uC2DC \uBD99\uB294\uB2E4 \u2014 \uC0C8 \uCC3D\uC740 \uADF8 \uBCF5\uADC0\uB97C
40180
+ // \uB36E\uC5B4\uC4F8 \uBFD0\uC774\uB2E4. \uC8FC\uC18C\uB97C \uB418\uCC3E\uC9C0 \uBABB\uD588\uC744 \uB54C\uB9CC, \uAC08 \uACF3\uC744 \uC783\uC9C0 \uC54A\uB3C4\uB85D \uCC3D\uC744 \uC5F0\uB2E4.
40181
+ const endpointChanged = !isSameEndpoint(lock.endpoint, config.currentEndpoint);
40182
+ if (endpointChanged) openBrowser(new URL("console/", lock.endpoint).toString());
40183
+ writeStatus("completed", endpointChanged ? { endpointChanged: true } : {});
40037
40184
  }
40038
40185
 
40039
40186
  main()
40040
- .catch((error) => {
40041
- writeStatus("failed", { error: sanitizeError(error) });
40042
- log("failed: " + sanitizeError(error));
40187
+ .catch(async (error) => {
40188
+ const reason = sanitizeError(error);
40189
+ log("failed: " + reason);
40190
+ // \uCF58\uC194\uC744 \uC774\uBBF8 \uB0B4\uB9B0 \uB4A4\uC5D0 \uC2E4\uD328\uD588\uB2E4\uBA74, \uC2E4\uD328\uB97C \uB9D0\uD560 \uD654\uBA74\uC870\uCC28 \uC5C6\uB2E4. \uC61B \uBC84\uC804\uC774\uB77C\uB3C4
40191
+ // \uB2E4\uC2DC \uC138\uC6CC\uC57C \uC0AC\uC6A9\uC790\uAC00 \uBB34\uC5C7\uC774 \uC798\uBABB\uB410\uB294\uC9C0 \uC77D\uC744 \uC218 \uC788\uB2E4.
40192
+ if (consoleStopped) await recoverConsoleBestEffort();
40193
+ writeStatus("failed", { error: reason });
40043
40194
  process.exitCode = 1;
40044
40195
  })
40045
40196
  .finally(() => {
@@ -40051,12 +40202,24 @@ main()
40051
40202
  });
40052
40203
 
40053
40204
  function writeStatus(phase, extra = {}) {
40054
- const payload = {
40205
+ const updatedAt = new Date().toISOString();
40206
+ fs.writeFileSync(config.statusFile, JSON.stringify({ phase, updatedAt, ...extra }, null, 2), { mode: 0o600 });
40207
+ // \uC7AC\uAE30\uB3D9\uD55C \uB370\uBAAC\uC774 \uC77D\uB294 \uAC83\uC740 \uC774 \uACE0\uC815 \uC774\uB984\uC758 \uAE30\uB85D\uC774\uB2E4. \uD0C0\uC784\uC2A4\uD0EC\uD504\uAC00 \uBD99\uC740 \uC704 \uD30C\uC77C\uC740
40208
+ // \uC774 \uC2E4\uD589\uC758 \uC9C4\uB2E8 \uD754\uC801\uC774\uACE0, \uC544\uB798\uAC00 "\uBC29\uAE08 \uBB34\uC2A8 \uC77C\uC774 \uC788\uC5C8\uB294\uAC00"\uC5D0 \uB2F5\uD558\uB294 \uCABD\uC774\uB2E4.
40209
+ const record = {
40055
40210
  phase,
40056
- updatedAt: new Date().toISOString(),
40057
- ...extra,
40211
+ startedAt: config.startedAt,
40212
+ updatedAt,
40213
+ targetVersion: config.targetVersion,
40214
+ fromVersion: config.fromVersion,
40058
40215
  };
40059
- fs.writeFileSync(config.statusFile, JSON.stringify(payload, null, 2), { mode: 0o600 });
40216
+ if (extra.endpointChanged === true) record.endpointChanged = true;
40217
+ if (typeof extra.error === "string") record.error = extra.error;
40218
+ try {
40219
+ fs.writeFileSync(config.progressFile, JSON.stringify(record, null, 2), { mode: 0o600 });
40220
+ } catch {
40221
+ // \uC9C4\uB2E8 \uAE30\uB85D\uC774 \uC5C6\uB2E4\uACE0 \uC5C5\uB370\uC774\uD2B8\uB97C \uBA48\uCD94\uC9C0\uB294 \uC54A\uB294\uB2E4.
40222
+ }
40060
40223
  log("phase: " + phase);
40061
40224
  }
40062
40225
 
@@ -40081,6 +40244,7 @@ function cleanupStaleWorkers() {
40081
40244
 
40082
40245
  async function stopCurrentConsole() {
40083
40246
  writeStatus("stopping-console");
40247
+ consoleStopped = true;
40084
40248
  try {
40085
40249
  process.kill(config.currentPid, "SIGTERM");
40086
40250
  } catch (error) {
@@ -40093,9 +40257,14 @@ async function waitForOldConsoleExit() {
40093
40257
  let escalated = false;
40094
40258
  while (Date.now() < deadline) {
40095
40259
  const pidGone = !isProcessAlive(config.currentPid);
40096
- const lockGone = !fs.existsSync(config.lockFile);
40097
40260
  const healthGone = await isHealthGone(config.currentEndpoint);
40098
- if (pidGone && lockGone && healthGone) return;
40261
+ if (pidGone && healthGone) {
40262
+ // \uC8FD\uC740 \uD504\uB85C\uC138\uC2A4\uAC00 \uB0A8\uAE34 \uB77D\uC740 \uC815\uC758\uC0C1 stale\uC774\uB2E4. \uADF8\uAC83\uC774 \uC2A4\uC2A4\uB85C \uC0AC\uB77C\uC9C0\uAE30\uB97C \uAE30\uB2E4\uB9AC\uBA74
40263
+ // SIGKILL\uB85C \uB0B4\uB9B0 \uCF58\uC194 \uB4A4\uC5D0\uC11C\uB294 \uC601\uC6D0\uD788 \uAE30\uB2E4\uB9AC\uAC8C \uB418\uACE0, \uC5C5\uB370\uC774\uD2B8\uB294 \uB298 \uC2DC\uAC04 \uCD08\uACFC\uB85C
40264
+ // \uB05D\uB09C\uB2E4. \uC6B0\uB9AC\uAC00 \uB0B4\uB9B0 \uADF8 pid\uC758 \uB77D\uC774\uBA74 \uC6B0\uB9AC\uAC00 \uCE58\uC6B4\uB2E4.
40265
+ removeStaleLock();
40266
+ return;
40267
+ }
40099
40268
  if (!escalated && Date.now() > deadline - 10000 && isProcessAlive(config.currentPid)) {
40100
40269
  escalated = true;
40101
40270
  try {
@@ -40145,10 +40314,15 @@ async function installPackages(manager) {
40145
40314
  if (code !== 0) throw new Error("global package install failed with exit code " + code);
40146
40315
  }
40147
40316
 
40317
+ function daemonEnv() {
40318
+ if (config.resumePort === null) return process.env;
40319
+ return { ...process.env, FLEET_CONSOLE_RESUME_PORT: String(config.resumePort) };
40320
+ }
40321
+
40148
40322
  async function startNewDaemon() {
40149
40323
  const child = spawn(process.execPath, [config.serverModulePath, "serve"], {
40150
40324
  detached: true,
40151
- env: process.env,
40325
+ env: daemonEnv(),
40152
40326
  stdio: "ignore",
40153
40327
  windowsHide: true,
40154
40328
  });
@@ -40163,6 +40337,43 @@ async function startNewDaemon() {
40163
40337
  throw new Error("new console daemon did not become healthy");
40164
40338
  }
40165
40339
 
40340
+ /**
40341
+ * \uC124\uCE58\uAC00 \uC2E4\uD328\uD55C \uB4A4\uC758 \uB9C8\uC9C0\uB9C9 \uC758\uBB34: \uC5B4\uB5A4 \uBC84\uC804\uC774\uB4E0 \uCF58\uC194\uC744 \uB2E4\uC2DC \uC138\uC6B4\uB2E4. \uC5EC\uAE30\uC11C\uB294 \uBAA9\uD45C \uBC84\uC804\uC744
40342
+ * \uC694\uAD6C\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uC61B \uBC84\uC804\uC73C\uB85C\uB77C\uB3C4 \uC0B4\uC544 \uC788\uC5B4\uC57C \uC2E4\uD328\uB97C \uC77D\uC744 \uD654\uBA74\uC774 \uC0DD\uAE34\uB2E4.
40343
+ */
40344
+ async function recoverConsoleBestEffort() {
40345
+ try {
40346
+ const child = spawn(process.execPath, [config.serverModulePath, "serve"], {
40347
+ detached: true,
40348
+ env: daemonEnv(),
40349
+ stdio: "ignore",
40350
+ windowsHide: true,
40351
+ });
40352
+ child.once("error", () => {});
40353
+ child.unref();
40354
+ const deadline = Date.now() + startTimeoutMs;
40355
+ while (Date.now() < deadline) {
40356
+ const lock = readLock();
40357
+ if (lock && isProcessAlive(lock.pid) && await isAnyHealthOk(lock)) {
40358
+ log("recovered console after failure");
40359
+ return;
40360
+ }
40361
+ await sleep(sleepMs);
40362
+ }
40363
+ log("recovery did not become healthy before timeout");
40364
+ } catch (error) {
40365
+ log("recovery failed: " + sanitizeError(error));
40366
+ }
40367
+ }
40368
+
40369
+ function isSameEndpoint(left, right) {
40370
+ try {
40371
+ return new URL(left).host === new URL(right).host;
40372
+ } catch {
40373
+ return false;
40374
+ }
40375
+ }
40376
+
40166
40377
  function openBrowser(url) {
40167
40378
  const platform = os.platform();
40168
40379
  if (platform === "darwin") {
@@ -40190,6 +40401,18 @@ function spawnExit(command, args) {
40190
40401
  });
40191
40402
  }
40192
40403
 
40404
+ /** \uC6B0\uB9AC\uAC00 \uB0B4\uB9B0 pid\uC758 \uB77D\uB9CC \uC9C0\uC6B4\uB2E4 \u2014 \uADF8 \uC0AC\uC774 \uC62C\uB77C\uC628 \uC0C8 \uCF58\uC194\uC758 \uB77D\uC740 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uB294\uB2E4. */
40405
+ function removeStaleLock() {
40406
+ const lock = readLock();
40407
+ if (!lock || lock.pid !== config.currentPid) return;
40408
+ try {
40409
+ fs.rmSync(config.lockFile, { force: true });
40410
+ log("removed the stale lock of the console we stopped");
40411
+ } catch {
40412
+ // \uC9C0\uC6B0\uC9C0 \uBABB\uD574\uB3C4 \uC0C8 \uB370\uBAAC\uC774 stale lock\uC744 \uC2A4\uC2A4\uB85C \uC815\uB9AC\uD55C\uB2E4.
40413
+ }
40414
+ }
40415
+
40193
40416
  function readLock() {
40194
40417
  try {
40195
40418
  return JSON.parse(fs.readFileSync(config.lockFile, "utf8"));
@@ -40216,6 +40439,18 @@ async function isNewHealthOk(lock) {
40216
40439
  }
40217
40440
  }
40218
40441
 
40442
+ async function isAnyHealthOk(lock) {
40443
+ if (!lock || typeof lock.endpoint !== "string" || typeof lock.token !== "string") return false;
40444
+ try {
40445
+ const response = await fetch(new URL("api/v1/health", lock.endpoint), {
40446
+ headers: { authorization: "Bearer " + lock.token },
40447
+ });
40448
+ return response.ok;
40449
+ } catch {
40450
+ return false;
40451
+ }
40452
+ }
40453
+
40219
40454
  async function readHealthVersion(response) {
40220
40455
  try {
40221
40456
  const payload = await response.json();
@@ -40284,6 +40519,14 @@ function sanitizeError(error) {
40284
40519
  }
40285
40520
  `;
40286
40521
  }
40522
+ function readEndpointPort(endpoint) {
40523
+ try {
40524
+ const port = Number.parseInt(new URL(endpoint).port, 10);
40525
+ return Number.isInteger(port) && port > 0 && port < 65536 ? port : null;
40526
+ } catch {
40527
+ return null;
40528
+ }
40529
+ }
40287
40530
  function isManagedRuntimePackageRoot(packageRoot) {
40288
40531
  if (path18__default.basename(packageRoot) !== "latest" || path18__default.basename(path18__default.dirname(packageRoot)) !== "console") return false;
40289
40532
  try {
@@ -40446,6 +40689,7 @@ var MIN_CONSOLE_STATIC_PORT2 = 1024;
40446
40689
  var MAX_CONSOLE_STATIC_PORT2 = 65535;
40447
40690
  var SERVER_TIMEOUT_MS = 30 * 60 * 1e3;
40448
40691
  var MAX_BODY_BYTES = 1024 * 1024;
40692
+ var DESKTOP_UPDATE_REQUEST_TTL_MS = 6e4;
40449
40693
  var UPDATE_APPLY_FORBIDDEN_BODY_KEYS = /* @__PURE__ */ new Set(["channel", "package", "packageName", "packageVersion", "packages", "targetVersion", "version"]);
40450
40694
  var OPERATION_RENAMED_EVENT_CHANNEL = "operation:renamed";
40451
40695
  var PAIRING_IDENTITY_PATH = "/api/v1/pairing-identity";
@@ -40547,6 +40791,14 @@ var SERVER_API_CATALOG = [
40547
40791
  gate: "origin-write",
40548
40792
  transport: "http"
40549
40793
  },
40794
+ {
40795
+ method: "GET",
40796
+ path: "/api/v1/updates/progress",
40797
+ summary: "Read the outcome of the update this console just came back from.",
40798
+ category: "Update",
40799
+ gate: "loopback",
40800
+ transport: "http"
40801
+ },
40550
40802
  {
40551
40803
  method: "POST",
40552
40804
  path: "/api/v1/updates/apply",
@@ -40694,6 +40946,7 @@ function createConsoleServer(deps = {}) {
40694
40946
  const desktop = readDesktopProtocolEnvironment();
40695
40947
  const version2 = deps.version ?? release.version;
40696
40948
  const channel = release.channel;
40949
+ const resumePort = takeConsoleResumePort(process.env);
40697
40950
  const lock = createConsoleLock({ hostname: () => host });
40698
40951
  const releaseNotes = deps.releaseNotes ?? createConsoleReleaseNotesService();
40699
40952
  const updateCheck = deps.updateCheck ?? createConsoleUpdateCheckService({ readRelease: () => release });
@@ -40760,6 +41013,9 @@ function createConsoleServer(deps = {}) {
40760
41013
  const pluginEventListeners = /* @__PURE__ */ new Map();
40761
41014
  const operationSseSubscribers = /* @__PURE__ */ new Set();
40762
41015
  const desktopThemeSseSubscribers = /* @__PURE__ */ new Set();
41016
+ const desktopUpdateSseSubscribers = /* @__PURE__ */ new Set();
41017
+ let desktopUpdateRequest = emptyDesktopUpdateRequest();
41018
+ let desktopUpdateRequestedAt = 0;
40763
41019
  function publishPluginEvent(channel2, payload) {
40764
41020
  for (const listener of pluginEventListeners.get(channel2) ?? []) listener(payload);
40765
41021
  }
@@ -40970,6 +41226,24 @@ function createConsoleServer(deps = {}) {
40970
41226
  });
40971
41227
  }
40972
41228
  });
41229
+ const desktopUpdateRouter = createDesktopUpdateRouter({
41230
+ getUpdateRequest: () => readDesktopUpdateRequest(),
41231
+ isAuthorized: isExactConsoleOrigin,
41232
+ writeJson,
41233
+ subscribe: (res, snapshot) => {
41234
+ res.writeHead(200, withSecurityHeaders2({
41235
+ "Content-Type": "text/event-stream",
41236
+ "Cache-Control": "no-cache",
41237
+ "Connection": "keep-alive"
41238
+ }));
41239
+ res.write(":connected\n\n");
41240
+ res.write(encodeSseData(DESKTOP_UPDATE_EVENT, snapshot));
41241
+ desktopUpdateSseSubscribers.add(res);
41242
+ res.on("close", () => {
41243
+ desktopUpdateSseSubscribers.delete(res);
41244
+ });
41245
+ }
41246
+ });
40973
41247
  const desktopShellRouter = createDesktopShellRouter({
40974
41248
  getShell: (req) => {
40975
41249
  const owner = shellOwnerOf(req);
@@ -41070,6 +41344,7 @@ function createConsoleServer(deps = {}) {
41070
41344
  routeRegistry.register("/api/v1/desktop", async (context) => {
41071
41345
  if (await desktopShellRouter(context)) return true;
41072
41346
  if (await desktopFullscreenRouter(context)) return true;
41347
+ if (desktopUpdateRouter(context)) return true;
41073
41348
  return desktopThemeRouter(context);
41074
41349
  });
41075
41350
  routeRegistry.register("/plugin-runtime", handlePluginRuntimeRoute);
@@ -41195,6 +41470,10 @@ function createConsoleServer(deps = {}) {
41195
41470
  runAsyncHandler(handleObserverReleaseNotes(req, res), res);
41196
41471
  return;
41197
41472
  }
41473
+ if (pathname === "/api/v1/updates/progress") {
41474
+ handleUpdateProgress(req, res);
41475
+ return;
41476
+ }
41198
41477
  if (pathname === "/api/v1/updates/apply") {
41199
41478
  runAsyncHandler(handleUpdateApply(req, res), res);
41200
41479
  return;
@@ -41860,6 +42139,19 @@ function createConsoleServer(deps = {}) {
41860
42139
  throw error51;
41861
42140
  }
41862
42141
  }
42142
+ function handleUpdateProgress(req, res) {
42143
+ if (req.method !== "GET") {
42144
+ writeJson(res, 405, { error: "Method not allowed" });
42145
+ return;
42146
+ }
42147
+ let progress;
42148
+ try {
42149
+ progress = readConsoleUpdateProgress(durablePaths.dir);
42150
+ } catch {
42151
+ progress = IDLE_CONSOLE_UPDATE_PROGRESS;
42152
+ }
42153
+ writeJson(res, 200, progress);
42154
+ }
41863
42155
  async function handleUpdateApply(req, res) {
41864
42156
  if (req.method !== "POST") {
41865
42157
  writeJson(res, 405, { error: "Method not allowed" });
@@ -41891,6 +42183,16 @@ function createConsoleServer(deps = {}) {
41891
42183
  writeJson(res, 409, { error: "update_not_available" });
41892
42184
  return;
41893
42185
  }
42186
+ if (!isLoopbackListener(req) && (body2 === null || body2.acknowledgeHostRestart !== true)) {
42187
+ writeJson(res, 409, { error: "host_restart_confirmation_required" });
42188
+ return;
42189
+ }
42190
+ if (isManagedRuntimePackageRoot(release.packageRoot)) {
42191
+ publishDesktopUpdateRequest({ requestedVersion: freshStatus.latestVersion, requestId: crypto5.randomUUID() });
42192
+ const delegated = { status: "delegated" };
42193
+ writeJson(res, 202, delegated);
42194
+ return;
42195
+ }
41894
42196
  const handle = lockHandle;
41895
42197
  if (!handle || !activeEndpoint || !activeLockFile) {
41896
42198
  writeJson(res, 503, { error: "console_not_ready" });
@@ -41903,6 +42205,7 @@ function createConsoleServer(deps = {}) {
41903
42205
  currentPackageRoot: release.packageRoot,
41904
42206
  currentPid: process.pid,
41905
42207
  dataDir: durablePaths.dir,
42208
+ fromVersion: version2,
41906
42209
  lockFile: activeLockFile,
41907
42210
  targetVersion: freshStatus.latestVersion
41908
42211
  });
@@ -41989,7 +42292,7 @@ function createConsoleServer(deps = {}) {
41989
42292
  listeners = [];
41990
42293
  boundPort = null;
41991
42294
  access.revokeAllSessions();
41992
- if (closingRemote) await new Promise((resolve3) => closingRemote.close(() => resolve3()));
42295
+ await closeHttpServer(closingRemote);
41993
42296
  const cleanupResults = await Promise.allSettled([...pluginCleanupCallbacks].map((cleanup) => cleanup()));
41994
42297
  for (const result of cleanupResults) {
41995
42298
  if (result.status === "rejected") {
@@ -42118,6 +42421,21 @@ function createConsoleServer(deps = {}) {
42118
42421
  res.write(data);
42119
42422
  }
42120
42423
  }
42424
+ function readDesktopUpdateRequest() {
42425
+ if (desktopUpdateRequest.requestId === null) return desktopUpdateRequest;
42426
+ if (Date.now() - desktopUpdateRequestedAt <= DESKTOP_UPDATE_REQUEST_TTL_MS) return desktopUpdateRequest;
42427
+ desktopUpdateRequest = emptyDesktopUpdateRequest();
42428
+ return desktopUpdateRequest;
42429
+ }
42430
+ function publishDesktopUpdateRequest(snapshot) {
42431
+ desktopUpdateRequestedAt = Date.now();
42432
+ desktopUpdateRequest = snapshot;
42433
+ if (desktopUpdateSseSubscribers.size === 0) return;
42434
+ const data = encodeSseData(DESKTOP_UPDATE_EVENT, snapshot);
42435
+ for (const res of desktopUpdateSseSubscribers) {
42436
+ res.write(data);
42437
+ }
42438
+ }
42121
42439
  function persistDurableState() {
42122
42440
  try {
42123
42441
  saveDurableState(deletionCoordinator.list());
@@ -42193,6 +42511,11 @@ function createConsoleServer(deps = {}) {
42193
42511
  }
42194
42512
  };
42195
42513
  function resolveConsolePortListenPlan() {
42514
+ const plan = resolveConfiguredConsolePortListenPlan();
42515
+ if (resumePort === null || plan.portMode !== "dynamic") return plan;
42516
+ return { ...plan, port: resumePort, allowFallback: true };
42517
+ }
42518
+ function resolveConfiguredConsolePortListenPlan() {
42196
42519
  if (deps.port !== void 0) {
42197
42520
  return {
42198
42521
  port,
@@ -42322,7 +42645,7 @@ function createConsoleServer(deps = {}) {
42322
42645
  if (owner !== "local") desktopShellsByOwner.delete(owner);
42323
42646
  }
42324
42647
  broadcastControlChanged();
42325
- if (closing) await new Promise((resolve3) => closing.close(() => resolve3()));
42648
+ await closeHttpServer(closing);
42326
42649
  }
42327
42650
  async function startRemoteAccessIfEnabled(_actualPort) {
42328
42651
  const configured = consoleSettingsStore.load().general?.remoteAccess;
@@ -42582,6 +42905,14 @@ function isPlainObject2(value) {
42582
42905
  function hasForbiddenUpdateApplyBodyKeys(body2) {
42583
42906
  return Object.keys(body2).some((key) => UPDATE_APPLY_FORBIDDEN_BODY_KEYS.has(key));
42584
42907
  }
42908
+ var CONSOLE_RESUME_PORT_ENV = "FLEET_CONSOLE_RESUME_PORT";
42909
+ function takeConsoleResumePort(env) {
42910
+ const raw = env[CONSOLE_RESUME_PORT_ENV];
42911
+ delete env[CONSOLE_RESUME_PORT_ENV];
42912
+ if (raw === void 0) return null;
42913
+ const port = Number.parseInt(raw, 10);
42914
+ return Number.isInteger(port) && port > 0 && port < 65536 ? port : null;
42915
+ }
42585
42916
  function isValidConsoleStaticPort2(value) {
42586
42917
  return typeof value === "number" && Number.isInteger(value) && value >= MIN_CONSOLE_STATIC_PORT2 && value <= MAX_CONSOLE_STATIC_PORT2;
42587
42918
  }