@dotobokuri/fleet-console 1.60.0 → 1.61.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 (64) hide show
  1. package/dist/cli.d.ts +13 -2
  2. package/dist/cli.mjs +114 -28
  3. package/dist/client/assets/{_baseUniq-CA3I-KTP.js → _baseUniq-9W447fNf.js} +1 -1
  4. package/dist/client/assets/{arc-DOkIWugv.js → arc-hoitnVWi.js} +1 -1
  5. package/dist/client/assets/{architectureDiagram-Q4EWVU46--Z_t3Gn9.js → architectureDiagram-Q4EWVU46-DYd58Vfc.js} +1 -1
  6. package/dist/client/assets/{blockDiagram-DXYQGD6D-CrRA5qvl.js → blockDiagram-DXYQGD6D-kuFqPdP_.js} +1 -1
  7. package/dist/client/assets/{c4Diagram-AHTNJAMY-DKvbMOAM.js → c4Diagram-AHTNJAMY-C7n41zCn.js} +1 -1
  8. package/dist/client/assets/channel-D2TsEFjE.js +1 -0
  9. package/dist/client/assets/{chunk-4BX2VUAB-D6wc2KrB.js → chunk-4BX2VUAB-DrHeb54-.js} +1 -1
  10. package/dist/client/assets/{chunk-4TB4RGXK-B4QvYh7f.js → chunk-4TB4RGXK-DFjT6kwT.js} +1 -1
  11. package/dist/client/assets/{chunk-55IACEB6-B0VZPoad.js → chunk-55IACEB6-DxEzuA7W.js} +1 -1
  12. package/dist/client/assets/{chunk-EDXVE4YY-BdHIgvVh.js → chunk-EDXVE4YY-aAlF0YEo.js} +1 -1
  13. package/dist/client/assets/{chunk-FMBD7UC4-BAYTd_pU.js → chunk-FMBD7UC4-DwqRMFL1.js} +1 -1
  14. package/dist/client/assets/{chunk-OYMX7WX6-DkZ-neug.js → chunk-OYMX7WX6-DCkUlaRi.js} +1 -1
  15. package/dist/client/assets/{chunk-QZHKN3VN-B5h7E8GE.js → chunk-QZHKN3VN-B1Dx8Zhl.js} +1 -1
  16. package/dist/client/assets/{chunk-YZCP3GAM-COMJ2EwJ.js → chunk-YZCP3GAM-J2bgJmn-.js} +1 -1
  17. package/dist/client/assets/classDiagram-6PBFFD2Q-DhjJiPic.js +1 -0
  18. package/dist/client/assets/classDiagram-v2-HSJHXN6E-DhjJiPic.js +1 -0
  19. package/dist/client/assets/clone-DwVC2-Mi.js +1 -0
  20. package/dist/client/assets/{cose-bilkent-S5V4N54A-JvC7pCqe.js → cose-bilkent-S5V4N54A-BxzLzyJp.js} +1 -1
  21. package/dist/client/assets/{dagre-KV5264BT-jZdVPQcJ.js → dagre-KV5264BT-D1ixDJxZ.js} +1 -1
  22. package/dist/client/assets/{diagram-5BDNPKRD-B6-u7AU2.js → diagram-5BDNPKRD-DZPIz1Pa.js} +1 -1
  23. package/dist/client/assets/{diagram-G4DWMVQ6-CV5G3KNM.js → diagram-G4DWMVQ6-Pbcr-a5H.js} +1 -1
  24. package/dist/client/assets/{diagram-MMDJMWI5-ZLrEkNww.js → diagram-MMDJMWI5-CBssBmPY.js} +1 -1
  25. package/dist/client/assets/{diagram-TYMM5635-D1tv2JX9.js → diagram-TYMM5635-cVQUs6jp.js} +1 -1
  26. package/dist/client/assets/{erDiagram-SMLLAGMA-Bta0Mt3t.js → erDiagram-SMLLAGMA-ChMSAXos.js} +1 -1
  27. package/dist/client/assets/{flowDiagram-DWJPFMVM-C282oRLT.js → flowDiagram-DWJPFMVM-FEIrU5mu.js} +1 -1
  28. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-D9P4CVIW.js → ganttDiagram-T4ZO3ILL-D6ELHTJY.js} +1 -1
  29. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-DLQnTjJ2.js → gitGraphDiagram-UUTBAWPF-DTJ6HgV4.js} +1 -1
  30. package/dist/client/assets/{graph-Cd8Q-8WG.js → graph-uscjDdHm.js} +1 -1
  31. package/dist/client/assets/index-DMjY1OQ0.js +465 -0
  32. package/dist/client/assets/index-Ec4RaE0g.css +1 -0
  33. package/dist/client/assets/{infoDiagram-42DDH7IO-ClhLpons.js → infoDiagram-42DDH7IO-C2AlA___.js} +1 -1
  34. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-BDeZ2vOG.js → ishikawaDiagram-UXIWVN3A-BZGfIhBp.js} +1 -1
  35. package/dist/client/assets/{journeyDiagram-VCZTEJTY-DbJhbFKN.js → journeyDiagram-VCZTEJTY-DnGNMdvj.js} +1 -1
  36. package/dist/client/assets/{kanban-definition-6JOO6SKY-CYKcE2fE.js → kanban-definition-6JOO6SKY-BkuplZGR.js} +1 -1
  37. package/dist/client/assets/{layout-CuAgTRkn.js → layout-BBayH8iU.js} +1 -1
  38. package/dist/client/assets/{linear-3EL9MakS.js → linear-BQvaHed0.js} +1 -1
  39. package/dist/client/assets/{mermaid.core-7qKaxHEA.js → mermaid.core-CO0f3d6Y.js} +4 -4
  40. package/dist/client/assets/{min-4mJsdKjT.js → min-B610GdeY.js} +1 -1
  41. package/dist/client/assets/{mindmap-definition-QFDTVHPH-B-Zve2OH.js → mindmap-definition-QFDTVHPH-gwkhgCyj.js} +1 -1
  42. package/dist/client/assets/{pieDiagram-DEJITSTG-DlsdbAii.js → pieDiagram-DEJITSTG-DYU2zgk8.js} +1 -1
  43. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-Cb4h0xwt.js → quadrantDiagram-34T5L4WZ-B5Vkk65i.js} +1 -1
  44. package/dist/client/assets/{requirementDiagram-MS252O5E-CZNGYqJt.js → requirementDiagram-MS252O5E-D4EcF7kq.js} +1 -1
  45. package/dist/client/assets/{sankeyDiagram-XADWPNL6-DeVSGR6W.js → sankeyDiagram-XADWPNL6-DFk-RMV4.js} +1 -1
  46. package/dist/client/assets/{sequenceDiagram-FGHM5R23-Te7TkLIe.js → sequenceDiagram-FGHM5R23-B-beo9e0.js} +1 -1
  47. package/dist/client/assets/{stateDiagram-FHFEXIEX-Be-0oYyv.js → stateDiagram-FHFEXIEX-VDT0TOd7.js} +1 -1
  48. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-wHPBl_p1.js +1 -0
  49. package/dist/client/assets/{timeline-definition-GMOUNBTQ-DQyUgC-K.js → timeline-definition-GMOUNBTQ-By4DMNqG.js} +1 -1
  50. package/dist/client/assets/{vennDiagram-DHZGUBPP-BrArIWI8.js → vennDiagram-DHZGUBPP-DzlBrijd.js} +1 -1
  51. package/dist/client/assets/{wardley-RL74JXVD-6-sWm8kk.js → wardley-RL74JXVD-BHSHTMU6.js} +1 -1
  52. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-C4MvFXGS.js → wardleyDiagram-NUSXRM2D-COB-cWHQ.js} +1 -1
  53. package/dist/client/assets/{xychartDiagram-5P7HB3ND-CGrtjhmr.js → xychartDiagram-5P7HB3ND-C-GVSBHd.js} +1 -1
  54. package/dist/client/index.html +2 -2
  55. package/dist/fleet-plugins/terminal/routes.mjs +243 -19
  56. package/dist/fleet.mjs +109 -23
  57. package/package.json +1 -1
  58. package/dist/client/assets/channel-Bjfd2afp.js +0 -1
  59. package/dist/client/assets/classDiagram-6PBFFD2Q-DgpHvFOU.js +0 -1
  60. package/dist/client/assets/classDiagram-v2-HSJHXN6E-DgpHvFOU.js +0 -1
  61. package/dist/client/assets/clone-sFXqwEZz.js +0 -1
  62. package/dist/client/assets/index-B3gusU1E.js +0 -465
  63. package/dist/client/assets/index-_PHObHQ-.css +0 -1
  64. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BnQ2lBhr.js +0 -1
