@wrongstack/cli 0.296.4 → 0.297.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 (81) hide show
  1. package/dist/boot/dispatch-webui.d.ts +7 -0
  2. package/dist/boot/dispatch-webui.d.ts.map +1 -1
  3. package/dist/boot/launch-banner.d.ts +22 -0
  4. package/dist/boot/launch-banner.d.ts.map +1 -0
  5. package/dist/boot/launch-menu.d.ts.map +1 -1
  6. package/dist/boot/tui-settings-adapter.d.ts.map +1 -1
  7. package/dist/{chunk-OI7GWJDS.js → chunk-7JABKXET.js} +372 -10
  8. package/dist/chunk-7JABKXET.js.map +7 -0
  9. package/dist/{chunk-WY7KKOUK.js → chunk-JW75HY4F.js} +31 -10
  10. package/dist/chunk-JW75HY4F.js.map +7 -0
  11. package/dist/{chunk-Z7YGASQP.js → chunk-QZKWRNIP.js} +19 -18
  12. package/dist/chunk-QZKWRNIP.js.map +7 -0
  13. package/dist/{chunk-BQCAJ7FI.js → chunk-XTIXVJXP.js} +16 -3
  14. package/dist/chunk-XTIXVJXP.js.map +7 -0
  15. package/dist/{cli-main-TOAWVJDY.js → cli-main-GOY5YGVL.js} +25199 -25107
  16. package/dist/cli-main-GOY5YGVL.js.map +7 -0
  17. package/dist/cli-main.d.ts.map +1 -1
  18. package/dist/{execution-O7I5DYE6.js → execution-A4PGK4XV.js} +39 -9
  19. package/dist/execution-A4PGK4XV.js.map +7 -0
  20. package/dist/execution.d.ts.map +1 -1
  21. package/dist/fleet-statusline.d.ts.map +1 -1
  22. package/dist/goal-host.d.ts +1 -1
  23. package/dist/goal-host.d.ts.map +1 -1
  24. package/dist/{hq-JUYKUNJX.js → hq-GBPGXIR5.js} +2 -2
  25. package/dist/hq-publisher.d.ts +3 -2
  26. package/dist/hq-publisher.d.ts.map +1 -1
  27. package/dist/hq-server/types.d.ts +8 -0
  28. package/dist/hq-server/types.d.ts.map +1 -1
  29. package/dist/hq-server/ws.d.ts +61 -2
  30. package/dist/hq-server/ws.d.ts.map +1 -1
  31. package/dist/{hq-server-6AQ3BXZA.js → hq-server-YQUUMTPU.js} +2 -2
  32. package/dist/index.js +293 -26
  33. package/dist/index.js.map +4 -4
  34. package/dist/kanban-hq-sync.d.ts +13 -1
  35. package/dist/kanban-hq-sync.d.ts.map +1 -1
  36. package/dist/live-settings-input.d.ts +9 -0
  37. package/dist/live-settings-input.d.ts.map +1 -1
  38. package/dist/{mailbox-serve-2UWHEN3M.js → mailbox-serve-DBOOOOWZ.js} +50 -8
  39. package/dist/mailbox-serve-DBOOOOWZ.js.map +7 -0
  40. package/dist/{sdd-runtime-JG4EEKL6.js → sdd-runtime-AIJCJGWC.js} +2 -2
  41. package/dist/services/sdd/state.d.ts +3 -3
  42. package/dist/services/sdd/state.d.ts.map +1 -1
  43. package/dist/services/sdd/task-manager.d.ts.map +1 -1
  44. package/dist/session-stats.d.ts.map +1 -1
  45. package/dist/settings-menu.d.ts.map +1 -1
  46. package/dist/slash-commands/command-context.d.ts +2 -0
  47. package/dist/slash-commands/command-context.d.ts.map +1 -1
  48. package/dist/slash-commands/goal.d.ts +1 -1
  49. package/dist/slash-commands/goal.d.ts.map +1 -1
  50. package/dist/slash-commands/sdd.d.ts.map +1 -1
  51. package/dist/webui-server/kanban-host-adapter.d.ts +1 -1
  52. package/dist/webui-server/kanban-host-adapter.d.ts.map +1 -1
  53. package/dist/webui-server/session-start-payload.d.ts +4 -0
  54. package/dist/webui-server/session-start-payload.d.ts.map +1 -1
  55. package/dist/{webui-server-ZF5KVGC5.js → webui-server-XKFUOKMJ.js} +155 -132
  56. package/dist/webui-server-XKFUOKMJ.js.map +7 -0
  57. package/dist/webui-server-options.d.ts +7 -0
  58. package/dist/webui-server-options.d.ts.map +1 -1
  59. package/dist/webui-server.d.ts.map +1 -1
  60. package/dist/wiring/command-host-state.d.ts.map +1 -1
  61. package/dist/wiring/hq-telemetry.d.ts +4 -3
  62. package/dist/wiring/hq-telemetry.d.ts.map +1 -1
  63. package/dist/wiring/lifecycle-plugins.d.ts +5 -8
  64. package/dist/wiring/lifecycle-plugins.d.ts.map +1 -1
  65. package/dist/wiring/metrics.d.ts +1 -1
  66. package/dist/wiring/metrics.d.ts.map +1 -1
  67. package/dist/wiring/runtime-lifecycle-deps.d.ts.map +1 -1
  68. package/dist/wiring/sage.d.ts.map +1 -1
  69. package/dist/wiring/sdd-handlers.d.ts.map +1 -1
  70. package/package.json +22 -22
  71. package/dist/chunk-BQCAJ7FI.js.map +0 -7
  72. package/dist/chunk-OI7GWJDS.js.map +0 -7
  73. package/dist/chunk-WY7KKOUK.js.map +0 -7
  74. package/dist/chunk-Z7YGASQP.js.map +0 -7
  75. package/dist/cli-main-TOAWVJDY.js.map +0 -7
  76. package/dist/execution-O7I5DYE6.js.map +0 -7
  77. package/dist/mailbox-serve-2UWHEN3M.js.map +0 -7
  78. package/dist/webui-server-ZF5KVGC5.js.map +0 -7
  79. /package/dist/{hq-JUYKUNJX.js.map → hq-GBPGXIR5.js.map} +0 -0
  80. /package/dist/{hq-server-6AQ3BXZA.js.map → hq-server-YQUUMTPU.js.map} +0 -0
  81. /package/dist/{sdd-runtime-JG4EEKL6.js.map → sdd-runtime-AIJCJGWC.js.map} +0 -0
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-OP47B3IX.js";
7
7
  import {
8
8
  startCliHqConnection
9
- } from "./chunk-WY7KKOUK.js";
9
+ } from "./chunk-JW75HY4F.js";
10
10
  import {
11
11
  loadConfigProviders,
12
12
  mutateConfigProviders
@@ -17,12 +17,15 @@ import "./chunk-7OCVIDC7.js";
17
17
  import { findPackageJSON } from "node:module";
18
18
  import * as os from "node:os";
19
19
  import * as path5 from "node:path";
20
- import { DefaultSecretScrubber } from "@wrongstack/core/security";
21
- import { PromptUsageStore, watchProviderConfig } from "@wrongstack/core/storage";
22
- import { resolveWstackPaths as resolveWstackPaths2, wstackGlobalRoot } from "@wrongstack/core/utils";
23
20
  import { TOKENS } from "@wrongstack/core/kernel";
24
- import { createCompatibilityTrustBoundary } from "@wrongstack/core/security";
21
+ import { createCompatibilityTrustBoundary, DefaultSecretScrubber } from "@wrongstack/core/security";
25
22
  import { SkillInstaller } from "@wrongstack/core/skills";
23
+ import { PromptUsageStore, watchProviderConfig } from "@wrongstack/core/storage";
24
+ import {
25
+ resolveWstackPaths as resolveWstackPaths2,
26
+ startSharedHeapWatchdog,
27
+ wstackGlobalRoot
28
+ } from "@wrongstack/core/utils";
26
29
  import { toErrorMessage } from "@wrongstack/core/utils/error";
27
30
  import { makeProviderFromConfig } from "@wrongstack/providers";
28
31
  import {
@@ -172,6 +175,7 @@ function createConnectionHandler(deps) {
172
175
  import { PhaseGraphBuilder } from "@wrongstack/core/goal";
173
176
  import { resolveWstackPaths } from "@wrongstack/core/utils";
174
177
  import { exportBoardToTaskGraph } from "@wrongstack/kanban";
178
+ import { TaskGraphStore } from "@wrongstack/sdd";
175
179
  import {
176
180
  startSddRunFromGraph
177
181
  } from "@wrongstack/webui-server";
@@ -301,6 +305,8 @@ function createCliKanbanHostRoutes(deps) {
301
305
  return failRun("SDD runs need the multi-agent host, which is not available here.");
302
306
  }
303
307
  const paths = resolveWstackPaths({ projectRoot });
308
+ const taskStore = new TaskGraphStore({ baseDir: paths.projectTaskGraphs });
309
+ await taskStore.save(exported.graph);
304
310
  const handle = await startSddRunFromGraph(
305
311
  exported.graph,
306
312
  {
@@ -309,6 +315,7 @@ function createCliKanbanHostRoutes(deps) {
309
315
  projectRoot,
310
316
  subagentFactory: opts.sddSubagentFactory,
311
317
  projectSddBoards: paths.projectSddBoards,
318
+ taskStore,
312
319
  ...opts.brain ? { brain: opts.brain } : {},
313
320
  ...runIsolatedTurn ? { runIsolatedTurn } : {}
314
321
  },
@@ -347,6 +354,52 @@ import {
347
354
  registerWebuiSignalHandlers
348
355
  } from "@wrongstack/webui-server";
349
356
 
357
+ // src/webui-server/listen-helpers.ts
358
+ import * as http from "node:http";
359
+ async function startDeferredHttpListen(args) {
360
+ const { server, host, httpPort, logger } = args;
361
+ await new Promise((resolveListen, rejectListen) => {
362
+ server.listen(httpPort, host, () => resolveListen());
363
+ server.once("error", rejectListen);
364
+ });
365
+ server.on("error", (err) => {
366
+ logger.error("http_server_error", {
367
+ message: err.message,
368
+ port: httpPort
369
+ });
370
+ });
371
+ }
372
+ async function startIpv6LoopbackProxy(args) {
373
+ const { primary, httpPort, logger } = args;
374
+ const v6 = http.createServer();
375
+ v6.on("request", (req, res) => primary.emit("request", req, res));
376
+ v6.on("upgrade", (req, socket, head) => primary.emit("upgrade", req, socket, head));
377
+ const logIpv6Error = (err) => {
378
+ if (err.code === "EAFNOSUPPORT" || err.code === "EADDRNOTAVAIL" || err.code === "EADDRINUSE") {
379
+ logger.warn("ipv6_loopback_unavailable", { code: err.code, port: httpPort });
380
+ return;
381
+ }
382
+ logger.error("http_server_error", { message: err.message, port: httpPort });
383
+ };
384
+ v6.on("error", logIpv6Error);
385
+ const ipv6Listening = await new Promise((resolveListen) => {
386
+ const onListening = () => {
387
+ v6.off("error", onInitialError);
388
+ resolveListen(true);
389
+ };
390
+ const onInitialError = () => {
391
+ v6.off("listening", onListening);
392
+ resolveListen(false);
393
+ };
394
+ v6.once("listening", onListening);
395
+ v6.once("error", onInitialError);
396
+ v6.listen(httpPort, "::1");
397
+ });
398
+ if (!ipv6Listening) return null;
399
+ console.log(`[WebUI] Also listening on http://[::1]:${httpPort}`);
400
+ return v6;
401
+ }
402
+
350
403
  // src/webui-server/logger-shim.ts
351
404
  var structuredLine = (level, message) => JSON.stringify({
352
405
  level,
@@ -376,9 +429,63 @@ var consoleLogger = {
376
429
  }
377
430
  };
378
431
 
432
+ // src/webui-server/node-pty-loader.ts
433
+ import { existsSync } from "node:fs";
434
+ import { createRequire } from "node:module";
435
+ import * as path from "node:path";
436
+ import { fileURLToPath } from "node:url";
437
+ function createNodePtyLoader(importMetaUrl, logger) {
438
+ const requireFromCli = createRequire(importMetaUrl);
439
+ let cachedNodePty;
440
+ return () => {
441
+ if (cachedNodePty !== void 0) return cachedNodePty;
442
+ try {
443
+ const webuiEntry = requireFromCli.resolve("@wrongstack/webui");
444
+ const webuiDir = webuiEntry.replace(/[\\/]dist.*$/, "");
445
+ const webuiPkgJson = path.join(webuiDir, "package.json");
446
+ cachedNodePty = createRequire(webuiPkgJson)("node-pty");
447
+ if (cachedNodePty) {
448
+ logger.debug?.(`[terminal] node-pty loaded via webui package (${webuiDir})`);
449
+ return cachedNodePty;
450
+ }
451
+ } catch (err) {
452
+ logger.debug?.(`[terminal] webui-route failed: ${err.message}`);
453
+ }
454
+ try {
455
+ cachedNodePty = requireFromCli("node-pty");
456
+ logger.debug?.("[terminal] node-pty loaded via direct requireFromCli");
457
+ return cachedNodePty;
458
+ } catch (err) {
459
+ logger.debug?.(`[terminal] direct require failed: ${err.message}`);
460
+ }
461
+ try {
462
+ const cliPkgJson = path.join(path.dirname(fileURLToPath(importMetaUrl)), "..", "package.json");
463
+ let dir = path.dirname(cliPkgJson);
464
+ for (let i = 0; i < 6; i++) {
465
+ const candidate = path.join(dir, "node_modules", "node-pty");
466
+ if (existsSync(candidate)) {
467
+ cachedNodePty = createRequire(candidate)("node-pty");
468
+ if (cachedNodePty) {
469
+ logger.debug?.(`[terminal] node-pty loaded via workspace root (${candidate})`);
470
+ return cachedNodePty;
471
+ }
472
+ }
473
+ const parent = path.dirname(dir);
474
+ if (parent === dir) break;
475
+ dir = parent;
476
+ }
477
+ } catch (err) {
478
+ logger.debug?.(`[terminal] workspace-root walk failed: ${err.message}`);
479
+ }
480
+ cachedNodePty = null;
481
+ logger.debug?.('[terminal] node-pty resolution failed; terminal panel will report "unavailable"');
482
+ return cachedNodePty;
483
+ };
484
+ }
485
+
379
486
  // src/webui-server/prefs-seeding.ts
380
487
  import * as fs from "node:fs/promises";
381
- import * as path from "node:path";
488
+ import * as path2 from "node:path";
382
489
  import { DefaultSecretVault } from "@wrongstack/core/security";
383
490
  import {
384
491
  PREF_KEYS,
@@ -434,8 +541,8 @@ function createPrefsSeeding(opts) {
434
541
  patchLiveAppConfig({ modelMatrix: payload["modelMatrix"] });
435
542
  }
436
543
  if (!opts.profileConfigPath) return;
437
- const globalRoot = opts.globalConfigPath ? path.dirname(opts.globalConfigPath) : path.dirname(opts.profileConfigPath);
438
- const vault = new DefaultSecretVault({ keyFile: path.join(globalRoot, ".key") });
544
+ const globalRoot = opts.globalConfigPath ? path2.dirname(opts.globalConfigPath) : path2.dirname(opts.profileConfigPath);
545
+ const vault = new DefaultSecretVault({ keyFile: path2.join(globalRoot, ".key") });
439
546
  await persistPrefsToConfig(
440
547
  {
441
548
  profileConfigPath: opts.profileConfigPath,
@@ -464,13 +571,13 @@ function createPrefsSeeding(opts) {
464
571
  }
465
572
 
466
573
  // src/webui-server/provider-config.ts
467
- import * as path2 from "node:path";
574
+ import * as path3 from "node:path";
468
575
  import { DefaultSecretVault as DefaultSecretVault2 } from "@wrongstack/core/security";
469
576
  function getVault(globalConfigPath) {
470
- const configDir = path2.dirname(globalConfigPath ?? "");
471
- const parentDir = path2.dirname(configDir);
472
- const globalRoot = path2.basename(parentDir) === "profiles" ? path2.dirname(parentDir) : configDir;
473
- const keyFile = path2.join(globalRoot, ".key");
577
+ const configDir = path3.dirname(globalConfigPath ?? "");
578
+ const parentDir = path3.dirname(configDir);
579
+ const globalRoot = path3.basename(parentDir) === "profiles" ? path3.dirname(parentDir) : configDir;
580
+ const keyFile = path3.join(globalRoot, ".key");
474
581
  return new DefaultSecretVault2({ keyFile });
475
582
  }
476
583
  async function loadSavedProviders(globalConfigPath) {
@@ -496,7 +603,7 @@ function createProviderConfigStore(globalConfigPath, configProvidersRef) {
496
603
  }
497
604
 
498
605
  // src/webui-server/session-start-payload.ts
499
- import * as path3 from "node:path";
606
+ import * as path4 from "node:path";
500
607
  import { DEFAULT_CONTEXT_WINDOW_MODE_ID } from "@wrongstack/core/types";
501
608
  import { protocolAdvertisement } from "@wrongstack/webui-server/protocol";
502
609
 
@@ -513,6 +620,7 @@ function createSessionStartPayloadBuilder(deps) {
513
620
  let inputCost = 0;
514
621
  let outputCost = 0;
515
622
  let cacheReadCost = 0;
623
+ const updateInfo = deps.updateInfo;
516
624
  try {
517
625
  if (deps.modelsRegistry) {
518
626
  const m = await deps.modelsRegistry.getModel(
@@ -536,7 +644,7 @@ function createSessionStartPayloadBuilder(deps) {
536
644
  model: deps.agent.ctx.model,
537
645
  provider: deps.agent.ctx.provider.id,
538
646
  mode: deps.modeId ?? "default",
539
- projectName: deps.projectRoot ? path3.basename(deps.projectRoot) : void 0,
647
+ projectName: deps.projectRoot ? path4.basename(deps.projectRoot) : void 0,
540
648
  // Frontend reads `projectRoot` from session.start (ws-handlers setEnv) —
541
649
  // omitting it left the store's projectRoot empty after a project switch.
542
650
  projectRoot: deps.projectRoot ?? deps.agent.ctx.projectRoot ?? "",
@@ -552,6 +660,10 @@ function createSessionStartPayloadBuilder(deps) {
552
660
  cacheReadCost,
553
661
  lastInputTokens,
554
662
  providerStatuses: deps.statusTracker?.getAllStatuses() ?? [],
663
+ appVersion: updateInfo?.current,
664
+ latestVersion: updateInfo?.latest,
665
+ updateAvailable: updateInfo?.outdated ?? false,
666
+ updateCheckFailed: updateInfo?.checkFailed ?? false,
555
667
  ...protocolAdvertisement(),
556
668
  ...overrides
557
669
  };
@@ -711,106 +823,6 @@ function startBoundedTerminalLogView(options = {}) {
711
823
  };
712
824
  }
713
825
 
714
- // src/webui-server/node-pty-loader.ts
715
- import { existsSync } from "node:fs";
716
- import { createRequire } from "node:module";
717
- import * as path4 from "node:path";
718
- import { fileURLToPath } from "node:url";
719
- function createNodePtyLoader(importMetaUrl, logger) {
720
- const requireFromCli = createRequire(importMetaUrl);
721
- let cachedNodePty;
722
- return () => {
723
- if (cachedNodePty !== void 0) return cachedNodePty;
724
- try {
725
- const webuiEntry = requireFromCli.resolve("@wrongstack/webui");
726
- const webuiDir = webuiEntry.replace(/[\\/]dist.*$/, "");
727
- const webuiPkgJson = path4.join(webuiDir, "package.json");
728
- cachedNodePty = createRequire(webuiPkgJson)("node-pty");
729
- if (cachedNodePty) {
730
- logger.debug?.(`[terminal] node-pty loaded via webui package (${webuiDir})`);
731
- return cachedNodePty;
732
- }
733
- } catch (err) {
734
- logger.debug?.(`[terminal] webui-route failed: ${err.message}`);
735
- }
736
- try {
737
- cachedNodePty = requireFromCli("node-pty");
738
- logger.debug?.("[terminal] node-pty loaded via direct requireFromCli");
739
- return cachedNodePty;
740
- } catch (err) {
741
- logger.debug?.(`[terminal] direct require failed: ${err.message}`);
742
- }
743
- try {
744
- const cliPkgJson = path4.join(path4.dirname(fileURLToPath(importMetaUrl)), "..", "package.json");
745
- let dir = path4.dirname(cliPkgJson);
746
- for (let i = 0; i < 6; i++) {
747
- const candidate = path4.join(dir, "node_modules", "node-pty");
748
- if (existsSync(candidate)) {
749
- cachedNodePty = createRequire(candidate)("node-pty");
750
- if (cachedNodePty) {
751
- logger.debug?.(`[terminal] node-pty loaded via workspace root (${candidate})`);
752
- return cachedNodePty;
753
- }
754
- }
755
- const parent = path4.dirname(dir);
756
- if (parent === dir) break;
757
- dir = parent;
758
- }
759
- } catch (err) {
760
- logger.debug?.(`[terminal] workspace-root walk failed: ${err.message}`);
761
- }
762
- cachedNodePty = null;
763
- logger.debug?.('[terminal] node-pty resolution failed; terminal panel will report "unavailable"');
764
- return cachedNodePty;
765
- };
766
- }
767
-
768
- // src/webui-server/listen-helpers.ts
769
- import * as http from "node:http";
770
- async function startDeferredHttpListen(args) {
771
- const { server, host, httpPort, logger } = args;
772
- await new Promise((resolveListen, rejectListen) => {
773
- server.listen(httpPort, host, () => resolveListen());
774
- server.once("error", rejectListen);
775
- });
776
- server.on("error", (err) => {
777
- logger.error("http_server_error", {
778
- message: err.message,
779
- port: httpPort
780
- });
781
- });
782
- }
783
- async function startIpv6LoopbackProxy(args) {
784
- const { primary, httpPort, logger } = args;
785
- const v6 = http.createServer();
786
- v6.on("request", (req, res) => primary.emit("request", req, res));
787
- v6.on("upgrade", (req, socket, head) => primary.emit("upgrade", req, socket, head));
788
- const logIpv6Error = (err) => {
789
- if (err.code === "EAFNOSUPPORT" || err.code === "EADDRNOTAVAIL" || err.code === "EADDRINUSE") {
790
- logger.warn("ipv6_loopback_unavailable", { code: err.code, port: httpPort });
791
- return;
792
- }
793
- logger.error("http_server_error", { message: err.message, port: httpPort });
794
- };
795
- v6.on("error", logIpv6Error);
796
- const ipv6Listening = await new Promise((resolveListen) => {
797
- const onListening = () => {
798
- v6.off("error", onInitialError);
799
- resolveListen(true);
800
- };
801
- const onInitialError = () => {
802
- v6.off("listening", onListening);
803
- resolveListen(false);
804
- };
805
- v6.once("listening", onListening);
806
- v6.once("error", onInitialError);
807
- v6.listen(httpPort, "::1");
808
- });
809
- if (!ipv6Listening) return null;
810
- console.log(`[WebUI] Also listening on http://[::1]:${httpPort}`);
811
- return v6;
812
- }
813
-
814
826
  // src/webui-server.ts
815
827
  async function runWebUI(opts) {
816
828
  const trustBoundary = opts.trustBoundary ?? createCompatibilityTrustBoundary({ policyId: "cli-webui-trusted-host-compat-v1" });
@@ -858,6 +870,17 @@ async function runWebUI(opts) {
858
870
  ...opts.onKanbanDispatch ? { dispatchTask: opts.onKanbanDispatch } : {},
859
871
  log: (message) => consoleLogger.info(message)
860
872
  }) : null;
873
+ const stopKanbanSupervisorMemoryStats = kanbanSupervisor ? startSharedHeapWatchdog({
874
+ collectStats: () => {
875
+ const stats = kanbanSupervisor.getStats();
876
+ return {
877
+ kanbanSupervisorSnapshots: stats.snapshots,
878
+ kanbanSupervisorScheduledBoards: stats.scheduledBoards,
879
+ kanbanSupervisorAgentCooldowns: stats.agentCooldowns,
880
+ kanbanSupervisorRunningAgents: stats.runningAgents
881
+ };
882
+ }
883
+ }) : void 0;
861
884
  const goalHandler = new GoalWebSocketHandler(
862
885
  opts.agent,
863
886
  opts.agent.ctx,
@@ -1290,6 +1313,8 @@ async function runWebUI(opts) {
1290
1313
  ...kanbanSupervisor ? { kanbanSupervisor } : {},
1291
1314
  ...kanbanRunMirror ? { kanbanRunMirror } : {}
1292
1315
  });
1316
+ let signalShutdown;
1317
+ const shutdown = () => signalShutdown?.();
1293
1318
  const handleMessage = createEmbeddedMessageRouter({
1294
1319
  trustBoundary,
1295
1320
  opts,
@@ -1386,7 +1411,7 @@ async function runWebUI(opts) {
1386
1411
  })
1387
1412
  );
1388
1413
  });
1389
- const signalShutdown = createWebuiShutdown({
1414
+ signalShutdown = createWebuiShutdown({
1390
1415
  abortInFlight: () => {
1391
1416
  if (abortController) {
1392
1417
  abortController.abort();
@@ -1399,6 +1424,18 @@ async function runWebUI(opts) {
1399
1424
  flushAllStreamBuffers();
1400
1425
  for (const unsub of eventUnsubscribers) unsub();
1401
1426
  },
1427
+ disposeResources: () => {
1428
+ credentialWatcherClose?.();
1429
+ credentialWatcherClose = void 0;
1430
+ goalHandler.dispose();
1431
+ sddBoardHandler.dispose();
1432
+ worktreeHandler.dispose();
1433
+ terminalHandler.dispose();
1434
+ kanbanRunMirror?.dispose();
1435
+ kanbanSupervisor?.dispose();
1436
+ void stopKanbanSupervisorMemoryStats?.();
1437
+ unregisterWebuiClient();
1438
+ },
1402
1439
  closeClients: () => {
1403
1440
  for (const [ws] of clients) ws.close();
1404
1441
  clients.clear();
@@ -1413,6 +1450,7 @@ async function runWebUI(opts) {
1413
1450
  onStopped: () => {
1414
1451
  terminalLogView.redraw();
1415
1452
  terminalLogView.stop();
1453
+ opts.onExit?.();
1416
1454
  resolve();
1417
1455
  }
1418
1456
  });
@@ -1423,21 +1461,6 @@ async function runWebUI(opts) {
1423
1461
  ws.send(JSON.stringify(msg));
1424
1462
  }
1425
1463
  }
1426
- function shutdown() {
1427
- console.log("[WebUI] Shutting down...");
1428
- terminalLogView.redraw();
1429
- terminalLogView.stop();
1430
- credentialWatcherClose?.();
1431
- flushAllStreamBuffers();
1432
- worktreeHandler.dispose();
1433
- terminalHandler.dispose();
1434
- kanbanRunMirror?.dispose();
1435
- kanbanSupervisor?.dispose();
1436
- unregisterWebuiClient();
1437
- ipv6LoopbackServer?.close();
1438
- httpServer?.server.close();
1439
- opts.onExit?.();
1440
- }
1441
1464
  function broadcast(msg) {
1442
1465
  const data = JSON.stringify(msg);
1443
1466
  for (const [ws] of clients) {
@@ -1457,4 +1480,4 @@ async function runWebUI(opts) {
1457
1480
  export {
1458
1481
  runWebUI
1459
1482
  };
1460
- //# sourceMappingURL=webui-server-ZF5KVGC5.js.map
1483
+ //# sourceMappingURL=webui-server-XKFUOKMJ.js.map