@dotobokuri/fleet-cli 1.5.0 → 1.5.2

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.
@@ -25,7 +25,6 @@ Confirm each readiness check below before the Workflow. Work through them in ord
25
25
  - [ ] **Common** — objective stated (Mission Anchor), mode-fit holds (Mode Gate), Standing Orders binding. → report `common: ready`
26
26
  - [ ] **Impact radius** — flag public-surface or API impact, irreversibility, and any security-sensitive surface. → report `impact: <…>`
27
27
  - [ ] **Rollback** — identify a rollback-safe checkpoint and any Admiral of the Navy approval point before execution begins. → report `rollback: <…>`
28
- - [ ] **Isolation** — confirm a working branch or worktree isolates the change; no direct work on the default branch. → report `isolation: <branch>`
29
28
  - [ ] **Carrier availability** — confirm the intended carriers are actually exposed and available this session. → report `carriers: <…>`
30
29
  - [ ] **Ownership** — pre-sketch each carrier's file or responsibility boundary. → report `ownership: <…>`
31
30
  - [ ] **Shared resources** — flag shared mutable resources (same files, lock files, or a singleton test environment). → report `shared: <…|none>`
@@ -26,7 +26,6 @@ Confirm each readiness check below before the Workflow. Work through them in ord
26
26
  - [ ] **Doctrine** — enumerate the applicable AGENTS.md files to load for the affected scope. → report `doctrine: <…>`
27
27
  - [ ] **Impact radius** — flag public-surface or API impact, irreversibility, and any security-sensitive surface. → report `impact: <…>`
28
28
  - [ ] **Rollback** — identify a rollback-safe checkpoint and any Admiral of the Navy approval point before execution begins. → report `rollback: <…>`
29
- - [ ] **Isolation** — confirm a working branch or worktree isolates the change; no direct work on the default branch. → report `isolation: <branch>`
30
29
  - [ ] **Escalation** — if multiple carriers or parallel ownership boundaries are required, re-classify under frontline. → report `escalation: clear`
31
30
 
32
31
  ## Workflow