@@ -46487,6 +46487,13 @@ function agentLaunchProviderFromModel(model) {
46487
46487
  function isAgentLaunchProvider(value) {
46488
46488
  return value === "claude" || GATEWAY_PROVIDERS.includes(value);
46489
46489
  }
46490
+ function agentLaunchProviderFromCliId(cliId) {
46491
+ if (!cliId) return void 0;
46492
+ if (cliId === "claude-gateway" || cliId === "claude") return "claude";
46493
+ const separator = cliId.indexOf(GATEWAY_MODEL_SEPARATOR);
46494
+ const candidate = separator > 0 ? cliId.slice(0, separator) : cliId;
46495
+ return GATEWAY_PROVIDERS.includes(candidate) ? candidate : void 0;
46496
+ }
46490
46497
  var BACKGROUND_PENDING_TTL_MS = 30 * 6e4;
46491
46498
  var EMPTY_SETTLED_AGENT_IDS = /* @__PURE__ */ new Set();
46492
46499
  function createConsoleObservabilityStore(deps = {}) {
@@ -46984,18 +46991,38 @@ function readAnalysisProviderSession(value) {
46984
46991
  };
46985
46992
  }
46986
46993
  var MAX_TOOL_DETAIL_CHARS = 160;
46994
+ var MAX_TOOL_RESULT_CHARS = 120;
46987
46995
  var MAX_TEXT_CHARS = 6e4;
46988
- function chatEventsFromTranscriptLine(raw, options = {}) {
46996
+ var WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
46997
+ var CONTENT_RESULT_TOOLS = /* @__PURE__ */ new Set([
46998
+ "Read",
46999
+ "NotebookRead",
47000
+ "Glob",
47001
+ "Grep",
47002
+ "WebFetch",
47003
+ "WebSearch",
47004
+ "Task",
47005
+ "Agent"
47006
+ ]);
47007
+ var PATH_KEYS = ["file_path", "path", "notebook_path"];
47008
+ function chatReplayFromTranscriptLine(raw, options = {}) {
46989
47009
  let line;
46990
47010
  try {
46991
47011
  line = JSON.parse(raw);
46992
47012
  } catch {
46993
- return [];
47013
+ return { events: [] };
46994
47014
  }
47015
+ const parsedAt = typeof line.timestamp === "string" ? Date.parse(line.timestamp) : Number.NaN;
47016
+ const events = eventsFromTranscriptLine(line, options);
47017
+ return Number.isFinite(parsedAt) ? { at: parsedAt, events } : { events };
47018
+ }
47019
+ function eventsFromTranscriptLine(line, options) {
46995
47020
  if (line.isMeta === true || line.isSidechain === true || line.isCompactSummary === true) return [];
46996
47021
  const at = typeof line.timestamp === "string" ? Date.parse(line.timestamp) : Number.NaN;
46997
47022
  const atField = Number.isFinite(at) ? { at } : {};
46998
47023
  if (line.type === "user") {
47024
+ const results = toolResultsFrom(line.message?.content, options);
47025
+ if (results.length > 0) return results;
46999
47026
  const text2 = readUserText(line.message?.content);
47000
47027
  return text2 ? [{ kind: "dispatch", text: text2, ...atField }] : [];
47001
47028
  }
@@ -47008,7 +47035,17 @@ function chatEventsFromSdkMessage(message, options = {}) {
47008
47035
  if (message.type === "stream_event") {
47009
47036
  const inner = message.event;
47010
47037
  if (!inner || typeof inner !== "object") return [];
47011
- if (inner.type !== "content_block_delta") return [];
47038
+ const innerType = inner.type;
47039
+ if (innerType === "content_block_start") {
47040
+ const block = inner.content_block;
47041
+ if (!block || typeof block !== "object") return [];
47042
+ const start = block;
47043
+ if (start.type !== "tool_use") return [];
47044
+ if (typeof start.id !== "string" || start.id.length === 0) return [];
47045
+ if (typeof start.name !== "string" || start.name.length === 0) return [];
47046
+ return [{ kind: "tool-start", id: start.id, name: start.name }];
47047
+ }
47048
+ if (innerType !== "content_block_delta") return [];
47012
47049
  const delta = inner.delta;
47013
47050
  if (!delta || typeof delta !== "object") return [];
47014
47051
  const text2 = delta;
@@ -47021,6 +47058,10 @@ function chatEventsFromSdkMessage(message, options = {}) {
47021
47058
  const body = message.message;
47022
47059
  return eventsFromAssistantContent(body?.content, options);
47023
47060
  }
47061
+ if (message.type === "user") {
47062
+ const body = message.message;
47063
+ return toolResultsFrom(body?.content, options);
47064
+ }
47024
47065
  if (message.type === "result") {
47025
47066
  const durationMs = message.duration_ms;
47026
47067
  const ok = message.is_error !== true;
@@ -47044,11 +47085,37 @@ function eventsFromAssistantContent(content, options) {
47044
47085
  continue;
47045
47086
  }
47046
47087
  if (block.type === "tool_use" && typeof block.name === "string" && block.name.length > 0) {
47047
- events.push({ kind: "tool", name: block.name, detail: summarizeToolInput(block.input, options) });
47088
+ const change = changeFromToolInput(block.name, block.input, options);
47089
+ events.push({
47090
+ kind: "tool",
47091
+ name: block.name,
47092
+ detail: summarizeToolInput(block.input, options),
47093
+ ...typeof block.id === "string" && block.id.length > 0 ? { id: block.id } : {},
47094
+ ...pathIsOutsideCwd(block.input, options.cwd) ? { outside: true } : {},
47095
+ ...change ? { change } : {}
47096
+ });
47048
47097
  }
47049
47098
  }
47050
47099
  return events;
47051
47100
  }
47101
+ function toolResultsFrom(content, options) {
47102
+ if (!Array.isArray(content)) return [];
47103
+ const events = [];
47104
+ for (const block of content) {
47105
+ if (!block || typeof block !== "object" || block.type !== "tool_result") continue;
47106
+ if (typeof block.tool_use_id !== "string" || block.tool_use_id.length === 0) continue;
47107
+ const ok = block.is_error !== true;
47108
+ const tool = options.toolNames?.get(block.tool_use_id);
47109
+ const quiet = ok && tool !== void 0 && (CONTENT_RESULT_TOOLS.has(tool) || WRITE_TOOLS.has(tool));
47110
+ events.push({
47111
+ kind: "tool-result",
47112
+ id: block.tool_use_id,
47113
+ ok,
47114
+ summary: quiet ? "" : summarizeToolResult(block.content, options)
47115
+ });
47116
+ }
47117
+ return events;
47118
+ }
47052
47119
  function readUserText(content) {
47053
47120
  if (typeof content === "string") return normalizeDispatchText(content);
47054
47121
  if (!Array.isArray(content)) return null;
@@ -47068,16 +47135,107 @@ function normalizeDispatchText(text2) {
47068
47135
  function summarizeToolInput(input, options = {}) {
47069
47136
  if (!input || typeof input !== "object" || Array.isArray(input)) return "";
47070
47137
  const record7 = input;
47071
- for (const key of ["file_path", "path", "command", "pattern", "url", "query", "description", "prompt", "subject"]) {
47138
+ for (const key of PATH_KEYS.concat(["command", "pattern", "url", "query", "description", "prompt", "subject"])) {
47072
47139
  const value = record7[key];
47073
47140
  if (typeof value === "string" && value.trim().length > 0) {
47074
47141
  const flat = value.replace(/\s+/g, " ").trim();
47075
- const shown = key === "file_path" || key === "path" ? displayPath(flat, options.cwd) : normalizePathTokens(flat, options.cwd);
47142
+ const shown = PATH_KEYS.includes(key) ? displayPath(flat, options.cwd) : normalizePathTokens(flat, options.cwd);
47076
47143
  return shown.length > MAX_TOOL_DETAIL_CHARS ? `${shown.slice(0, MAX_TOOL_DETAIL_CHARS - 1)}\u2026` : shown;
47077
47144
  }
47078
47145
  }
47079
47146
  return "";
47080
47147
  }
47148
+ function summarizeToolResult(content, options = {}) {
47149
+ const text2 = readResultText(content);
47150
+ if (text2 === null) return "";
47151
+ const first = text2.split("\n").map((line) => line.trim()).find((line) => line.length > 0);
47152
+ if (first === void 0) return "";
47153
+ const flat = normalizePathTokens(first.replace(/\s+/g, " ").trim(), options.cwd);
47154
+ const masked = maskSecrets(abbreviateAbsolutePaths(flat));
47155
+ return masked.length > MAX_TOOL_RESULT_CHARS ? `${masked.slice(0, MAX_TOOL_RESULT_CHARS - 1)}\u2026` : masked;
47156
+ }
47157
+ var QUOTED_ABSOLUTE_PATH = /(['"`])((?:[A-Za-z]:[\\/]|\\\\|\/)[^'"`\n]*)\1/g;
47158
+ var BARE_ABSOLUTE_PATH = /(?<![^\s'"`([{=<])(?:[A-Za-z]:[\\/]|\\\\|\/)[^\s'"`,;:()[\]<>]+/g;
47159
+ function abbreviateAbsolutePaths(value) {
47160
+ return value.replace(QUOTED_ABSOLUTE_PATH, (_match, quote, path46) => `${quote}${foldPath(path46)}${quote}`).replace(BARE_ABSOLUTE_PATH, (match) => foldPath(match));
47161
+ }
47162
+ function foldPath(path46) {
47163
+ const segments = path46.split(/[\\/]/).filter((segment) => segment.length > 0 && !/^[A-Za-z]:$/.test(segment));
47164
+ if (segments.length === 0) return path46;
47165
+ return `\u2026/${segments.slice(-2).join("/")}`;
47166
+ }
47167
+ function readResultText(content) {
47168
+ if (typeof content === "string") return content;
47169
+ if (!Array.isArray(content)) return null;
47170
+ const parts = [];
47171
+ for (const block of content) {
47172
+ if (block?.type === "text" && typeof block.text === "string") parts.push(block.text);
47173
+ }
47174
+ return parts.length > 0 ? parts.join("\n") : null;
47175
+ }
47176
+ function maskSecrets(value) {
47177
+ return value.replace(/\b(sk|pk|rk)-[A-Za-z0-9_-]{16,}/g, "$1-\u2026").replace(/\b(gh[pousr]|xox[baprs])_[A-Za-z0-9_-]{16,}/g, "$1_\u2026").replace(/\bAKIA[0-9A-Z]{12,}/g, "AKIA\u2026").replace(/\b(?:Bearer|Authorization:)\s+[A-Za-z0-9._~+/=-]{16,}/gi, "Bearer \u2026").replace(/\beyJ[A-Za-z0-9._-]{24,}/g, "eyJ\u2026");
47178
+ }
47179
+ function changeFromToolInput(name, input, options = {}) {
47180
+ if (!WRITE_TOOLS.has(name)) return null;
47181
+ if (!input || typeof input !== "object" || Array.isArray(input)) return null;
47182
+ const record7 = input;
47183
+ const raw = PATH_KEYS.map((key) => record7[key]).find((value) => typeof value === "string" && value.trim().length > 0) ?? null;
47184
+ if (raw === null) return null;
47185
+ const file2 = displayPath(raw.trim(), options.cwd);
47186
+ if (name === "Write") {
47187
+ return { file: file2, added: lineCount(record7.content), removed: 0 };
47188
+ }
47189
+ if (name === "Edit") {
47190
+ return { file: file2, added: lineCount(record7.new_string), removed: lineCount(record7.old_string) };
47191
+ }
47192
+ if (name === "MultiEdit") {
47193
+ let added = 0;
47194
+ let removed = 0;
47195
+ const edits = Array.isArray(record7.edits) ? record7.edits : [];
47196
+ for (const edit of edits) {
47197
+ if (!edit || typeof edit !== "object") continue;
47198
+ const entry = edit;
47199
+ added += lineCount(entry.new_string);
47200
+ removed += lineCount(entry.old_string);
47201
+ }
47202
+ return { file: file2, added, removed };
47203
+ }
47204
+ return { file: file2, added: 0, removed: 0 };
47205
+ }
47206
+ function lineCount(value) {
47207
+ if (typeof value !== "string" || value.length === 0) return 0;
47208
+ return value.replace(/\n$/, "").split("\n").length;
47209
+ }
47210
+ function pathIsOutsideCwd(input, cwd) {
47211
+ if (!cwd || !input || typeof input !== "object" || Array.isArray(input)) return false;
47212
+ const record7 = input;
47213
+ for (const key of PATH_KEYS) {
47214
+ const value = record7[key];
47215
+ if (typeof value !== "string" || value.trim().length === 0) continue;
47216
+ const normalized = resolveDotSegments(value.trim().replace(/\\/g, "/"));
47217
+ if (!normalized.startsWith("/") && !/^[A-Za-z]:\//.test(normalized)) return false;
47218
+ const root = resolveDotSegments(cwd.replace(/\\/g, "/")).replace(/\/+$/, "");
47219
+ if (root.length <= 1) return false;
47220
+ return normalized !== root && !normalized.startsWith(`${root}/`);
47221
+ }
47222
+ return false;
47223
+ }
47224
+ function resolveDotSegments(path46) {
47225
+ if (!path46.includes("./") && !path46.endsWith("/.") && !path46.endsWith("/..")) return path46;
47226
+ const parts = path46.split("/");
47227
+ const out = [];
47228
+ for (const part of parts) {
47229
+ if (part === ".") continue;
47230
+ if (part === "..") {
47231
+ if (out.length > 1 || out.length === 1 && out[0] !== "") out.pop();
47232
+ continue;
47233
+ }
47234
+ out.push(part);
47235
+ }
47236
+ const joined = out.join("/");
47237
+ return joined.length > 0 ? joined : "/";
47238
+ }
47081
47239
  function normalizePathTokens(value, cwd) {
47082
47240
  let result = value;
47083
47241
  if (cwd) {
@@ -47089,10 +47247,10 @@ function normalizePathTokens(value, cwd) {
47089
47247
  return result;
47090
47248
  }
47091
47249
  function displayPath(value, cwd) {
47092
- const normalized = value.replace(/\\/g, "/");
47250
+ const normalized = resolveDotSegments(value.replace(/\\/g, "/"));
47093
47251
  if (!normalized.startsWith("/") && !/^[A-Za-z]:\//.test(normalized)) return value;
47094
47252
  if (cwd) {
47095
- const root = cwd.replace(/\\/g, "/").replace(/\/+$/, "");
47253
+ const root = resolveDotSegments(cwd.replace(/\\/g, "/")).replace(/\/+$/, "");
47096
47254
  if (normalized === root) return ".";
47097
47255
  if (normalized.startsWith(`${root}/`)) return normalized.slice(root.length + 1);
47098
47256
  }
@@ -47105,6 +47263,7 @@ function capText(text2) {
47105
47263
 
47106
47264
  // ../fleet-plugins/terminal/server/agent-api/chat-session.ts
47107
47265
  var JOURNAL_CAP = 2e3;
47266
+ var TOOL_NAME_CAP = 500;
47108
47267
  var AgentChatSession = class {
47109
47268
  operationId;
47110
47269
  seed;
@@ -47122,6 +47281,12 @@ var AgentChatSession = class {
47122
47281
  turnFlight = Promise.resolve();
47123
47282
  pendingTurns = 0;
47124
47283
  disposed = false;
47284
+ /**
47285
+ * tool_use id → 도구 이름. 결과 블록은 자기가 어떤 도구의 결말인지 모르는데, 무엇을 요약해도
47286
+ * 되는지는 도구가 정한다(읽기·쓰기 계열의 성공 결과는 내용이라 싣지 않는다). 긴 세션에서
47287
+ * 무한히 자라지 않게 상한을 두고 오래된 것부터 버린다 — 결과는 호출 직후에 오므로 잃을 게 없다.
47288
+ */
47289
+ toolNames = /* @__PURE__ */ new Map();
47125
47290
  constructor(operationId, seed, createSdk) {
47126
47291
  this.operationId = operationId;
47127
47292
  this.seed = seed;
@@ -47136,15 +47301,32 @@ var AgentChatSession = class {
47136
47301
  const transcriptPath = this.seed.origin.transcriptPath;
47137
47302
  this.push({ kind: "replay-start" });
47138
47303
  let turns = 0;
47304
+ let turnAt = null;
47305
+ let lastAt = null;
47306
+ const closeReplayedTurn = () => {
47307
+ if (turnAt !== null && lastAt !== null && lastAt > turnAt) {
47308
+ this.push({ kind: "turn-end", ok: true, durationMs: lastAt - turnAt });
47309
+ }
47310
+ turnAt = null;
47311
+ lastAt = null;
47312
+ };
47139
47313
  try {
47140
47314
  const raw = await promises.readFile(transcriptPath, "utf8");
47141
47315
  for (const line of raw.split("\n")) {
47142
47316
  if (line.trim().length === 0) continue;
47143
- for (const event of chatEventsFromTranscriptLine(line, { cwd: this.seed.cwd })) {
47144
- if (event.kind === "dispatch") turns += 1;
47317
+ const mapped = chatReplayFromTranscriptLine(line, { cwd: this.seed.cwd, toolNames: this.toolNames });
47318
+ for (const event of mapped.events) {
47319
+ if (event.kind === "dispatch") {
47320
+ closeReplayedTurn();
47321
+ turns += 1;
47322
+ turnAt = mapped.at ?? null;
47323
+ }
47324
+ this.rememberTool(event);
47145
47325
  this.push(event);
47146
47326
  }
47327
+ if (mapped.events.length > 0 && mapped.at !== void 0) lastAt = mapped.at;
47147
47328
  }
47329
+ closeReplayedTurn();
47148
47330
  } catch {
47149
47331
  this.push({ kind: "error", code: "chat_replay_unavailable" });
47150
47332
  }
@@ -47185,6 +47367,16 @@ var AgentChatSession = class {
47185
47367
  await this.turnFlight.catch(() => void 0);
47186
47368
  this.listeners.clear();
47187
47369
  }
47370
+ /** 도구 이벤트가 지나갈 때 id→이름을 적어 둔다. 결과 요약 정책이 이 축 위에서 결정된다. */
47371
+ rememberTool(event) {
47372
+ if (event.kind !== "tool" && event.kind !== "tool-start") return;
47373
+ if (event.id === void 0) return;
47374
+ this.toolNames.set(event.id, event.name);
47375
+ if (this.toolNames.size > TOOL_NAME_CAP) {
47376
+ const oldest = this.toolNames.keys().next();
47377
+ if (!oldest.done) this.toolNames.delete(oldest.value);
47378
+ }
47379
+ }
47188
47380
  push(event) {
47189
47381
  const entry = { seq: ++this.seq, event };
47190
47382
  this.journal.push(entry);
@@ -47264,9 +47456,10 @@ var AgentChatSession = class {
47264
47456
  if (typeof message.session_id === "string" && message.session_id.length > 0) {
47265
47457
  this.latestSessionId = message.session_id;
47266
47458
  }
47267
- for (const event of chatEventsFromSdkMessage(message, { cwd: this.seed.cwd })) {
47459
+ for (const event of chatEventsFromSdkMessage(message, { cwd: this.seed.cwd, toolNames: this.toolNames })) {
47268
47460
  if (event.kind === "turn-end") sawResult = true;
47269
- if (event.kind === "text-delta") this.pushEphemeral(event);
47461
+ this.rememberTool(event);
47462
+ if (event.kind === "text-delta" || event.kind === "tool-start") this.pushEphemeral(event);
47270
47463
  else this.push(event);
47271
47464
  }
47272
47465
  }
@@ -47727,7 +47920,10 @@ function buildAgentLaunchKindBackfillPatch(operation) {
47727
47920
  function buildAgentLaunchProviderBackfillPatch(operation) {
47728
47921
  if (operation.pluginId !== TERMINAL_PLUGIN_ID || operation.type !== AGENT_OPERATION_TYPE) return null;
47729
47922
  if (isAgentLaunchProvider(operation.payload[AGENT_LAUNCH_PROVIDER_PAYLOAD_KEY])) return null;
47730
- return { payload: { ...operation.payload, [AGENT_LAUNCH_PROVIDER_PAYLOAD_KEY]: "claude" } };
47923
+ const cliId = typeof operation.payload.cliId === "string" ? operation.payload.cliId : void 0;
47924
+ const provider = agentLaunchProviderFromCliId(cliId);
47925
+ if (!provider) return null;
47926
+ return { payload: { ...operation.payload, [AGENT_LAUNCH_PROVIDER_PAYLOAD_KEY]: provider } };
47731
47927
  }
47732
47928
  function buildGatewayLoadoutTools(deps) {
47733
47929
  const readAiGatewaySettings = deps.readAiGatewaySettings;
@@ -48129,7 +48325,7 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
48129
48325
  if (launchOptions.attachmentIds && launchOptions.attachmentIds.length > 0) {
48130
48326
  launchAttachments.unreserve(launchOptions.attachmentIds);
48131
48327
  }
48132
- ctx.host.http.writeJson(res, 409, { error: "agent_cli_unavailable" });
48328
+ ctx.host.http.writeJson(res, 409, { error: meta3 && !meta3.available ? "agent_cli_not_installed" : "agent_cli_signed_out" });
48133
48329
  return;
48134
48330
  }
48135
48331
  const sessionId = crypto5.randomUUID();
@@ -48205,7 +48401,7 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
48205
48401
  }
48206
48402
  pendingRuntimeSessions.delete(sessionId);
48207
48403
  observability.updateTerminalSessionStatus(sessionId, "error");
48208
- ctx.host.http.writeJson(res, 503, { error: "terminal_unavailable" });
48404
+ ctx.host.http.writeJson(res, 503, { error: classifyLaunchSpawnFailure(error51) });
48209
48405
  }
48210
48406
  }
48211
48407
  async function handleResume(req, res, sessionId) {
@@ -48730,7 +48926,12 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
48730
48926
  terminalRuntime.invalidateTicketsForSession(operationId);
48731
48927
  observability.notifySessionUpdated(dormant);
48732
48928
  const exitCwd = readPayloadString(ctx.host.operations.get(operationId)?.payload ?? {}, "cwd") ?? "";
48733
- ctx.host.operations.patch(operationId, { payload: toOperationPayload(ctx.host.operations.get(operationId)?.payload, exitCwd, dormant, providerSession, observability.getDurableOperation(operationId)?.providerTitle) });
48929
+ ctx.host.operations.patch(operationId, {
48930
+ payload: {
48931
+ ...toOperationPayload(ctx.host.operations.get(operationId)?.payload, exitCwd, dormant, providerSession, observability.getDurableOperation(operationId)?.providerTitle),
48932
+ restoredDormant: true
48933
+ }
48934
+ });
48734
48935
  }
48735
48936
  } else {
48736
48937
  observability.removeTerminalSession(operationId);
@@ -48838,14 +49039,23 @@ async function createAgentApi(ctx, terminalRuntime, deps) {
48838
49039
  if (operation.pluginId !== ctx.pluginId || operation.type !== AGENT_OPERATION_TYPE) continue;
48839
49040
  const providerSession = readProviderSession(operation.payload);
48840
49041
  const chatBorn = operation.payload[CHAT_BORN_PAYLOAD_KEY] === true;
48841
- if (!providerSession && !chatBorn || observability.getTerminalSessionInfo(operation.id)) continue;
49042
+ if (observability.getTerminalSessionInfo(operation.id)) continue;
49043
+ if (!providerSession && !chatBorn) {
49044
+ ctx.host.operations.patch(operation.id, { payload: { ...operation.payload, restoredDormant: true } });
49045
+ continue;
49046
+ }
48842
49047
  const dormant = injectOperation(operation);
48843
49048
  if (operation.payload[CHAT_MODE_PAYLOAD_KEY] === true) {
48844
49049
  const adopted = observability.setTerminalSessionChatActive(operation.id, true);
48845
49050
  if (adopted) observability.notifySessionUpdated(adopted);
48846
49051
  }
48847
49052
  const cwd = readPayloadString(operation.payload, "cwd") || (ctx.host.paths.resolveTheaterPath(operation.theaterId) ?? "");
48848
- ctx.host.operations.patch(operation.id, { payload: toOperationPayload(operation.payload, cwd, dormant, providerSession, observability.getDurableOperation(operation.id)?.providerTitle) });
49053
+ ctx.host.operations.patch(operation.id, {
49054
+ payload: {
49055
+ ...toOperationPayload(operation.payload, cwd, dormant, providerSession, observability.getDurableOperation(operation.id)?.providerTitle),
49056
+ restoredDormant: true
49057
+ }
49058
+ });
48849
49059
  }
48850
49060
  }
48851
49061
  function backfillAgentOperationLaunchAxes() {
@@ -48894,7 +49104,7 @@ function createTerminalWikiToolSpecs(fleetDataDir) {
48894
49104
  }
48895
49105
  function toOperationPayload(existing, cwd, session, providerSession, providerTitle) {
48896
49106
  const payload = { ...existing ?? {} };
48897
- for (const key of ["cwd", "cliId", "launchKindId", "cliLabel", "providerSession", "labelSource", "providerTitle"]) {
49107
+ for (const key of ["cwd", "cliId", "launchKindId", "cliLabel", "providerSession", "labelSource", "providerTitle", "restoredDormant"]) {
48898
49108
  delete payload[key];
48899
49109
  }
48900
49110
  return {
@@ -48980,6 +49190,12 @@ function readHookPrompt(value) {
48980
49190
  return void 0;
48981
49191
  }
48982
49192
  }
49193
+ function classifyLaunchSpawnFailure(error51) {
49194
+ const code = error51?.code;
49195
+ if (code === "ENOENT") return "agent_cli_binary_missing";
49196
+ if (code === "EACCES" || code === "EPERM") return "agent_cli_not_executable";
49197
+ return "terminal_unavailable";
49198
+ }
48983
49199
  var DEFAULT_MAX_READ = 4 * 1024 * 1024;
48984
49200
  var DEFAULT_MAX_REFRESH = 32 * 1024 * 1024;
48985
49201
  var CONTINUITY_BYTES = 4 * 1024;
@@ -50479,6 +50695,7 @@ function formatSignInFailureMessage(displayName, status) {
50479
50695
  var OPERATION_RESTORED_EVENT_CHANNEL2 = "operation:restored";
50480
50696
  var RESTORED_DORMANT_PAYLOAD_KEY = "restoredDormant";
50481
50697
  function registerShellRoutes(ctx, runtime) {
50698
+ markRestoredShellOperations(ctx);
50482
50699
  const unsubscribeRestore = ctx.host.events.subscribe(OPERATION_RESTORED_EVENT_CHANNEL2, (payload) => {
50483
50700
  if (!isRestoredShellEvent(payload, ctx.pluginId)) return;
50484
50701
  const operation = ctx.host.operations.get(payload.operationId);
@@ -50579,6 +50796,13 @@ function registerShellRoutes(ctx, runtime) {
50579
50796
  { method: "POST", path: "/:operationId/relaunch", summary: "Relaunch a dormant Shell session.", category: "Terminal Plugin", gate: "origin-write", transport: "http" }
50580
50797
  ]);
50581
50798
  }
50799
+ function markRestoredShellOperations(ctx) {
50800
+ for (const operation of ctx.host.operations.list()) {
50801
+ if (operation.pluginId !== ctx.pluginId || operation.type !== "shell") continue;
50802
+ if (operation.payload[RESTORED_DORMANT_PAYLOAD_KEY] === true) continue;
50803
+ ctx.host.operations.patch(operation.id, { payload: { ...operation.payload, [RESTORED_DORMANT_PAYLOAD_KEY]: true } });
50804
+ }
50805
+ }
50582
50806
  function isRestoredShellEvent(value, pluginId) {
50583
50807
  if (!value || typeof value !== "object") return false;
50584
50808
  const event = value;
package/dist/fleet.mjs CHANGED
@@ -46753,23 +46753,88 @@ function buildFleetHelpText(options = {}) {
46753
46753
 
46754
46754
  // core/host/console-lifecycle.ts
46755
46755
  init_src();
46756
+ var SPAWN_GRACE_MS = 250;
46756
46757
  function openBrowser(url2, deps = {}) {
46757
46758
  const platform = deps.platform ?? os3__default.platform();
46758
- const spawnBrowser = deps.spawnBrowser ?? ((command2, args, options) => {
46759
- const child = spawn(command2, [...args], options);
46760
- child.once("error", () => {
46759
+ const spawnBrowser = deps.spawnBrowser ?? defaultSpawnBrowser;
46760
+ const options = { detached: true, stdio: "ignore", windowsHide: true };
46761
+ if (platform === "darwin") return normalize(spawnBrowser("open", [url2], options));
46762
+ if (platform === "win32") return normalize(spawnBrowser("cmd", ["/c", "start", "", url2], options));
46763
+ return normalize(spawnBrowser("xdg-open", [url2], options));
46764
+ }
46765
+ async function normalize(result) {
46766
+ const resolved = await result;
46767
+ return resolved ?? { opened: true };
46768
+ }
46769
+ function defaultSpawnBrowser(command2, args, options) {
46770
+ return new Promise((resolve3) => {
46771
+ let child;
46772
+ try {
46773
+ child = spawn(command2, [...args], options);
46774
+ } catch (error51) {
46775
+ resolve3({ opened: false, reason: describe3(error51, command2) });
46776
+ return;
46777
+ }
46778
+ const timer = setTimeout(() => {
46779
+ child.unref();
46780
+ resolve3({ opened: true });
46781
+ }, SPAWN_GRACE_MS);
46782
+ child.once("error", (error51) => {
46783
+ clearTimeout(timer);
46784
+ child.unref();
46785
+ resolve3({ opened: false, reason: describe3(error51, command2) });
46786
+ });
46787
+ child.once("exit", (code) => {
46788
+ clearTimeout(timer);
46789
+ child.unref();
46790
+ if (code === null || code === 0) resolve3({ opened: true });
46791
+ else resolve3({ opened: false, reason: `${command2} exited with status ${code}` });
46761
46792
  });
46762
- child.unref();
46763
46793
  });
46764
- if (platform === "darwin") {
46765
- spawnBrowser("open", [url2], { detached: true, stdio: "ignore", windowsHide: true });
46766
- return;
46767
- }
46768
- if (platform === "win32") {
46769
- spawnBrowser("cmd", ["/c", "start", "", url2], { detached: true, stdio: "ignore", windowsHide: true });
46770
- return;
46794
+ }
46795
+ function describe3(error51, command2) {
46796
+ const code = error51?.code;
46797
+ if (code === "ENOENT") return `${command2} is not on PATH`;
46798
+ if (code === "EACCES") return `${command2} is not executable`;
46799
+ return error51 instanceof Error ? error51.message : String(error51);
46800
+ }
46801
+
46802
+ // core/host/failure-notice.ts
46803
+ function formatFailureNotice(notice) {
46804
+ const lines = [notice.what, ` Why ${notice.why}`];
46805
+ notice.next.forEach((step, index) => {
46806
+ lines.push(` ${index === 0 ? "Next " : " "} ${step}`);
46807
+ });
46808
+ return lines.join("\n");
46809
+ }
46810
+ function describeConsoleLaunch(successLine, outcome) {
46811
+ if (outcome.browserOpened) return successLine;
46812
+ return formatFailureNotice({
46813
+ what: "Fleet Console is running, but no browser opened on this machine.",
46814
+ why: outcome.browserError ?? "the browser launcher did not start",
46815
+ next: [`Open this address yourself: ${outcome.url}`]
46816
+ });
46817
+ }
46818
+ function describeDaemonStartFailure(input) {
46819
+ if (input.spawnError) {
46820
+ return formatFailureNotice({
46821
+ what: "Fleet Console server did not start.",
46822
+ why: `the server process could not be spawned \u2014 ${input.spawnError}`,
46823
+ next: [
46824
+ "Check that this Node install can run the Console: node --version",
46825
+ "Reinstall if the package is incomplete: npm install -g @dotobokuri/fleet-console"
46826
+ ]
46827
+ });
46771
46828
  }
46772
- spawnBrowser("xdg-open", [url2], { detached: true, stdio: "ignore", windowsHide: true });
46829
+ return formatFailureNotice({
46830
+ what: "Fleet Console server did not start.",
46831
+ why: input.probeError ? `it was started but never answered a health check \u2014 ${input.probeError}` : "it was started but never answered a health check within 3 seconds",
46832
+ next: [
46833
+ `Check that this directory is writable: ${input.dataDir}`,
46834
+ "Show the current state: fleet console status",
46835
+ "Then try again: fleet console restart"
46836
+ ]
46837
+ });
46773
46838
  }
46774
46839
 
46775
46840
  // core/host/health.ts
@@ -52693,7 +52758,8 @@ var SHIM_NAMED_EXPORTS = {
52693
52758
  "@fleet-console/sdk/settings/browser": ["ExperimentalBadge", "SettingsCard", "SettingsField", "SettingsRow", "SettingsSelect", "SettingsToggle", "defineSettingsSection"],
52694
52759
  "@fleet-console/sdk/operations/browser": ["ApiError", "OperationBody", "assertOperationNode", "fetchOperationCatalog", "hasForbiddenBrowserPayloadKey"],
52695
52760
  "@fleet-console/sdk/notifications/browser": ["defineNotificationKind"],
52696
- "@fleet-console/sdk/react/browser": ["PluginErrorBoundary", "React", "Select", "useSelect"]
52761
+ "@fleet-console/sdk/react/browser": ["PluginErrorBoundary", "React", "Select", "useSelect"],
52762
+ "@fleet-console/sdk/components/failure-notice": ["FailureNotice"]
52697
52763
  };
52698
52764
 
52699
52765
  // core/host/plugin-host/plugin-host.ts
@@ -52826,7 +52892,8 @@ var SHIM_DEFINITIONS = [
52826
52892
  { name: "sdk-settings-browser", specifier: "@fleet-console/sdk/settings/browser", globalKey: "@fleet-console/sdk/settings/browser", namedExports: SHIM_NAMED_EXPORTS["@fleet-console/sdk/settings/browser"] ?? [] },
52827
52893
  { name: "sdk-operations-browser", specifier: "@fleet-console/sdk/operations/browser", globalKey: "@fleet-console/sdk/operations/browser", namedExports: SHIM_NAMED_EXPORTS["@fleet-console/sdk/operations/browser"] ?? [] },
52828
52894
  { name: "sdk-notifications-browser", specifier: "@fleet-console/sdk/notifications/browser", globalKey: "@fleet-console/sdk/notifications/browser", namedExports: SHIM_NAMED_EXPORTS["@fleet-console/sdk/notifications/browser"] ?? [] },
52829
- { name: "sdk-react-browser", specifier: "@fleet-console/sdk/react/browser", globalKey: "@fleet-console/sdk/react/browser", namedExports: SHIM_NAMED_EXPORTS["@fleet-console/sdk/react/browser"] ?? [] }
52895
+ { name: "sdk-react-browser", specifier: "@fleet-console/sdk/react/browser", globalKey: "@fleet-console/sdk/react/browser", namedExports: SHIM_NAMED_EXPORTS["@fleet-console/sdk/react/browser"] ?? [] },
52896
+ { name: "sdk-components-failure-notice", specifier: "@fleet-console/sdk/components/failure-notice", globalKey: "@fleet-console/sdk/components/failure-notice", namedExports: SHIM_NAMED_EXPORTS["@fleet-console/sdk/components/failure-notice"] ?? [] }
52830
52897
  ];
52831
52898
  var SHIM_URL_BY_SPECIFIER = new Map(SHIM_DEFINITIONS.map((definition) => [definition.specifier, `/plugin-runtime/shim/${definition.name}.mjs`]));
52832
52899
  var SHIM_BY_NAME = new Map(SHIM_DEFINITIONS.map((definition) => [definition.name, definition]));
@@ -52837,9 +52904,11 @@ var NODE_BUILTINS = /* @__PURE__ */ new Set([
52837
52904
  function createPluginClientAssets(deps) {
52838
52905
  const clientSources = /* @__PURE__ */ new Map();
52839
52906
  const preparedPlugins = /* @__PURE__ */ new Set();
52907
+ const skippedPlugins = /* @__PURE__ */ new Map();
52840
52908
  async function prepare() {
52841
52909
  clientSources.clear();
52842
52910
  preparedPlugins.clear();
52911
+ skippedPlugins.clear();
52843
52912
  for (const plugin of deps.plugins) {
52844
52913
  if (!plugin.external || !plugin.clientEntry) continue;
52845
52914
  try {
@@ -52848,17 +52917,24 @@ function createPluginClientAssets(deps) {
52848
52917
  preparedPlugins.add(plugin.manifest.id);
52849
52918
  } catch (error51) {
52850
52919
  console.warn(`[fleet-console] Plugin ${plugin.manifest.id} client skipped: ${error51 instanceof Error ? error51.message : String(error51)}`);
52920
+ skippedPlugins.set(plugin.manifest.id, {
52921
+ id: plugin.manifest.id,
52922
+ ...plugin.manifest.name ? { name: plugin.manifest.name } : {},
52923
+ reason: error51?.message === "unsupported_client_entry" ? "unsupported_client_entry" : "client_build_failed"
52924
+ });
52851
52925
  }
52852
52926
  }
52853
52927
  }
52854
52928
  function manifest() {
52929
+ const skipped = [...skippedPlugins.values()];
52855
52930
  return {
52856
52931
  plugins: deps.plugins.filter((plugin) => plugin.external && !!plugin.clientEntry && preparedPlugins.has(plugin.manifest.id)).map((plugin) => ({
52857
52932
  id: plugin.manifest.id,
52858
52933
  ...plugin.manifest.name ? { name: plugin.manifest.name } : {},
52859
52934
  clientUrl: `/plugin-runtime/client/${plugin.manifest.id}.mjs`,
52860
52935
  apiVersion: plugin.manifest.apiVersion ?? SDK_API_VERSION
52861
- }))
52936
+ })),
52937
+ ...skipped.length > 0 ? { skipped } : {}
52862
52938
  };
52863
52939
  }
52864
52940
  function getClient(id) {
@@ -56601,9 +56677,11 @@ function createConsoleDaemonLifecycle(deps = {}) {
56601
56677
  const childEnv = env.FLEET_CONSOLE_NO_SYSTEM_CA === "1" ? env : withNodeSystemCa(env);
56602
56678
  const execPath = deps.execPath ?? V2.execPath;
56603
56679
  const serverModulePath = deps.serverModulePath ?? resolveDefaultServerModulePath2();
56680
+ let lastSpawnError = null;
56604
56681
  const spawnDetached = deps.spawnDetached ?? ((bin, args, options) => {
56605
56682
  const child = spawn(bin, [...args], options);
56606
- child.once("error", () => {
56683
+ child.once("error", (error51) => {
56684
+ lastSpawnError = error51 instanceof Error ? error51.message : String(error51);
56607
56685
  });
56608
56686
  child.unref();
56609
56687
  });
@@ -56656,13 +56734,20 @@ function createConsoleDaemonLifecycle(deps = {}) {
56656
56734
  if (typeof probeResult.health?.workspaceCount === "number" && probeResult.health.workspaceCount > 0) return current.endpoint;
56657
56735
  }
56658
56736
  if (current) await stop();
56737
+ lastSpawnError = null;
56659
56738
  spawnDetached(execPath, [serverModulePath, "serve"], withHidden({ detached: true, env: childEnv, stdio: "ignore" }));
56739
+ let lastProbeError = null;
56660
56740
  for (let i = 0; i < 30; i += 1) {
56661
56741
  await sleep(100);
56662
56742
  const next = await probe();
56663
56743
  if (next.healthy && next.lock) return next.lock.endpoint;
56744
+ if (next.error) lastProbeError = next.error;
56664
56745
  }
56665
- throw new Error("Fleet Console server did not become healthy");
56746
+ throw new Error(describeDaemonStartFailure({
56747
+ spawnError: lastSpawnError,
56748
+ probeError: lastProbeError,
56749
+ dataDir: paths.dir
56750
+ }));
56666
56751
  }
56667
56752
  return { ensureDaemon, probe, runServer, stop };
56668
56753
  function readTrustedLock(options = {}) {
@@ -56691,8 +56776,9 @@ async function openFleetConsole(deps = {}) {
56691
56776
  throw new Error("Fleet Console server is not healthy after ensure");
56692
56777
  }
56693
56778
  const url2 = `${status2.lock.endpoint}console/`;
56694
- (deps.openBrowser ?? openBrowser)(url2);
56695
- return { url: url2 };
56779
+ const browser = await (deps.openBrowser ?? openBrowser)(url2);
56780
+ const result = browser ?? { opened: true };
56781
+ return result.opened ? { url: url2, browserOpened: true } : { url: url2, browserOpened: false, browserError: result.reason };
56696
56782
  }
56697
56783
  async function runConsoleStatus(deps = {}) {
56698
56784
  const lifecycle = deps.lifecycle ?? createConsoleDaemonLifecycle();
@@ -56805,14 +56891,14 @@ async function dispatchFleetArgv(argv, options) {
56805
56891
  return 0;
56806
56892
  }
56807
56893
  if (mode === "restart") {
56808
- await runConsoleRestart();
56809
- io.stdout.write(`Fleet Console restarted.
56894
+ const restarted = await runConsoleRestart();
56895
+ io.stdout.write(`${describeConsoleLaunch("Fleet Console restarted.", restarted)}
56810
56896
  ${await runConsoleStatus()}
56811
56897
  `);
56812
56898
  return 0;
56813
56899
  }
56814
- await openFleetConsole();
56815
- io.stdout.write(`Fleet Console opened.
56900
+ const opened = await openFleetConsole();
56901
+ io.stdout.write(`${describeConsoleLaunch("Fleet Console opened.", opened)}
56816
56902
  ${await runConsoleStatus()}
56817
56903
  `);
56818
56904
  return 0;