package/dist/index.js CHANGED
@@ -141,8 +141,8 @@ var init_resolve_bin = __esm({
141
141
  // src/release.ts
142
142
  import { createRequire as createRequire2 } from "module";
143
143
  function readFleetCliRelease() {
144
- const requireFromHere = createRequire2(import.meta.url);
145
- const pkg = requireFromHere("../package.json");
144
+ const requireFromHere2 = createRequire2(import.meta.url);
145
+ const pkg = requireFromHere2("../package.json");
146
146
  const version3 = pkg.version ?? "";
147
147
  if (pkg.private === true) {
148
148
  return { channel: "local", version: version3 };
@@ -374,16 +374,58 @@ var init_check = __esm({
374
374
  }
375
375
  });
376
376
 
377
+ // src/update/stop-console.ts
378
+ import { spawn as spawn4 } from "child_process";
379
+ import { createRequire as createRequire3 } from "module";
380
+ async function stopRunningConsoleBeforeUpdate(io) {
381
+ let cliPath;
382
+ try {
383
+ cliPath = requireFromHere.resolve(CONSOLE_CLI_SPECIFIER);
384
+ } catch {
385
+ return;
386
+ }
387
+ io.stdout.write("Stopping the running Fleet Console to release file locks before update...\n");
388
+ await runConsoleStop2(cliPath, io);
389
+ }
390
+ function runConsoleStop2(cliPath, io) {
391
+ return new Promise((resolve2) => {
392
+ let settled = false;
393
+ const child = spawn4(process.execPath, [cliPath, "stop"], { stdio: "ignore" });
394
+ const timer = setTimeout(() => {
395
+ io.stderr.write("Fleet Console did not stop within the timeout; continuing with the update.\n");
396
+ child.kill();
397
+ finish();
398
+ }, STOP_TIMEOUT_MS);
399
+ const finish = () => {
400
+ if (settled) return;
401
+ settled = true;
402
+ clearTimeout(timer);
403
+ resolve2();
404
+ };
405
+ child.on("error", finish);
406
+ child.on("exit", finish);
407
+ });
408
+ }
409
+ var CONSOLE_CLI_SPECIFIER, STOP_TIMEOUT_MS, requireFromHere;
410
+ var init_stop_console = __esm({
411
+ "src/update/stop-console.ts"() {
412
+ "use strict";
413
+ CONSOLE_CLI_SPECIFIER = "@dotobokuri/fleet-console/cli";
414
+ STOP_TIMEOUT_MS = 15e3;
415
+ requireFromHere = createRequire3(import.meta.url);
416
+ }
417
+ });
418
+
377
419
  // src/update/installer.ts
378
420
  var installer_exports = {};
379
421
  __export(installer_exports, {
380
422
  __installerTestHooks: () => __installerTestHooks,
381
423
  runFleetUpdate: () => runFleetUpdate
382
424
  });
383
- import { spawn as spawn4 } from "child_process";
425
+ import { spawn as spawn5 } from "child_process";
384
426
  import { execFileSync } from "child_process";
385
427
  import { accessSync, constants as constants10, realpathSync as realpathSync8 } from "fs";
386
- import { createRequire as createRequire3 } from "module";
428
+ import { createRequire as createRequire4 } from "module";
387
429
  import path25 from "path";
388
430
  async function runFleetUpdate(io) {
389
431
  const release2 = readFleetCliRelease();
@@ -412,6 +454,7 @@ async function runFleetUpdate(io) {
412
454
  io.stdout.write(`Updating Fleet packages with ${target.manager.command} (${versionOrChannel})...
413
455
  `);
414
456
  }
457
+ await stopRunningConsoleBeforeUpdate(io);
415
458
  const status = await installFleetPackages(target.manager, versionOrChannel, io);
416
459
  if (status !== 0) {
417
460
  io.stderr.write(`Fleet update did not complete. You can run this manually:
@@ -469,11 +512,11 @@ function detectGlobalRoot(command3, packageRoot, io) {
469
512
  return void 0;
470
513
  }
471
514
  function getCurrentPackageRoot() {
472
- const requireFromHere = createRequire3(import.meta.url);
515
+ const requireFromHere2 = createRequire4(import.meta.url);
473
516
  for (const candidate of PACKAGE_JSON_CANDIDATES) {
474
517
  try {
475
- const packageJsonPath = requireFromHere.resolve(candidate);
476
- const pkg = requireFromHere(packageJsonPath);
518
+ const packageJsonPath = requireFromHere2.resolve(candidate);
519
+ const pkg = requireFromHere2(packageJsonPath);
477
520
  if (pkg.name === FLEET_CLI_PACKAGE_NAME) {
478
521
  return normalizePath(realpathSync8(path25.dirname(packageJsonPath)));
479
522
  }
@@ -483,7 +526,7 @@ function getCurrentPackageRoot() {
483
526
  return void 0;
484
527
  }
485
528
  function installFleetPackages(manager, versionOrChannel, io) {
486
- const child = spawn4(manager.resolved.bin, [...manager.resolved.prefixArgs, "i", "-g", ...PACKAGE_NAMES.map((name) => `${name}@${versionOrChannel}`)], {
529
+ const child = spawn5(manager.resolved.bin, [...manager.resolved.prefixArgs, "i", "-g", "--force", ...PACKAGE_NAMES.map((name) => `${name}@${versionOrChannel}`)], {
487
530
  stdio: "inherit"
488
531
  });
489
532
  return new Promise((resolve2) => {
@@ -577,6 +620,7 @@ var init_installer = __esm({
577
620
  init_resolve_bin();
578
621
  init_release();
579
622
  init_check();
623
+ init_stop_console();
580
624
  PACKAGE_NAMES = ["@dotobokuri/fleet-cli", "@dotobokuri/fleet-console"];
581
625
  FLEET_CLI_PACKAGE_NAME = "@dotobokuri/fleet-cli";
582
626
  PACKAGE_JSON_CANDIDATES = ["../package.json", "../../package.json"];
@@ -588,10 +632,10 @@ var init_installer = __esm({
588
632
  });
589
633
 
590
634
  // src/index.ts
591
- import { spawn as spawn5 } from "child_process";
635
+ import { spawn as spawn6 } from "child_process";
592
636
  import path26, { dirname as dirname6 } from "path";
593
637
  import { fileURLToPath as fileURLToPath2 } from "url";
594
- import { createRequire as createRequire4 } from "module";
638
+ import { createRequire as createRequire5 } from "module";
595
639
 
596
640
  // ../../packages/fleet-infra/dist/chunk-IUYHSUHV.js
597
641
  init_chunk_UH3Q34DS();
@@ -39165,9 +39209,9 @@ function createConsoleLock(deps = {}) {
39165
39209
  return { ensureLockDir, readLock, writeLock, removeLock, assertLockModes, assertTrustedLock };
39166
39210
  }
39167
39211
  function readFleetConsoleRelease() {
39168
- const requireFromHere = createRequire(import.meta.url);
39169
- const packageJsonPath = requireFromHere.resolve("../package.json");
39170
- const pkg = requireFromHere(packageJsonPath);
39212
+ const requireFromHere2 = createRequire(import.meta.url);
39213
+ const packageJsonPath = requireFromHere2.resolve("../package.json");
39214
+ const pkg = requireFromHere2(packageJsonPath);
39171
39215
  const packageRoot = path9__default.dirname(packageJsonPath);
39172
39216
  const version22 = pkg.version ?? "";
39173
39217
  if (pkg.private === true) {
@@ -39942,10 +39986,10 @@ function mergeDefs2(...defs) {
39942
39986
  function cloneDef2(schema) {
39943
39987
  return mergeDefs2(schema._zod.def);
39944
39988
  }
39945
- function getElementAtPath2(obj, path182) {
39946
- if (!path182)
39989
+ function getElementAtPath2(obj, path192) {
39990
+ if (!path192)
39947
39991
  return obj;
39948
- return path182.reduce((acc, key) => acc?.[key], obj);
39992
+ return path192.reduce((acc, key) => acc?.[key], obj);
39949
39993
  }
39950
39994
  function promiseAllObject2(promisesObj) {
39951
39995
  const keys = Object.keys(promisesObj);
@@ -40354,11 +40398,11 @@ function explicitlyAborted2(x, startIndex = 0) {
40354
40398
  }
40355
40399
  return false;
40356
40400
  }
40357
- function prefixIssues2(path182, issues) {
40401
+ function prefixIssues2(path192, issues) {
40358
40402
  return issues.map((iss) => {
40359
40403
  var _a32;
40360
40404
  (_a32 = iss).path ?? (_a32.path = []);
40361
- iss.path.unshift(path182);
40405
+ iss.path.unshift(path192);
40362
40406
  return iss;
40363
40407
  });
40364
40408
  }
@@ -40503,16 +40547,16 @@ function flattenError2(error512, mapper = (issue22) => issue22.message) {
40503
40547
  }
40504
40548
  function formatError3(error512, mapper = (issue22) => issue22.message) {
40505
40549
  const fieldErrors = { _errors: [] };
40506
- const processError = (error522, path182 = []) => {
40550
+ const processError = (error522, path192 = []) => {
40507
40551
  for (const issue22 of error522.issues) {
40508
40552
  if (issue22.code === "invalid_union" && issue22.errors.length) {
40509
- issue22.errors.map((issues) => processError({ issues }, [...path182, ...issue22.path]));
40553
+ issue22.errors.map((issues) => processError({ issues }, [...path192, ...issue22.path]));
40510
40554
  } else if (issue22.code === "invalid_key") {
40511
- processError({ issues: issue22.issues }, [...path182, ...issue22.path]);
40555
+ processError({ issues: issue22.issues }, [...path192, ...issue22.path]);
40512
40556
  } else if (issue22.code === "invalid_element") {
40513
- processError({ issues: issue22.issues }, [...path182, ...issue22.path]);
40557
+ processError({ issues: issue22.issues }, [...path192, ...issue22.path]);
40514
40558
  } else {
40515
- const fullpath = [...path182, ...issue22.path];
40559
+ const fullpath = [...path192, ...issue22.path];
40516
40560
  if (fullpath.length === 0) {
40517
40561
  fieldErrors._errors.push(mapper(issue22));
40518
40562
  } else {
@@ -40539,17 +40583,17 @@ function formatError3(error512, mapper = (issue22) => issue22.message) {
40539
40583
  }
40540
40584
  function treeifyError2(error512, mapper = (issue22) => issue22.message) {
40541
40585
  const result = { errors: [] };
40542
- const processError = (error522, path182 = []) => {
40586
+ const processError = (error522, path192 = []) => {
40543
40587
  var _a32, _b;
40544
40588
  for (const issue22 of error522.issues) {
40545
40589
  if (issue22.code === "invalid_union" && issue22.errors.length) {
40546
- issue22.errors.map((issues) => processError({ issues }, [...path182, ...issue22.path]));
40590
+ issue22.errors.map((issues) => processError({ issues }, [...path192, ...issue22.path]));
40547
40591
  } else if (issue22.code === "invalid_key") {
40548
- processError({ issues: issue22.issues }, [...path182, ...issue22.path]);
40592
+ processError({ issues: issue22.issues }, [...path192, ...issue22.path]);
40549
40593
  } else if (issue22.code === "invalid_element") {
40550
- processError({ issues: issue22.issues }, [...path182, ...issue22.path]);
40594
+ processError({ issues: issue22.issues }, [...path192, ...issue22.path]);
40551
40595
  } else {
40552
- const fullpath = [...path182, ...issue22.path];
40596
+ const fullpath = [...path192, ...issue22.path];
40553
40597
  if (fullpath.length === 0) {
40554
40598
  result.errors.push(mapper(issue22));
40555
40599
  continue;
@@ -40581,8 +40625,8 @@ function treeifyError2(error512, mapper = (issue22) => issue22.message) {
40581
40625
  }
40582
40626
  function toDotPath2(_path) {
40583
40627
  const segs = [];
40584
- const path182 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
40585
- for (const seg of path182) {
40628
+ const path192 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
40629
+ for (const seg of path192) {
40586
40630
  if (typeof seg === "number")
40587
40631
  segs.push(`[${seg}]`);
40588
40632
  else if (typeof seg === "symbol")
@@ -53103,13 +53147,13 @@ function resolveRef2(ref, ctx) {
53103
53147
  if (!ref.startsWith("#")) {
53104
53148
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
53105
53149
  }
53106
- const path182 = ref.slice(1).split("/").filter(Boolean);
53107
- if (path182.length === 0) {
53150
+ const path192 = ref.slice(1).split("/").filter(Boolean);
53151
+ if (path192.length === 0) {
53108
53152
  return ctx.rootSchema;
53109
53153
  }
53110
53154
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
53111
- if (path182[0] === defsKey) {
53112
- const key = path182[1];
53155
+ if (path192[0] === defsKey) {
53156
+ const key = path192[1];
53113
53157
  if (!key || !ctx.defs[key]) {
53114
53158
  throw new Error(`Reference not found: ${ref}`);
53115
53159
  }
@@ -71608,6 +71652,10 @@ function writeAggregateObserverEvents(req, res, workspaces, store22, resolveWork
71608
71652
  }
71609
71653
  const unsubscribers = options2.subscribeAll ? [
71610
71654
  store22.subscribeAll((event) => {
71655
+ if (isSessionUpdatedEvent(event)) {
71656
+ writeEvent(res, 0, event.type, { session: event.session });
71657
+ return;
71658
+ }
71611
71659
  writeEvent(res, event.id, event.type, { tenant: resolvedWorkspaceSnapshot(resolveWorkspace, event.tenantId), event });
71612
71660
  })
71613
71661
  ] : workspaces.map(
@@ -71630,6 +71678,9 @@ function writeEvent(res, id, event, data) {
71630
71678
 
71631
71679
  `);
71632
71680
  }
71681
+ function isSessionUpdatedEvent(event) {
71682
+ return event.type === "session:updated" && "session" in event;
71683
+ }
71633
71684
  function workspaceSnapshot(workspace) {
71634
71685
  return {
71635
71686
  tenantId: workspace.tenantId,
@@ -71665,6 +71716,7 @@ function createConsoleObservabilityStore(deps = {}) {
71665
71716
  const truncationByTenant = /* @__PURE__ */ new Map();
71666
71717
  const jobsByTenant = /* @__PURE__ */ new Map();
71667
71718
  const terminalSessionsById = /* @__PURE__ */ new Map();
71719
+ const terminalSequenceByTheater = /* @__PURE__ */ new Map();
71668
71720
  const listenersByTenant = /* @__PURE__ */ new Map();
71669
71721
  const allListeners = /* @__PURE__ */ new Set();
71670
71722
  let nextObservedId = 1;
@@ -71828,13 +71880,18 @@ function createConsoleObservabilityStore(deps = {}) {
71828
71880
  function createPendingTerminalSession(input) {
71829
71881
  if (!path9__default.isAbsolute(input.cwd)) throw new Error("Terminal session cwd must be absolute");
71830
71882
  const createdAt = input.createdAt ?? now();
71883
+ const canonicalCwd = canonicalizeTheaterPathSync(input.cwd);
71884
+ const theaterId = workspaceHash(canonicalCwd);
71885
+ const sequence = (terminalSequenceByTheater.get(theaterId) ?? 0) + 1;
71886
+ terminalSequenceByTheater.set(theaterId, sequence);
71831
71887
  const state = {
71832
71888
  sessionId: input.sessionId,
71833
71889
  cwd: input.cwd,
71834
- canonicalCwd: canonicalizeTheaterPathSync(input.cwd),
71890
+ canonicalCwd,
71835
71891
  cwdLabel: path9__default.basename(input.cwd) || input.cwd,
71892
+ sequence,
71836
71893
  createdAt,
71837
- theaterId: workspaceHash(canonicalizeTheaterPathSync(input.cwd)),
71894
+ theaterId,
71838
71895
  terminalSessionId: input.sessionId,
71839
71896
  status: "starting"
71840
71897
  };
@@ -71850,6 +71907,21 @@ function createConsoleObservabilityStore(deps = {}) {
71850
71907
  session.status = status;
71851
71908
  return toTerminalSessionInfo(session);
71852
71909
  }
71910
+ function renameTerminalSession(sessionId, rawLabel) {
71911
+ const session = terminalSessionsById.get(sessionId);
71912
+ if (!session) return null;
71913
+ const label = rawLabel.trim().slice(0, 200);
71914
+ if (label.length === 0) {
71915
+ delete session.label;
71916
+ } else {
71917
+ session.label = label;
71918
+ }
71919
+ return toTerminalSessionInfo(session);
71920
+ }
71921
+ function notifySessionUpdated(session) {
71922
+ const event = { type: "session:updated", session };
71923
+ for (const listener of allListeners) listener(event);
71924
+ }
71853
71925
  function removeTerminalSession(sessionId) {
71854
71926
  return terminalSessionsById.delete(sessionId);
71855
71927
  }
@@ -71861,6 +71933,7 @@ function createConsoleObservabilityStore(deps = {}) {
71861
71933
  truncationByTenant.clear();
71862
71934
  jobsByTenant.clear();
71863
71935
  terminalSessionsById.clear();
71936
+ terminalSequenceByTheater.clear();
71864
71937
  listenersByTenant.clear();
71865
71938
  allListeners.clear();
71866
71939
  }
@@ -71880,6 +71953,8 @@ function createConsoleObservabilityStore(deps = {}) {
71880
71953
  pushEvents,
71881
71954
  register,
71882
71955
  createPendingTerminalSession,
71956
+ notifySessionUpdated,
71957
+ renameTerminalSession,
71883
71958
  subscribe,
71884
71959
  subscribeAll,
71885
71960
  updateTerminalSessionStatus,
@@ -71961,6 +72036,8 @@ function toTerminalSessionInfo(state) {
71961
72036
  sessionId: state.sessionId,
71962
72037
  terminalSessionId: state.terminalSessionId,
71963
72038
  cwdLabel: state.cwdLabel,
72039
+ sequence: state.sequence,
72040
+ label: state.label,
71964
72041
  status: state.status,
71965
72042
  createdAt: state.createdAt,
71966
72043
  theaterId: state.theaterId,
@@ -72290,6 +72367,75 @@ async function runNativeCommand(bin, args) {
72290
72367
  const result = await execFileAsync(bin, [...args], { timeout: DIALOG_TIMEOUT_MS });
72291
72368
  return { stdout: result.stdout, stderr: result.stderr };
72292
72369
  }
72370
+ var DEFAULT_WINDOWS_PATHEXT2 = ".COM;.EXE;.BAT;.CMD";
72371
+ var WINDOWS_PATH_SEPARATOR2 = ";";
72372
+ var WINDOWS_SHIM_EXTENSIONS2 = /* @__PURE__ */ new Set([".cmd", ".bat"]);
72373
+ var CMD_EXPANSION_SENSITIVE_PATTERN2 = /[%^]/;
72374
+ function resolvePathBinary2(command22, env, options2 = {}) {
72375
+ const platform2 = options2.platform ?? process.platform;
72376
+ const isWindows22 = platform2 === "win32";
72377
+ const pathValue = isWindows22 ? env.Path ?? env.PATH ?? "" : env.PATH ?? "";
72378
+ const pathExts = isWindows22 ? parsePathExt2(env.PATHEXT) : [""];
72379
+ const resolved = findOnPath2(command22, pathValue, pathExts, platform2, options2.exists ?? fs4__default.existsSync);
72380
+ return resolved ? wrapWindowsShim2(resolved, env, platform2) : void 0;
72381
+ }
72382
+ function wrapWindowsShim2(resolved, env, platform2) {
72383
+ if (platform2 !== "win32" || !WINDOWS_SHIM_EXTENSIONS2.has(path9__default.extname(resolved).toLowerCase())) {
72384
+ return { bin: resolved, prefixArgs: [] };
72385
+ }
72386
+ rejectCmdExpansionSensitiveShim2(resolved);
72387
+ return {
72388
+ bin: env.ComSpec ?? "cmd.exe",
72389
+ prefixArgs: ["/d", "/s", "/c", "call", forceWindowsArgQuoting2(resolved)]
72390
+ };
72391
+ }
72392
+ function rejectCmdExpansionSensitiveShim2(resolved) {
72393
+ if (CMD_EXPANSION_SENSITIVE_PATTERN2.test(resolved)) {
72394
+ throw new Error(`Refusing to run Windows shim path with cmd.exe expansion-sensitive characters (% or ^): ${resolved}`);
72395
+ }
72396
+ }
72397
+ function forceWindowsArgQuoting2(value) {
72398
+ return `${value} `;
72399
+ }
72400
+ function findOnPath2(bin, pathValue, pathExts, platform2, exists) {
72401
+ const pathSeparator = platform2 === "win32" ? WINDOWS_PATH_SEPARATOR2 : path9__default.delimiter;
72402
+ if (hasPathSeparator2(bin, platform2) || path9__default.isAbsolute(bin)) {
72403
+ return resolveWithExtensions2(bin, pathExts, platform2, exists);
72404
+ }
72405
+ for (const entry of pathValue.split(pathSeparator)) {
72406
+ const searchDir = entry.length === 0 ? "." : entry;
72407
+ const candidate = resolveWithExtensions2(path9__default.join(searchDir, bin), pathExts, platform2, exists);
72408
+ if (candidate) {
72409
+ return candidate;
72410
+ }
72411
+ }
72412
+ return void 0;
72413
+ }
72414
+ function resolveWithExtensions2(candidate, pathExts, platform2, exists) {
72415
+ if (platform2 !== "win32" || path9__default.extname(candidate).length > 0) {
72416
+ return exists(candidate) ? candidate : void 0;
72417
+ }
72418
+ for (const ext of pathExts) {
72419
+ if (ext.length === 0) {
72420
+ continue;
72421
+ }
72422
+ const withExt = `${candidate}${ext}`;
72423
+ if (exists(withExt)) {
72424
+ return withExt;
72425
+ }
72426
+ }
72427
+ return exists(candidate) ? candidate : void 0;
72428
+ }
72429
+ function hasPathSeparator2(value, platform2) {
72430
+ if (value.includes("/")) {
72431
+ return true;
72432
+ }
72433
+ return platform2 === "win32" && value.includes("\\");
72434
+ }
72435
+ function parsePathExt2(pathExt) {
72436
+ const raw = pathExt && pathExt.trim().length > 0 ? pathExt : DEFAULT_WINDOWS_PATHEXT2;
72437
+ return raw.split(WINDOWS_PATH_SEPARATOR2).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
72438
+ }
72293
72439
  var DEFAULT_TERMINAL_CWD_FALLBACK = os22__default.homedir;
72294
72440
  var TERMINAL_TERM = "xterm-256color";
72295
72441
  var FLEET_HEADLESS_NATIVE_FLAGS = ["--headless", "--native"];
@@ -72300,16 +72446,26 @@ function createDefaultTerminalLaunchResolver(deps = {}) {
72300
72446
  const execPath = deps.execPath ?? process4.execPath;
72301
72447
  const homedir22 = deps.homedir ?? DEFAULT_TERMINAL_CWD_FALLBACK;
72302
72448
  const exists = deps.exists ?? fs4__default.existsSync;
72449
+ const platform2 = deps.platform ?? process4.platform;
72303
72450
  const release2 = deps.release ?? readFleetConsoleRelease();
72304
72451
  return (selectedCwd, context) => {
72305
72452
  const cwd = selectedCwd || baseCwd || homedir22();
72306
72453
  if (context?.kind === "shell") {
72307
- return { bin: resolveUserShell(env), args: [], cwd, env: buildShellLaunchEnv(env) };
72454
+ const shell = resolveWindowsLaunchBinary(resolveUserShell(env, platform2), [], env, exists, platform2, "user shell");
72455
+ return { bin: shell.bin, args: shell.args, cwd, env: buildShellLaunchEnv(env) };
72308
72456
  }
72309
72457
  const launchEnv = buildLaunchEnv(env, cwd, context?.sessionId);
72310
72458
  const override = parseTerminalCommand(env.FLEET_TERMINAL_CMD);
72311
72459
  if (override) {
72312
- return { ...override, cwd, env: launchEnv };
72460
+ const resolvedOverride = resolveWindowsLaunchBinary(
72461
+ override.bin,
72462
+ override.args,
72463
+ env,
72464
+ exists,
72465
+ platform2,
72466
+ "FLEET_TERMINAL_CMD"
72467
+ );
72468
+ return { ...resolvedOverride, cwd, env: launchEnv };
72313
72469
  }
72314
72470
  if (release2.channel === "local") {
72315
72471
  const localCli = resolveSiblingFleetCliEntry(release2.packageRoot, exists);
@@ -72320,7 +72476,15 @@ function createDefaultTerminalLaunchResolver(deps = {}) {
72320
72476
  }
72321
72477
  return { bin: execPath, args: [localCli, ...FLEET_HEADLESS_NATIVE_FLAGS], cwd, env: launchEnv };
72322
72478
  }
72323
- return { bin: "fleet", args: [...FLEET_HEADLESS_NATIVE_FLAGS], cwd, env: launchEnv };
72479
+ const stableFleet = resolveWindowsLaunchBinary(
72480
+ "fleet",
72481
+ [...FLEET_HEADLESS_NATIVE_FLAGS],
72482
+ env,
72483
+ exists,
72484
+ platform2,
72485
+ "fleet stable terminal binary"
72486
+ );
72487
+ return { ...stableFleet, cwd, env: launchEnv };
72324
72488
  };
72325
72489
  }
72326
72490
  function startTerminalShell(launch, size) {
@@ -72333,11 +72497,21 @@ function startTerminalShell(launch, size) {
72333
72497
  name: TERMINAL_TERM
72334
72498
  });
72335
72499
  }
72336
- function resolveUserShell(env) {
72500
+ function resolveUserShell(env, platform2) {
72337
72501
  if (env.SHELL) return env.SHELL;
72338
- if (process4.platform === "win32") return env.ComSpec || "powershell.exe";
72502
+ if (platform2 === "win32") return env.ComSpec || "powershell.exe";
72339
72503
  return "/bin/bash";
72340
72504
  }
72505
+ function resolveWindowsLaunchBinary(bin, args, env, exists, platform2, label) {
72506
+ if (platform2 !== "win32") {
72507
+ return { bin, args };
72508
+ }
72509
+ const resolved = resolvePathBinary2(bin, env, { exists, platform: platform2 });
72510
+ if (!resolved) {
72511
+ throw new Error(`${label} "${bin}" was not found on PATH; provide an absolute path or install it before launching a terminal session.`);
72512
+ }
72513
+ return { bin: resolved.bin, args: [...resolved.prefixArgs, ...args] };
72514
+ }
72341
72515
  function resolveSiblingFleetCliEntry(consolePackageRoot, exists = fs4__default.existsSync) {
72342
72516
  const candidate = expectedSiblingFleetCliEntry(consolePackageRoot);
72343
72517
  return exists(candidate) ? candidate : null;
@@ -72367,15 +72541,11 @@ function buildShellLaunchEnv(env) {
72367
72541
  }
72368
72542
  var DEFAULT_COLS = 80;
72369
72543
  var DEFAULT_ROWS2 = 24;
72370
- var DEFAULT_GRACE_MS = 18e4;
72371
72544
  var DEFAULT_SCROLLBACK_LIMIT = 512;
72372
72545
  var WS_OPEN_STATE = 1;
72373
72546
  function createTerminalSessionManager(deps) {
72374
72547
  const startShell2 = deps.startShell ?? startTerminalShell;
72375
- const graceMs = deps.graceMs ?? DEFAULT_GRACE_MS;
72376
72548
  const scrollbackLimit = deps.scrollbackLimit ?? DEFAULT_SCROLLBACK_LIMIT;
72377
- const setTimeoutImpl = deps.setTimeout ?? setTimeout;
72378
- const clearTimeoutImpl = deps.clearTimeout ?? clearTimeout;
72379
72549
  const sessions = /* @__PURE__ */ new Map();
72380
72550
  function canAttach() {
72381
72551
  return true;
@@ -72385,10 +72555,6 @@ function createTerminalSessionManager(deps) {
72385
72555
  if (session.activeSocket && session.activeSocket !== socket) {
72386
72556
  session.activeSocket.close(4e3, "terminal_replaced");
72387
72557
  }
72388
- if (session.graceTimer) {
72389
- clearTimeoutImpl(session.graceTimer);
72390
- session.graceTimer = null;
72391
- }
72392
72558
  session.activeSocket = socket;
72393
72559
  session.pty.resize(session.cols, session.rows);
72394
72560
  replayScrollback(session, socket);
@@ -72420,7 +72586,6 @@ function createTerminalSessionManager(deps) {
72420
72586
  disposables: [],
72421
72587
  scrollback: [],
72422
72588
  activeSocket: null,
72423
- graceTimer: null,
72424
72589
  cols: DEFAULT_COLS,
72425
72590
  rows: DEFAULT_ROWS2
72426
72591
  };
@@ -72460,7 +72625,6 @@ function createTerminalSessionManager(deps) {
72460
72625
  function detachSocket(session, socket) {
72461
72626
  if (session.activeSocket !== socket) return;
72462
72627
  session.activeSocket = null;
72463
- session.graceTimer = setTimeoutImpl(() => removeSession(session), graceMs);
72464
72628
  }
72465
72629
  function replayScrollback(session, socket) {
72466
72630
  for (const chunk of session.scrollback) {
@@ -72476,10 +72640,6 @@ function createTerminalSessionManager(deps) {
72476
72640
  }
72477
72641
  function killSession(session, options2 = {}) {
72478
72642
  const killPty = options2.killPty ?? true;
72479
- if (session.graceTimer) {
72480
- clearTimeoutImpl(session.graceTimer);
72481
- session.graceTimer = null;
72482
- }
72483
72643
  session.activeSocket?.close(4001, "terminal_closed");
72484
72644
  session.activeSocket = null;
72485
72645
  for (const disposable of session.disposables) disposable.dispose();
@@ -72619,7 +72779,7 @@ var MAX_BODY_BYTES = 1024 * 1024;
72619
72779
  function createConsoleServer(deps = {}) {
72620
72780
  const host = deps.host ?? DEFAULT_HOST2;
72621
72781
  const port = deps.port ?? DEFAULT_PORT2;
72622
- const version22 = deps.version ?? (typeof __PKG_VERSION__ === "string" ? __PKG_VERSION__ : "0.0.0-dev");
72782
+ const version22 = deps.version ?? readFleetConsoleRelease().version;
72623
72783
  const channel = readConsoleChannel();
72624
72784
  const carrierRegistry = createCarrierRegistry2();
72625
72785
  registerDefaultCarriers2(carrierRegistry);
@@ -72639,7 +72799,6 @@ function createConsoleServer(deps = {}) {
72639
72799
  const terminalSessions = createTerminalSessionManager({
72640
72800
  launch: deps.terminalLaunch ?? createDefaultTerminalLaunchResolver(),
72641
72801
  startShell: deps.terminalStartShell,
72642
- graceMs: deps.terminalGraceMs,
72643
72802
  maxSessions: deps.maxTerminalSessions,
72644
72803
  // PTY가 종료되면(예: fleet-cli 종료) 콘솔 세션 목록에서도 제거해 잔존/재실행을 막는다.
72645
72804
  onSessionExit: (sessionId) => observability.removeTerminalSession(sessionId)
@@ -72700,7 +72859,7 @@ function createConsoleServer(deps = {}) {
72700
72859
  }
72701
72860
  const terminalSessionItemMatch = pathname.match(/^\/terminal\/sessions\/([^/]+)$/);
72702
72861
  if (terminalSessionItemMatch) {
72703
- handleTerminalSessionItem(req, res, decodeURIComponent(terminalSessionItemMatch[1] ?? ""));
72862
+ runAsyncHandler(handleTerminalSessionItem(req, res, decodeURIComponent(terminalSessionItemMatch[1] ?? "")), res);
72704
72863
  return;
72705
72864
  }
72706
72865
  if (pathname === "/observer/theaters") {
@@ -72896,8 +73055,8 @@ function createConsoleServer(deps = {}) {
72896
73055
  }
72897
73056
  await createTerminalSessionForCwd(cwd, res);
72898
73057
  }
72899
- function handleTerminalSessionItem(req, res, sessionId) {
72900
- if (req.method !== "DELETE") {
73058
+ async function handleTerminalSessionItem(req, res, sessionId) {
73059
+ if (req.method !== "DELETE" && req.method !== "PATCH") {
72901
73060
  writeJson(res, 405, { error: "Method not allowed" });
72902
73061
  return;
72903
73062
  }
@@ -72905,6 +73064,21 @@ function createConsoleServer(deps = {}) {
72905
73064
  writeJson(res, 401, { error: "unauthorized" });
72906
73065
  return;
72907
73066
  }
73067
+ if (req.method === "PATCH") {
73068
+ const body = await readJsonBody(req);
73069
+ if (!body || body.label !== void 0 && typeof body.label !== "string") {
73070
+ writeJson(res, 400, { error: "invalid_session_label" });
73071
+ return;
73072
+ }
73073
+ const updated = observability.renameTerminalSession(sessionId, body.label ?? "");
73074
+ if (!updated) {
73075
+ writeJson(res, 404, { error: "session_not_found" });
73076
+ return;
73077
+ }
73078
+ observability.notifySessionUpdated(updated);
73079
+ writeJson(res, 200, updated);
73080
+ return;
73081
+ }
72908
73082
  terminalSessions.terminate(sessionId);
72909
73083
  observability.removeTerminalSession(sessionId);
72910
73084
  writeJson(res, 200, { ok: true });
@@ -83117,7 +83291,7 @@ async function dispatchUpdateCommand(argv2, io) {
83117
83291
 
83118
83292
  // src/index.ts
83119
83293
  var HELP_HINT2 = "Run 'fleet --help' for usage.";
83120
- var require4 = createRequire4(import.meta.url);
83294
+ var require4 = createRequire5(import.meta.url);
83121
83295
  var FLEET_ENTRY_PATH = fileURLToPath2(import.meta.url);
83122
83296
  var PLUGIN_ASSETS_DIR = path26.join(dirname6(dirname6(FLEET_ENTRY_PATH)), "assets");
83123
83297
  var PLUGIN_TSX_LOADER_PATH = resolveOptionalPackage("tsx");
@@ -83209,7 +83383,7 @@ function resolveOptionalPackage(id) {
83209
83383
  }
83210
83384
  async function relayToPackageCli(specifier, args) {
83211
83385
  const cliPath = require4.resolve(specifier);
83212
- const child = spawn5(process.execPath, [cliPath, ...args], {
83386
+ const child = spawn6(process.execPath, [cliPath, ...args], {
83213
83387
  stdio: "inherit",
83214
83388
  cwd: process.env.INIT_CWD || process.cwd()
83215
83389
  });