@wrongstack/cli 0.313.1 → 0.316.1
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.
- package/dist/{acp-LQMSMV32.js → acp-CEQINXWB.js} +2 -2
- package/dist/acp-registry-cache.d.ts +0 -2
- package/dist/acp-server-agent.d.ts +2 -1
- package/dist/arg-parser.d.ts +2 -1
- package/dist/{auth-VQMSZGKE.js → auth-GZ5A5OF5.js} +3 -3
- package/dist/auth-menu/local.d.ts +3 -2
- package/dist/boot/boot-auto-thin.d.ts +44 -0
- package/dist/boot/brain-decision-log.d.ts +2 -1
- package/dist/boot/config-backup-recovery.d.ts +2 -6
- package/dist/boot/dispatch-singleshot.d.ts +2 -1
- package/dist/boot/dispatch-webui.d.ts +5 -1
- package/dist/boot/event-wiring.d.ts +1 -1
- package/dist/boot/execution-mode.d.ts +2 -1
- package/dist/boot/governance-shadow-bridge.d.ts +4 -3
- package/dist/boot/governance-trace-context.d.ts +4 -4
- package/dist/boot/launch-menu.d.ts +2 -2
- package/dist/boot/system-prompt-builder.d.ts +1 -1
- package/dist/boot/system-prompt-menu.d.ts +10 -58
- package/dist/boot/system-prompt.d.ts +3 -3
- package/dist/boot/tool-registry.d.ts +3 -1
- package/dist/boot/tui-coordinator-callbacks.d.ts +2 -1
- package/dist/boot/tui-coordinator-setup.d.ts +3 -2
- package/dist/boot/tui-goal-wiring.d.ts +2 -1
- package/dist/boot/tui-live-sessions.d.ts +3 -1
- package/dist/boot/tui-session-resume.d.ts +82 -10
- package/dist/boot/tui-session-stub-enrich.d.ts +7 -1
- package/dist/boot/tui-settings-adapter.d.ts +3 -2
- package/dist/boot/tui-theme-adapter.d.ts +3 -2
- package/dist/boot/webui-session-child.d.ts +2 -12
- package/dist/boot-auto-thin-BZ675EVT.js +102 -0
- package/dist/boot-config.d.ts +1 -1
- package/dist/brain-menu/panel-service.d.ts +2 -1
- package/dist/chimera-cascade-evidence.d.ts +4 -21
- package/dist/chimera-review-task.d.ts +0 -7
- package/dist/chimera-subagent-ladder.d.ts +2 -2
- package/dist/{chunk-USYOJIV2.js → chunk-32MTOI2I.js} +2 -2
- package/dist/{chunk-CNQZ4HG4.js → chunk-37SPPLUD.js} +57 -8
- package/dist/chunk-ALE5KHCA.js +1 -0
- package/dist/{chunk-GWVLRUVH.js → chunk-G3IMKQRH.js} +25 -6
- package/dist/{chunk-BZJ4MP32.js → chunk-GKHUHAIK.js} +8 -3
- package/dist/{chunk-3JPTNADJ.js → chunk-GPPRNAKW.js} +1 -1
- package/dist/{chunk-KE7E7DPX.js → chunk-HXJYHAR4.js} +1 -1
- package/dist/{chunk-X3N5TS2S.js → chunk-JACOMTIH.js} +2 -76
- package/dist/{chunk-DGXNL7OT.js → chunk-JHUJUIOH.js} +1 -18
- package/dist/{chunk-WOTGMHAH.js → chunk-KG4X5F7Z.js} +1 -1
- package/dist/{chunk-NUIHRGPY.js → chunk-NV3JDK4F.js} +1 -1
- package/dist/{chunk-E3X4KM7T.js → chunk-NZZOYLQ7.js} +5 -10
- package/dist/{chunk-H6C7SO5H.js → chunk-QC5I4VWZ.js} +1 -1
- package/dist/chunk-SXJTVNWF.js +78 -0
- package/dist/{chunk-AOAIPYFB.js → chunk-SYIBTG3X.js} +2 -2
- package/dist/{chunk-PKHGLWUJ.js → chunk-T642Q732.js} +5 -4
- package/dist/{chunk-BGFM3PD7.js → chunk-UZ2TMHNH.js} +1 -1
- package/dist/{chunk-4S52MIIM.js → chunk-VXCCBI2W.js} +2 -2
- package/dist/{chunk-FKWHSFX4.js → chunk-XYKYXFEE.js} +1 -1
- package/dist/cli-entry-point.d.ts +9 -0
- package/dist/cli-eternal-flag.d.ts +1 -1
- package/dist/{cli-main-UOQ2ICUO.js → cli-main-IEAM6SK7.js} +3578 -1626
- package/dist/cli-main-helpers.d.ts +3 -3
- package/dist/config-doctor.d.ts +34 -3
- package/dist/context-limit.d.ts +4 -3
- package/dist/{diag-doctor-OPTB2NWY.js → diag-doctor-LFCCBPMD.js} +3 -3
- package/dist/execute-deps.d.ts +36 -7
- package/dist/{execution-L4CXQEND.js → execution-TZUGCKFU.js} +459 -77
- package/dist/execution-chimera-cascade.d.ts +1 -1
- package/dist/execution-chimera-review.d.ts +1 -1
- package/dist/execution-cleanup.d.ts +2 -1
- package/dist/execution-kanban-dispatch.d.ts +2 -1
- package/dist/execution-repl-fleet-callbacks.d.ts +3 -2
- package/dist/fleet/budget-source.d.ts +2 -1
- package/dist/fleet/host-context.d.ts +8 -1
- package/dist/fleet/host-director-event-bridges.d.ts +28 -3
- package/dist/fleet/host-event-bridge.d.ts +6 -1
- package/dist/fleet/host-explore-companion-registry.d.ts +60 -0
- package/dist/fleet/host-explore-companion.d.ts +7 -3
- package/dist/fleet/host-provider.d.ts +2 -1
- package/dist/fleet/host-session-writer.d.ts +9 -1
- package/dist/fleet/host-shadow-manager.d.ts +82 -20
- package/dist/fleet/host-shadow-pass.d.ts +9 -1
- package/dist/fleet/host-spawn-types.d.ts +9 -0
- package/dist/fleet/host-status.d.ts +2 -1
- package/dist/fleet/host-subagent-factory.d.ts +2 -1
- package/dist/fleet/host-subagent-session-audit.d.ts +56 -0
- package/dist/fleet/host-supervisor.d.ts +11 -1
- package/dist/fleet/host-task-result-report.d.ts +2 -1
- package/dist/fleet/host.d.ts +38 -6
- package/dist/fleet/status-broadcast.d.ts +2 -1
- package/dist/fleet-statusline.d.ts +2 -1
- package/dist/goal-host.d.ts +3 -2
- package/dist/{governance-V2Z42RBE.js → governance-JSGHHA2R.js} +1 -1
- package/dist/governance-hq-telemetry.d.ts +2 -2
- package/dist/{hq-2XIXGAER.js → hq-NDTJRRX3.js} +3 -3
- package/dist/hq-publisher.d.ts +1 -1
- package/dist/hq-recovery-html.d.ts +1 -6
- package/dist/hq-server/auth-state.d.ts +2 -1
- package/dist/hq-server/auth.d.ts +3 -2
- package/dist/hq-server/login-attempt-store.d.ts +2 -1
- package/dist/hq-server/mailbox-gateway-manager.d.ts +2 -1
- package/dist/hq-server/preflight.d.ts +3 -2
- package/dist/hq-server/server-lifecycle.d.ts +2 -1
- package/dist/hq-server/snapshot.d.ts +0 -9
- package/dist/hq-server/startup.d.ts +2 -3
- package/dist/hq-server/types.d.ts +2 -2
- package/dist/hq-server/upgrade-handler.d.ts +2 -1
- package/dist/hq-server/ws-leader.d.ts +0 -6
- package/dist/{hq-server-DBT3OOYD.js → hq-server-OH3SPSE2.js} +3 -13
- package/dist/hq-server.d.ts +1 -6
- package/dist/hq-static-serve.d.ts +2 -1
- package/dist/{hq-ttl-U2FAYFAT.js → hq-ttl-KUO3AU7X.js} +2 -2
- package/dist/{hq-tunnel-RFKGC4HI.js → hq-tunnel-LO55RCZE.js} +1 -1
- package/dist/hq-tunnel.d.ts +2 -1
- package/dist/index.js +113 -109
- package/dist/input-reader.d.ts +2 -1
- package/dist/kanban-dispatch-route.d.ts +27 -2
- package/dist/kanban-hq-sync.d.ts +2 -1
- package/dist/launch-hints.d.ts +2 -1
- package/dist/mailbox-bridge-bootstrap.d.ts +2 -3
- package/dist/{mailbox-serve-5S3PHG5D.js → mailbox-serve-VKQIRCCB.js} +47 -28
- package/dist/{mcp-RRPKNRSN.js → mcp-AQKSASHD.js} +1 -1
- package/dist/mcp-serve.d.ts +2 -1
- package/dist/{modeldiag-H6DKWJ2N.js → modeldiag-4NYBRSFP.js} +1 -1
- package/dist/next-task-predictor.d.ts +3 -2
- package/dist/permission-prompt.d.ts +2 -1
- package/dist/picker.d.ts +0 -18
- package/dist/plugin-management.d.ts +3 -14
- package/dist/{plugin-usage-GCTYOZPH.js → plugin-usage-AP7SJCDA.js} +2 -2
- package/dist/{plugins-EEKHCMBE.js → plugins-HQ5FU2AM.js} +3 -5
- package/dist/preflight.d.ts +2 -1
- package/dist/{proxy-wiring-G26YGP7S.js → proxy-wiring-EQB5X5YH.js} +3 -3
- package/dist/renderer.d.ts +18 -1
- package/dist/repl-client-registration.d.ts +2 -1
- package/dist/repl.d.ts +1 -1
- package/dist/{replay-XU6ZD7GM.js → replay-FJ2JBXUA.js} +1 -1
- package/dist/{sdd-runtime-GFZ3NJKS.js → sdd-runtime-FLKCSTYV.js} +3 -5
- package/dist/services/commit-message.d.ts +2 -1
- package/dist/services/sdd/state.d.ts +0 -2
- package/dist/services/sdd-runtime.d.ts +1 -1
- package/dist/services/statusline-config.d.ts +27 -4
- package/dist/session-event-wiring.d.ts +3 -5
- package/dist/{sessions-config-7Q7QJBKJ.js → sessions-config-P2LK2L32.js} +5 -1
- package/dist/sessions-doctor-EXHAZSSE.js +146 -0
- package/dist/settings-menu.d.ts +2 -1
- package/dist/shutdown-cleanup.d.ts +3 -2
- package/dist/{simpleui-dist-33VO3QHF.js → simpleui-dist-JKNRCRRK.js} +1 -1
- package/dist/simpleui-dist.d.ts +3 -2
- package/dist/slash-commands/brain-council.d.ts +0 -4
- package/dist/slash-commands/brain-status.d.ts +0 -1
- package/dist/slash-commands/command-context.d.ts +16 -2
- package/dist/slash-commands/fix-classifier.d.ts +2 -1
- package/dist/slash-commands/kanban-agent-helpers.d.ts +2 -1
- package/dist/slash-commands/kanban-board-handlers.d.ts +2 -1
- package/dist/slash-commands/kanban-lifecycle-diagnostics.d.ts +2 -1
- package/dist/slash-commands/kanban-task-subcommands.d.ts +0 -1
- package/dist/slash-commands/memory-audience.d.ts +1 -10
- package/dist/slash-commands/memory-flags.d.ts +2 -1
- package/dist/slash-commands/sdd-command-helpers.d.ts +3 -2
- package/dist/slash-commands/sdd.d.ts +2 -2
- package/dist/slash-commands/settings-mutations.d.ts +0 -1
- package/dist/slash-commands/statusline.d.ts +4 -4
- package/dist/slash-commands/telegram-pairing.d.ts +2 -2
- package/dist/slash-commands/telegram-setup.d.ts +2 -1
- package/dist/slash-commands/tier.d.ts +13 -0
- package/dist/subcommands/handlers/acp-connection-gate.d.ts +4 -3
- package/dist/subcommands/handlers/bench-run-help.d.ts +3 -8
- package/dist/subcommands/handlers/daemon-inventory.d.ts +3 -2
- package/dist/subcommands/handlers/governance.d.ts +2 -1
- package/dist/subcommands/handlers/model-smoke-test.d.ts +3 -2
- package/dist/subcommands/handlers/modeldiag-eval.d.ts +0 -4
- package/dist/subcommands/handlers/modeldiag-profiles.d.ts +1 -1
- package/dist/subcommands/handlers/models-add-help.d.ts +3 -8
- package/dist/subcommands/handlers/sessions-doctor.d.ts +3 -0
- package/dist/subcommands/handlers/update.d.ts +1 -1
- package/dist/terminal-format.d.ts +3 -2
- package/dist/tui-resource-menus.d.ts +2 -1
- package/dist/tuneup.d.ts +5 -5
- package/dist/{update-ZXNC6I6G.js → update-XKARO3CY.js} +3 -3
- package/dist/{update-check-WARHSVZA.js → update-check-LICQNYFK.js} +2 -2
- package/dist/update-check.d.ts +1 -1
- package/dist/utils/hq-ttl.d.ts +2 -1
- package/dist/webui-server/connection-handler.d.ts +34 -2
- package/dist/webui-server/prefs-seeding.d.ts +11 -1
- package/dist/webui-server/route-contexts.d.ts +45 -5
- package/dist/webui-server/session-start-payload.d.ts +13 -0
- package/dist/webui-server/setup-events.d.ts +14 -1
- package/dist/{webui-server-I53IYBW4.js → webui-server-LZOEH7O6.js} +374 -169
- package/dist/webui-server-options.d.ts +30 -4
- package/dist/wiring/brain-and-orchestration.d.ts +3 -3
- package/dist/wiring/cli-execute-builder.d.ts +3 -0
- package/dist/wiring/cli-heap-watchdog.d.ts +2 -1
- package/dist/wiring/cli-prompt-and-tools-setup.d.ts +10 -8
- package/dist/wiring/codebase-index.d.ts +2 -1
- package/dist/wiring/command-host-state.d.ts +61 -5
- package/dist/wiring/controllers.d.ts +3 -4
- package/dist/wiring/dep-watcher-bridge.d.ts +3 -2
- package/dist/wiring/dep-watcher.d.ts +2 -1
- package/dist/wiring/design-studio.d.ts +2 -1
- package/dist/wiring/director-setup.d.ts +3 -2
- package/dist/wiring/domain-terms-mirror.d.ts +2 -1
- package/dist/wiring/external-plugins.d.ts +3 -2
- package/dist/wiring/fleet-command-handlers.d.ts +2 -2
- package/dist/wiring/heap-watchdog-setup.d.ts +2 -1
- package/dist/wiring/hq-telemetry.d.ts +3 -2
- package/dist/wiring/lifecycle-plugins.d.ts +2 -2
- package/dist/wiring/management-tools.d.ts +30 -2
- package/dist/wiring/metrics.d.ts +2 -2
- package/dist/wiring/plugins.d.ts +0 -1
- package/dist/wiring/prompt-journal-recorder.d.ts +1 -1
- package/dist/wiring/provider-runtime-setup.d.ts +9 -2
- package/dist/wiring/provider-runtime.d.ts +23 -3
- package/dist/wiring/provider-status.d.ts +2 -1
- package/dist/wiring/provider-utility-tools.d.ts +2 -1
- package/dist/wiring/provider.d.ts +2 -1
- package/dist/wiring/replay-governance-setup.d.ts +2 -1
- package/dist/wiring/replay.d.ts +2 -1
- package/dist/wiring/resume-candidate.d.ts +27 -0
- package/dist/wiring/runtime-controller-deps.d.ts +8 -1
- package/dist/wiring/runtime-dispatch-state.d.ts +2 -1
- package/dist/wiring/runtime-lifecycle-deps.d.ts +2 -1
- package/dist/wiring/runtime-picker-deps.d.ts +2 -1
- package/dist/wiring/session-command-handlers.d.ts +2 -2
- package/dist/wiring/session-establishment.d.ts +2 -1
- package/dist/wiring/session-registry.d.ts +3 -2
- package/dist/wiring/session-runtime.d.ts +1 -1
- package/dist/wiring/slash-commands.d.ts +5 -4
- package/dist/wiring/teardown-registrar.d.ts +3 -2
- package/dist/wiring/vector-memory-setup.d.ts +3 -2
- package/dist/wiring/wrongtrace-hooks.d.ts +1 -1
- package/dist/wiring/wrongtrace-telemetry.d.ts +2 -1
- package/dist/{wrongtrace-telemetry-6M2CCBX5.js → wrongtrace-telemetry-FNLANZ5I.js} +1 -1
- package/package.json +30 -30
- package/dist/auth-menu.d.ts +0 -7
- package/dist/chunk-WHOIR577.js +0 -1
- package/dist/webui-server/terminal-log-view.d.ts +0 -47
- package/dist/wiring/tools.d.ts +0 -29
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
} from "./chunk-VCYZ3WZV.js";
|
|
7
7
|
import {
|
|
8
8
|
startCliHqConnection
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XYKYXFEE.js";
|
|
10
10
|
import {
|
|
11
11
|
WEBUI_SESSION_CHILD_CAPABILITIES
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-GPPRNAKW.js";
|
|
13
13
|
import {
|
|
14
14
|
loadConfigProviders,
|
|
15
15
|
mutateConfigProviders
|
|
@@ -19,19 +19,31 @@ import "./chunk-7OCVIDC7.js";
|
|
|
19
19
|
// src/webui-server.ts
|
|
20
20
|
import * as path7 from "node:path";
|
|
21
21
|
import { createCompatibilityTrustBoundary, DefaultSecretScrubber } from "@wrongstack/core/security";
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
addFatalSalvageHook,
|
|
24
|
+
startSharedHeapWatchdog,
|
|
25
|
+
wstackGlobalRoot
|
|
26
|
+
} from "@wrongstack/core/utils";
|
|
23
27
|
import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils/error";
|
|
24
28
|
import {
|
|
25
29
|
buildWebUIAccessUrl,
|
|
30
|
+
clientWantsSession,
|
|
31
|
+
collectDisplayedSessionIds,
|
|
26
32
|
createCustomModeStore,
|
|
27
33
|
createEmbeddedMessageRouter,
|
|
28
34
|
createEmbeddedProviderOperations,
|
|
35
|
+
createSessionAgentRegistry,
|
|
29
36
|
envFlag,
|
|
30
37
|
findFreePort,
|
|
31
38
|
findInstalledPackageJson,
|
|
32
39
|
isStrictPort,
|
|
33
40
|
resolveAuthToken,
|
|
34
|
-
|
|
41
|
+
resolvePendingConfirmsForSession,
|
|
42
|
+
scheduleOwnerlessEmptySessionCleanup,
|
|
43
|
+
sendSerialized,
|
|
44
|
+
startTerminalDashboard,
|
|
45
|
+
startWebUILiveStatusLogger,
|
|
46
|
+
toSessionHistoryEntries
|
|
35
47
|
} from "@wrongstack/webui-server";
|
|
36
48
|
import { WebSocketServer } from "ws";
|
|
37
49
|
|
|
@@ -64,11 +76,12 @@ function createWebuiClientRegistration(deps) {
|
|
|
64
76
|
}
|
|
65
77
|
|
|
66
78
|
// src/webui-server/connection-handler.ts
|
|
79
|
+
import { deriveSessionAgents } from "@wrongstack/core/storage";
|
|
80
|
+
import { buildReplayPayload, decodeProtocolFrame } from "@wrongstack/webui-protocol";
|
|
67
81
|
import {
|
|
68
82
|
createConnectionLifecycle,
|
|
69
83
|
verifyClient as verifyWsClient
|
|
70
84
|
} from "@wrongstack/webui-server";
|
|
71
|
-
import { buildReplayPayload, decodeProtocolFrame } from "@wrongstack/webui-protocol";
|
|
72
85
|
function createConnectionHandler(deps) {
|
|
73
86
|
const clientHandlers = [
|
|
74
87
|
deps.goalHandler,
|
|
@@ -139,19 +152,46 @@ function createConnectionHandler(deps) {
|
|
|
139
152
|
send: deps.send,
|
|
140
153
|
sessionPayload: deps.sessionPayload,
|
|
141
154
|
rateLimitMax: deps.rateLimitMax,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
155
|
+
// NOTE — a closed SOCKET is not a stopped run.
|
|
156
|
+
//
|
|
157
|
+
// This used to `abortControllers.delete(client.sessionId)` — a delete
|
|
158
|
+
// WITHOUT an abort, and only for the tab this connection last acted on.
|
|
159
|
+
// Both halves were wrong. The run kept going while `isRunActive` reported
|
|
160
|
+
// `false`, so `session.delete` would happily unlink the journal a live run
|
|
161
|
+
// was still appending to, and the next `user_message` claimed a second
|
|
162
|
+
// lock for the same session and hit `Agent.run() is already in progress`.
|
|
163
|
+
// The other three declared tabs were never touched at all.
|
|
164
|
+
//
|
|
165
|
+
// Doing nothing is the correct behaviour: a background run outlives the
|
|
166
|
+
// tab that started it (the same invariant `retireUndisplayedSessions`
|
|
167
|
+
// honours), and the run's own `finally` releases the lock when it unwinds.
|
|
168
|
+
// A page that comes back is told what is still running by
|
|
169
|
+
// `session.run_state`; a browser that never comes back drains its
|
|
170
|
+
// unanswerable permission prompts through the last-client drain in
|
|
171
|
+
// `connection-lifecycle.ts`, which lets a wedged run finish and release.
|
|
172
|
+
//
|
|
173
|
+
// So there is deliberately NO `onClose` here.
|
|
145
174
|
buildInitialPayload: async () => {
|
|
146
175
|
const payload = { ...await deps.buildSessionStartPayload({}, deps.needsSetup) };
|
|
176
|
+
try {
|
|
177
|
+
const open = deps.openSessionIds?.();
|
|
178
|
+
if (open) payload["openSessionIds"] = [.../* @__PURE__ */ new Set([...open, deps.currentSessionId()])];
|
|
179
|
+
} catch {
|
|
180
|
+
console.debug("[WebUI] Failed to list open sessions");
|
|
181
|
+
}
|
|
182
|
+
let replayEvents;
|
|
147
183
|
try {
|
|
148
184
|
const replay = await deps.loadReplay?.();
|
|
149
|
-
if (replay)
|
|
185
|
+
if (replay) {
|
|
186
|
+
Object.assign(payload, buildReplayPayload(replay));
|
|
187
|
+
replayEvents = replay.events;
|
|
188
|
+
}
|
|
150
189
|
} catch {
|
|
151
190
|
console.debug("[WebUI] Failed to load replay");
|
|
152
191
|
}
|
|
153
192
|
try {
|
|
154
|
-
const
|
|
193
|
+
const roster = deriveSessionAgents(replayEvents ?? []);
|
|
194
|
+
const sessions = roster.length > 0 ? await deps.loadAgentSessions?.(roster.map((agent) => agent.agentId)) : [];
|
|
155
195
|
if (sessions?.length) payload["agentSessions"] = sessions;
|
|
156
196
|
} catch {
|
|
157
197
|
console.debug("[WebUI] Failed to load agent sessions");
|
|
@@ -755,6 +795,9 @@ function createPrefsSeeding(opts) {
|
|
|
755
795
|
if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"])) {
|
|
756
796
|
patchLiveAppConfig({ modelMatrix: payload["modelMatrix"] });
|
|
757
797
|
}
|
|
798
|
+
if (payload["modelTiers"] && typeof payload["modelTiers"] === "object" && !Array.isArray(payload["modelTiers"])) {
|
|
799
|
+
patchLiveAppConfig({ modelTiers: payload["modelTiers"] });
|
|
800
|
+
}
|
|
758
801
|
if (!opts.profileConfigPath) return;
|
|
759
802
|
const globalRoot = opts.globalConfigPath ? path4.dirname(opts.globalConfigPath) : path4.dirname(opts.profileConfigPath);
|
|
760
803
|
const vault = new DefaultSecretVault2({ keyFile: path4.join(globalRoot, ".key") });
|
|
@@ -780,7 +823,7 @@ function createPrefsSeeding(opts) {
|
|
|
780
823
|
);
|
|
781
824
|
};
|
|
782
825
|
return {
|
|
783
|
-
prefSnapshot: () => snapshotPrefs(opts.agent.ctx.meta),
|
|
826
|
+
prefSnapshot: (meta) => snapshotPrefs(meta ?? opts.agent.ctx.meta),
|
|
784
827
|
persistPrefs
|
|
785
828
|
};
|
|
786
829
|
}
|
|
@@ -792,7 +835,9 @@ import { SkillInstaller } from "@wrongstack/core/skills";
|
|
|
792
835
|
import { PromptUsageStore } from "@wrongstack/core/storage";
|
|
793
836
|
import { resolveWstackPaths as resolveWstackPaths3 } from "@wrongstack/core/utils";
|
|
794
837
|
import {
|
|
795
|
-
|
|
838
|
+
createChimeraRouteHandlers,
|
|
839
|
+
createMailboxRouteHandlers,
|
|
840
|
+
rebuildSystemPrompt
|
|
796
841
|
} from "@wrongstack/webui-server";
|
|
797
842
|
function createWebuiRouteContexts({
|
|
798
843
|
opts,
|
|
@@ -807,10 +852,17 @@ function createWebuiRouteContexts({
|
|
|
807
852
|
persistPrefs,
|
|
808
853
|
pendingConfirms,
|
|
809
854
|
abortControllers,
|
|
810
|
-
|
|
811
|
-
|
|
855
|
+
getSessionAgent,
|
|
856
|
+
peekSessionAgent,
|
|
857
|
+
isSessionLive,
|
|
858
|
+
getForegroundSession,
|
|
859
|
+
setForegroundSession,
|
|
860
|
+
clients,
|
|
861
|
+
onSessionsUndisplayed,
|
|
862
|
+
stopSessionFleet,
|
|
812
863
|
send,
|
|
813
|
-
broadcast
|
|
864
|
+
broadcast,
|
|
865
|
+
broadcastEveryone
|
|
814
866
|
}) {
|
|
815
867
|
const brainCtx = {
|
|
816
868
|
brainSettings: opts.brainSettings,
|
|
@@ -823,6 +875,14 @@ function createWebuiRouteContexts({
|
|
|
823
875
|
const introspectionConfigStore = opts.agent.container?.safeResolve?.(TOKENS2.ConfigStore);
|
|
824
876
|
const introspectionCtx = {
|
|
825
877
|
agent: opts.agent,
|
|
878
|
+
// Answer `diag.get` / `stats.get` / `side_effects.list` from the agent that
|
|
879
|
+
// owns the ASKING session. `opts.agent` is the leader, i.e. whichever tab
|
|
880
|
+
// the runtime last switched to, so a background tab asking for its own
|
|
881
|
+
// stats got the foreground's numbers stamped with the foreground's id —
|
|
882
|
+
// which the browser's session filter then dropped, leaving the panel blank.
|
|
883
|
+
// Read path, so `peek` first: `getAgent` creates, and materialising an
|
|
884
|
+
// agent for a stale id can evict a live tab's.
|
|
885
|
+
getAgent: (sessionId) => peekSessionAgent?.(sessionId) ?? (sessionId ? getSessionAgent(sessionId) : opts.agent),
|
|
826
886
|
modelsRegistry: opts.modelsRegistry,
|
|
827
887
|
configStore: introspectionConfigStore,
|
|
828
888
|
getConfig: () => {
|
|
@@ -863,10 +923,10 @@ function createWebuiRouteContexts({
|
|
|
863
923
|
skillsPaths?.promptUsage ?? path5.join(profileDir, "prompt-usage.json")
|
|
864
924
|
)
|
|
865
925
|
};
|
|
866
|
-
const designCtx = {
|
|
926
|
+
const designCtx = (sessionId) => ({
|
|
867
927
|
projectRoot: skillsProjectRoot,
|
|
868
|
-
agentMeta: opts.agent.ctx
|
|
869
|
-
};
|
|
928
|
+
agentMeta: (sessionId ? getSessionAgent(sessionId)?.ctx : void 0) ?? opts.agent.ctx
|
|
929
|
+
});
|
|
870
930
|
const agentConfigCtx = {
|
|
871
931
|
agent: opts.agent,
|
|
872
932
|
modeStore: opts.modeStore,
|
|
@@ -881,31 +941,86 @@ function createWebuiRouteContexts({
|
|
|
881
941
|
broadcast,
|
|
882
942
|
log: (m) => console.log(m)
|
|
883
943
|
};
|
|
944
|
+
const promptProjectRoot = () => opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
|
|
945
|
+
const metaForSession = (sessionId) => (sessionId ? getSessionAgent(sessionId)?.ctx.meta : void 0) ?? opts.agent.ctx.meta;
|
|
884
946
|
const prefsCtx = {
|
|
885
947
|
meta: opts.agent.ctx.meta,
|
|
886
|
-
|
|
948
|
+
metaFor: metaForSession,
|
|
949
|
+
snapshot: (sessionId) => prefSnapshot(sessionId ? metaForSession(sessionId) : void 0),
|
|
887
950
|
persist: persistPrefs,
|
|
888
951
|
setYolo: opts.onYoloSwitch,
|
|
889
952
|
setAutonomy: opts.onAutonomySwitch,
|
|
890
953
|
applyWrongProxyPrefs: opts.onWrongProxyPrefsChange,
|
|
891
954
|
pendingConfirms,
|
|
892
955
|
configStore: opts.agent.container?.safeResolve?.(TOKENS2.ConfigStore),
|
|
956
|
+
systemPrompt: {
|
|
957
|
+
paths: () => {
|
|
958
|
+
const wpaths = resolveWstackPaths3({ projectRoot: promptProjectRoot(), globalRoot });
|
|
959
|
+
return {
|
|
960
|
+
globalDir: wpaths.globalInstructions,
|
|
961
|
+
projectDir: wpaths.inProjectInstructions
|
|
962
|
+
};
|
|
963
|
+
},
|
|
964
|
+
profileConfigPath,
|
|
965
|
+
current: () => opts.appConfig?.systemPrompt?.variant ?? "default",
|
|
966
|
+
// Patch the live config before rebuilding — `persistPrefs` writes the
|
|
967
|
+
// file, and the builder reads the variant off the in-memory object.
|
|
968
|
+
applyVariant: async (variant, sessionId) => {
|
|
969
|
+
if (opts.appConfig) {
|
|
970
|
+
opts.appConfig = {
|
|
971
|
+
...opts.appConfig,
|
|
972
|
+
systemPrompt: { ...opts.appConfig.systemPrompt ?? {}, variant }
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
const tools = opts.agent.tools;
|
|
976
|
+
if (!tools || !opts.appConfig) return;
|
|
977
|
+
const targetCtx = sessionId ? getSessionAgent(sessionId).ctx : opts.agent.ctx;
|
|
978
|
+
targetCtx.meta["systemPromptVariant"] = variant;
|
|
979
|
+
await rebuildSystemPrompt(
|
|
980
|
+
{
|
|
981
|
+
modeStore: opts.modeStore,
|
|
982
|
+
memoryStore: opts.memoryStore,
|
|
983
|
+
skillLoader: opts.skillLoader,
|
|
984
|
+
modelCapabilities: {
|
|
985
|
+
maxContextTokens: opts.agent.ctx.provider?.capabilities?.maxContext,
|
|
986
|
+
supportsTools: !!opts.agent.ctx.provider?.capabilities?.tools,
|
|
987
|
+
supportsVision: !!opts.agent.ctx.provider?.capabilities?.vision,
|
|
988
|
+
supportsReasoning: !!opts.agent.ctx.provider?.capabilities?.reasoning
|
|
989
|
+
},
|
|
990
|
+
context: targetCtx,
|
|
991
|
+
toolRegistry: tools,
|
|
992
|
+
getConfig: () => opts.appConfig,
|
|
993
|
+
projectRoot: promptProjectRoot(),
|
|
994
|
+
globalRoot,
|
|
995
|
+
container: opts.agent.container
|
|
996
|
+
},
|
|
997
|
+
typeof targetCtx.meta["mode"] === "string" ? targetCtx.meta["mode"] : "default"
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
},
|
|
893
1001
|
send,
|
|
894
1002
|
broadcast
|
|
895
1003
|
};
|
|
896
1004
|
const projectsCtx = {
|
|
897
1005
|
opts,
|
|
1006
|
+
getForegroundSession,
|
|
1007
|
+
setForegroundSession,
|
|
898
1008
|
abortControllers,
|
|
1009
|
+
// Switching projects re-roots the server, so every conversation's run
|
|
1010
|
+
// has to stop — there is no single "legacy" run to cancel here. The name
|
|
1011
|
+
// is the shared context's; this host answers it with the only registry it
|
|
1012
|
+
// has, one controller per tab. It used to read a process-wide controller
|
|
1013
|
+
// that nothing ever set, so the teardown ran on nothing.
|
|
899
1014
|
abortLegacyRun: () => {
|
|
900
|
-
const controller
|
|
901
|
-
|
|
902
|
-
controller.abort();
|
|
903
|
-
clearAbortController();
|
|
904
|
-
}
|
|
1015
|
+
for (const controller of abortControllers.values()) controller.abort();
|
|
1016
|
+
abortControllers.clear();
|
|
905
1017
|
},
|
|
906
1018
|
buildSessionStart: (overrides) => buildSessionStartPayload(overrides),
|
|
907
1019
|
send,
|
|
908
1020
|
broadcast,
|
|
1021
|
+
// A project switch re-roots the whole server; it is the one announcement
|
|
1022
|
+
// that must reach connections which are displaying something else.
|
|
1023
|
+
...broadcastEveryone ? { broadcastEveryone } : {},
|
|
909
1024
|
log: (m) => console.log(m)
|
|
910
1025
|
};
|
|
911
1026
|
const mailboxRoutes = createMailboxRouteHandlers({
|
|
@@ -913,18 +1028,53 @@ function createWebuiRouteContexts({
|
|
|
913
1028
|
getGlobalRoot: () => opts.globalConfigPath ? path5.dirname(opts.globalConfigPath) : "",
|
|
914
1029
|
events: opts.events
|
|
915
1030
|
});
|
|
1031
|
+
const chimeraRoutes = createChimeraRouteHandlers({
|
|
1032
|
+
// Same lazily-resolved layout the mailbox routes read roots through:
|
|
1033
|
+
// projectDir is the per-project WrongStack home where review-reports.jsonl
|
|
1034
|
+
// lives. A project switch re-roots it, hence the getter.
|
|
1035
|
+
projectDir: () => resolveWstackPaths3({
|
|
1036
|
+
projectRoot: opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "",
|
|
1037
|
+
globalRoot: opts.globalConfigPath ? path5.dirname(opts.globalConfigPath) : ""
|
|
1038
|
+
}).projectDir,
|
|
1039
|
+
send,
|
|
1040
|
+
log: (m) => console.warn(m)
|
|
1041
|
+
});
|
|
916
1042
|
const sessionsCtx = {
|
|
917
1043
|
opts,
|
|
918
1044
|
buildSessionStart: (overrides) => buildSessionStartPayload(overrides),
|
|
919
1045
|
getCustomModeStore,
|
|
1046
|
+
// Session transitions must re-point the TARGET session's context, not the
|
|
1047
|
+
// leader's — resuming tab 2 was rewriting the context tab 1 ran in.
|
|
1048
|
+
getAgent: getSessionAgent,
|
|
1049
|
+
// Non-creating peek for the hasSession ownership gate.
|
|
1050
|
+
...peekSessionAgent ? { peekAgent: peekSessionAgent } : {},
|
|
1051
|
+
// Session-keyed, so "is this tab running" is answered per tab and not
|
|
1052
|
+
// "is anything running in this process".
|
|
1053
|
+
isRunActive: (sessionId) => sessionId ? abortControllers.has(sessionId) : abortControllers.size > 0,
|
|
1054
|
+
isSessionLive,
|
|
1055
|
+
// A replayed session brings its own fleet back: the roster comes from that
|
|
1056
|
+
// session's journal, the bodies from here. Scoped by id, because the
|
|
1057
|
+
// transcripts directory is shared by every session of the project.
|
|
1058
|
+
...opts.agentTranscripts ? {
|
|
1059
|
+
loadAgentSessions: async (subagentIds) => await opts.agentTranscripts?.loadSessionsFromDisk(subagentIds) ?? []
|
|
1060
|
+
} : {},
|
|
1061
|
+
getForegroundSession,
|
|
1062
|
+
setForegroundSession,
|
|
1063
|
+
clients,
|
|
1064
|
+
onSessionsUndisplayed,
|
|
920
1065
|
send,
|
|
921
1066
|
broadcast,
|
|
922
1067
|
log: (m) => console.log(m)
|
|
923
1068
|
};
|
|
924
1069
|
const connectionCtx = {
|
|
925
1070
|
agent: opts.agent,
|
|
1071
|
+
getAgent: getSessionAgent,
|
|
1072
|
+
// Non-creating peek for the hasSession ownership gate (background-tab
|
|
1073
|
+
// requests are legitimate; arbitrary strings are not).
|
|
1074
|
+
...peekSessionAgent ? { peekAgent: peekSessionAgent } : {},
|
|
926
1075
|
abortControllers,
|
|
927
1076
|
pendingConfirms,
|
|
1077
|
+
...stopSessionFleet ? { stopSessionFleet } : {},
|
|
928
1078
|
send,
|
|
929
1079
|
broadcast,
|
|
930
1080
|
log: (m) => console.log(m)
|
|
@@ -939,6 +1089,7 @@ function createWebuiRouteContexts({
|
|
|
939
1089
|
prefsCtx,
|
|
940
1090
|
projectsCtx,
|
|
941
1091
|
mailboxRoutes,
|
|
1092
|
+
chimeraRoutes,
|
|
942
1093
|
sessionsCtx,
|
|
943
1094
|
connectionCtx
|
|
944
1095
|
};
|
|
@@ -958,6 +1109,9 @@ import {
|
|
|
958
1109
|
// src/webui-server/session-start-payload.ts
|
|
959
1110
|
function createSessionStartPayloadBuilder(deps) {
|
|
960
1111
|
return async function buildSessionStartPayload(overrides, needsSetup = false) {
|
|
1112
|
+
const targetSessionId = typeof overrides?.["sessionId"] === "string" && overrides["sessionId"] ? overrides["sessionId"] : deps.agent.ctx.session?.id ?? deps.session.id;
|
|
1113
|
+
const ctx = deps.getSessionContext?.(targetSessionId) ?? deps.agent.ctx;
|
|
1114
|
+
const startedAt = typeof ctx.session?.startedAt === "string" && ctx.session.startedAt.length > 0 ? ctx.session.startedAt : void 0;
|
|
961
1115
|
let maxContext = 0;
|
|
962
1116
|
let inputCost = 0;
|
|
963
1117
|
let outputCost = 0;
|
|
@@ -965,37 +1119,33 @@ function createSessionStartPayloadBuilder(deps) {
|
|
|
965
1119
|
const updateInfo = deps.updateInfo;
|
|
966
1120
|
try {
|
|
967
1121
|
if (deps.modelsRegistry) {
|
|
968
|
-
const m = await deps.modelsRegistry.getModel(
|
|
969
|
-
deps.agent.ctx.provider.id,
|
|
970
|
-
deps.agent.ctx.model
|
|
971
|
-
);
|
|
1122
|
+
const m = await deps.modelsRegistry.getModel(ctx.provider.id, ctx.model);
|
|
972
1123
|
const registryMax = m?.capabilities.maxContext;
|
|
973
|
-
maxContext = registryMax ??
|
|
1124
|
+
maxContext = registryMax ?? ctx.provider.capabilities?.maxContext ?? 0;
|
|
974
1125
|
const rates = getCostRates(m);
|
|
975
1126
|
inputCost = rates.input;
|
|
976
1127
|
outputCost = rates.output;
|
|
977
1128
|
cacheReadCost = rates.cacheRead;
|
|
978
1129
|
} else {
|
|
979
|
-
maxContext =
|
|
1130
|
+
maxContext = ctx.provider.capabilities?.maxContext ?? 0;
|
|
980
1131
|
}
|
|
981
1132
|
} catch {
|
|
982
1133
|
}
|
|
983
|
-
const lastInputTokens = typeof
|
|
1134
|
+
const lastInputTokens = typeof ctx.lastRequestTokens === "number" && ctx.lastRequestTokens > 0 ? ctx.lastRequestTokens : 0;
|
|
984
1135
|
return {
|
|
985
|
-
sessionId:
|
|
986
|
-
|
|
987
|
-
|
|
1136
|
+
sessionId: targetSessionId,
|
|
1137
|
+
...startedAt ? { startedAt } : {},
|
|
1138
|
+
model: ctx.model,
|
|
1139
|
+
provider: ctx.provider.id,
|
|
988
1140
|
mode: deps.modeId ?? "default",
|
|
989
1141
|
projectName: deps.projectRoot ? path6.basename(deps.projectRoot) : void 0,
|
|
990
1142
|
// Frontend reads `projectRoot` from session.start (ws-handlers setEnv) —
|
|
991
1143
|
// omitting it left the store's projectRoot empty after a project switch.
|
|
992
|
-
projectRoot: deps.projectRoot ??
|
|
993
|
-
cwd: deps.projectRoot ??
|
|
1144
|
+
projectRoot: deps.projectRoot ?? ctx.projectRoot ?? "",
|
|
1145
|
+
cwd: deps.projectRoot ?? ctx.projectRoot ?? "",
|
|
994
1146
|
needsSetup,
|
|
995
1147
|
// true when provider/model not configured and running in --webui mode
|
|
996
|
-
contextMode: String(
|
|
997
|
-
deps.agent.ctx.meta?.["contextWindowMode"] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID
|
|
998
|
-
),
|
|
1148
|
+
contextMode: String(ctx.meta?.["contextWindowMode"] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID),
|
|
999
1149
|
maxContext,
|
|
1000
1150
|
inputCost,
|
|
1001
1151
|
outputCost,
|
|
@@ -1013,6 +1163,7 @@ function createSessionStartPayloadBuilder(deps) {
|
|
|
1013
1163
|
}
|
|
1014
1164
|
|
|
1015
1165
|
// src/webui-server/setup-events.ts
|
|
1166
|
+
import { mailboxSessionTag } from "@wrongstack/core/coordination";
|
|
1016
1167
|
import {
|
|
1017
1168
|
createEternalSubscription,
|
|
1018
1169
|
setupEvents as setupCanonicalEvents
|
|
@@ -1024,7 +1175,14 @@ function createSetupEvents(deps) {
|
|
|
1024
1175
|
const budget = deps.getFleetBudget?.() ?? null;
|
|
1025
1176
|
deps.broadcast({
|
|
1026
1177
|
type: "fleet.concurrency_update",
|
|
1027
|
-
|
|
1178
|
+
// Deliberately UNSTAMPED. The spawn/concurrency budget is one ceiling
|
|
1179
|
+
// for the whole process — every tab draws from it and the client keeps
|
|
1180
|
+
// it in a single global store — so this is a project-wide fact, not a
|
|
1181
|
+
// conversation's. Stamping it with the runtime's session made it a
|
|
1182
|
+
// conversation's by accident: `clientWantsSession` then dropped it for
|
|
1183
|
+
// any page whose declared tabs did not include that session, and the
|
|
1184
|
+
// fleet gauges froze the moment the boot tab was closed.
|
|
1185
|
+
payload: {
|
|
1028
1186
|
fleetConcurrency: budget?.activeAgents ?? fleetConcurrency,
|
|
1029
1187
|
fleetConcurrencyMax: budget?.maxConcurrent ?? fleetConcurrencyMax,
|
|
1030
1188
|
...budget ? {
|
|
@@ -1037,7 +1195,7 @@ function createSetupEvents(deps) {
|
|
|
1037
1195
|
checkpointMaxSpawns: budget.checkpointMaxSpawns,
|
|
1038
1196
|
ceilingMismatch: budget.ceilingMismatch
|
|
1039
1197
|
} : {}
|
|
1040
|
-
}
|
|
1198
|
+
}
|
|
1041
1199
|
});
|
|
1042
1200
|
};
|
|
1043
1201
|
return function setupEvents() {
|
|
@@ -1046,7 +1204,11 @@ function createSetupEvents(deps) {
|
|
|
1046
1204
|
const maxIterations = deps.agent.ctx?.meta?.["maxIterations"];
|
|
1047
1205
|
const disposeCanonical = setupCanonicalEvents({
|
|
1048
1206
|
events: deps.events,
|
|
1049
|
-
|
|
1207
|
+
// The third argument is not decoration: it names the tab that OWNS a
|
|
1208
|
+
// subagent, and the frames that pass it carry the subagent's own
|
|
1209
|
+
// session on the payload. Dropping it here routed every subagent
|
|
1210
|
+
// codemap frame at a session no tab subscribes to.
|
|
1211
|
+
broadcast: (_clients, message, targetSessionId) => targetSessionId !== void 0 ? deps.broadcast(message, targetSessionId) : deps.broadcast(message),
|
|
1050
1212
|
clients: deps.getClients(),
|
|
1051
1213
|
config: {
|
|
1052
1214
|
tools: {
|
|
@@ -1054,6 +1216,7 @@ function createSetupEvents(deps) {
|
|
|
1054
1216
|
}
|
|
1055
1217
|
},
|
|
1056
1218
|
context: deps.agent.ctx,
|
|
1219
|
+
...deps.sessionContext ? { sessionContext: deps.sessionContext } : {},
|
|
1057
1220
|
pendingConfirms: deps.pendingConfirms,
|
|
1058
1221
|
globalConfigPath: deps.globalConfigPath,
|
|
1059
1222
|
onFleetBroadcaster: deps.onFleetBroadcaster,
|
|
@@ -1071,7 +1234,7 @@ function createSetupEvents(deps) {
|
|
|
1071
1234
|
type: "subagent.event",
|
|
1072
1235
|
payload: deps.sessionPayload({
|
|
1073
1236
|
kind: "leader_updated",
|
|
1074
|
-
subagentId:
|
|
1237
|
+
subagentId: `leader@${mailboxSessionTag(deps.currentSessionId())}`,
|
|
1075
1238
|
isLeader: true,
|
|
1076
1239
|
name: "Leader",
|
|
1077
1240
|
status: "running"
|
|
@@ -1115,96 +1278,8 @@ import {
|
|
|
1115
1278
|
createStreamCoalescer
|
|
1116
1279
|
} from "@wrongstack/webui-server";
|
|
1117
1280
|
|
|
1118
|
-
// src/webui-server/terminal-log-view.ts
|
|
1119
|
-
import { format } from "node:util";
|
|
1120
|
-
var DEFAULT_BUFFER_LINES = 200;
|
|
1121
|
-
var MUTED_LEVELS = ["log", "info", "debug"];
|
|
1122
|
-
var FORWARDED_LEVELS = ["warn", "error"];
|
|
1123
|
-
var DISABLED = {
|
|
1124
|
-
enabled: false,
|
|
1125
|
-
mutedCount: 0,
|
|
1126
|
-
recent: () => [],
|
|
1127
|
-
stop: () => {
|
|
1128
|
-
}
|
|
1129
|
-
};
|
|
1130
|
-
function envVerbose() {
|
|
1131
|
-
for (const name of ["WEBUI_VERBOSE", "WRONGSTACK_WEBUI_VERBOSE"]) {
|
|
1132
|
-
const value = process.env[name]?.trim().toLowerCase();
|
|
1133
|
-
if (value === "1" || value === "true" || value === "yes" || value === "on") return true;
|
|
1134
|
-
}
|
|
1135
|
-
return false;
|
|
1136
|
-
}
|
|
1137
|
-
function startQuietSurfaceConsole(options = {}) {
|
|
1138
|
-
const isTTY = options.isTTY ?? process.stdout.isTTY === true;
|
|
1139
|
-
const verbose = options.verbose ?? envVerbose();
|
|
1140
|
-
if (!isTTY || verbose) return DISABLED;
|
|
1141
|
-
const bufferLines = Math.max(1, Math.trunc(options.bufferLines ?? DEFAULT_BUFFER_LINES));
|
|
1142
|
-
const target = options.consoleTarget ?? console;
|
|
1143
|
-
const muted = [];
|
|
1144
|
-
let mutedCount = 0;
|
|
1145
|
-
let stopped = false;
|
|
1146
|
-
let lastLine = null;
|
|
1147
|
-
let lastEmit = null;
|
|
1148
|
-
let repeats = 0;
|
|
1149
|
-
const originals = {
|
|
1150
|
-
log: target.log,
|
|
1151
|
-
info: target.info,
|
|
1152
|
-
warn: target.warn,
|
|
1153
|
-
error: target.error,
|
|
1154
|
-
debug: target.debug
|
|
1155
|
-
};
|
|
1156
|
-
const wrappers = {};
|
|
1157
|
-
const flushRepeats = () => {
|
|
1158
|
-
if (repeats === 0 || !lastEmit) return;
|
|
1159
|
-
const count = repeats;
|
|
1160
|
-
repeats = 0;
|
|
1161
|
-
lastEmit.call(target, ` \u2191 previous line repeated ${count}\xD7`);
|
|
1162
|
-
};
|
|
1163
|
-
for (const level of MUTED_LEVELS) {
|
|
1164
|
-
const wrapper = (...args) => {
|
|
1165
|
-
mutedCount += 1;
|
|
1166
|
-
muted.push(format(...args));
|
|
1167
|
-
if (muted.length > bufferLines) muted.shift();
|
|
1168
|
-
};
|
|
1169
|
-
wrappers[level] = wrapper;
|
|
1170
|
-
target[level] = wrapper;
|
|
1171
|
-
}
|
|
1172
|
-
for (const level of FORWARDED_LEVELS) {
|
|
1173
|
-
const original = originals[level];
|
|
1174
|
-
const wrapper = (...args) => {
|
|
1175
|
-
const line = format(...args);
|
|
1176
|
-
if (line === lastLine) {
|
|
1177
|
-
repeats += 1;
|
|
1178
|
-
return;
|
|
1179
|
-
}
|
|
1180
|
-
flushRepeats();
|
|
1181
|
-
lastLine = line;
|
|
1182
|
-
lastEmit = original;
|
|
1183
|
-
original.apply(target, args);
|
|
1184
|
-
};
|
|
1185
|
-
wrappers[level] = wrapper;
|
|
1186
|
-
target[level] = wrapper;
|
|
1187
|
-
}
|
|
1188
|
-
return {
|
|
1189
|
-
enabled: true,
|
|
1190
|
-
get mutedCount() {
|
|
1191
|
-
return mutedCount;
|
|
1192
|
-
},
|
|
1193
|
-
recent: () => [...muted],
|
|
1194
|
-
stop: () => {
|
|
1195
|
-
if (stopped) return;
|
|
1196
|
-
stopped = true;
|
|
1197
|
-
flushRepeats();
|
|
1198
|
-
for (const level of Object.keys(originals)) {
|
|
1199
|
-
if (target[level] === wrappers[level]) target[level] = originals[level];
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
};
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
1281
|
// src/webui-server.ts
|
|
1206
1282
|
async function runWebUI(opts) {
|
|
1207
|
-
const terminalLogView = startQuietSurfaceConsole();
|
|
1208
1283
|
const trustBoundary = opts.trustBoundary ?? createCompatibilityTrustBoundary({ policyId: "cli-webui-trusted-host-compat-v1" });
|
|
1209
1284
|
const host = opts.host ?? process.env["WEBUI_HOST"] ?? process.env["WS_HOST"] ?? "127.0.0.1";
|
|
1210
1285
|
const publicUrl = opts.publicUrl ?? process.env["WEBUI_PUBLIC_URL"];
|
|
@@ -1213,6 +1288,11 @@ async function runWebUI(opts) {
|
|
|
1213
1288
|
const surface = opts.surface ?? "webui";
|
|
1214
1289
|
const surfaceDefaults = surface === "simpleui" ? { http: 3466 } : { http: 3456 };
|
|
1215
1290
|
const requestedHttpPort = opts.httpPort ?? opts.port ?? surfaceDefaults.http;
|
|
1291
|
+
const terminalLogView = startTerminalDashboard({
|
|
1292
|
+
title: surface === "simpleui" ? "SimpleUI" : "WebUI",
|
|
1293
|
+
quiet: !envFlag("WEBUI_LOGS"),
|
|
1294
|
+
getUrl: () => `http://${host}:${httpPort}`
|
|
1295
|
+
});
|
|
1216
1296
|
const strictPort = opts.strictPort ?? isStrictPort();
|
|
1217
1297
|
let httpPort = requestedHttpPort;
|
|
1218
1298
|
if (!strictPort) {
|
|
@@ -1225,7 +1305,6 @@ async function runWebUI(opts) {
|
|
|
1225
1305
|
const clients = /* @__PURE__ */ new Map();
|
|
1226
1306
|
const pendingConfirms = /* @__PURE__ */ new Map();
|
|
1227
1307
|
const secretScrubber = new DefaultSecretScrubber();
|
|
1228
|
-
let abortController = null;
|
|
1229
1308
|
const abortControllers = /* @__PURE__ */ new Map();
|
|
1230
1309
|
const profileDir = path7.dirname(profileConfigPath);
|
|
1231
1310
|
let customModeStoreP = null;
|
|
@@ -1274,7 +1353,24 @@ async function runWebUI(opts) {
|
|
|
1274
1353
|
}
|
|
1275
1354
|
const { prefSnapshot, persistPrefs } = createPrefsSeeding(opts);
|
|
1276
1355
|
const sessionStartedAt = Date.now();
|
|
1277
|
-
|
|
1356
|
+
let sessionAgentsRef;
|
|
1357
|
+
const buildSessionStartPayload = createSessionStartPayloadBuilder({
|
|
1358
|
+
...opts,
|
|
1359
|
+
// Read through to the live `opts`, do NOT snapshot: `projects.select`
|
|
1360
|
+
// re-roots the host by assigning `opts.projectRoot` / `opts.session` on
|
|
1361
|
+
// this very object. A spread copy froze both at boot, so every
|
|
1362
|
+
// `session.start` broadcast after a project switch still announced the
|
|
1363
|
+
// previous project's root — the switch looked like it had not happened.
|
|
1364
|
+
get projectRoot() {
|
|
1365
|
+
return opts.projectRoot;
|
|
1366
|
+
},
|
|
1367
|
+
get session() {
|
|
1368
|
+
return opts.session;
|
|
1369
|
+
},
|
|
1370
|
+
// `peek`, never `get`: building a payload must not materialise an agent
|
|
1371
|
+
// for a session id that arrived from a stale browser tab.
|
|
1372
|
+
getSessionContext: (sessionId) => sessionAgentsRef?.peek(sessionId)?.ctx
|
|
1373
|
+
});
|
|
1278
1374
|
const { register: registerWebuiClient, unregister: unregisterWebuiClient } = createWebuiClientRegistration({
|
|
1279
1375
|
projectRoot: opts.projectRoot,
|
|
1280
1376
|
appConfig: opts.appConfig,
|
|
@@ -1282,19 +1378,23 @@ async function runWebUI(opts) {
|
|
|
1282
1378
|
hqSessionId: opts.session.id,
|
|
1283
1379
|
getSessionId: () => opts.agent.ctx.session?.id ?? opts.session.id,
|
|
1284
1380
|
hqControl: {
|
|
1381
|
+
// HQ speaks for the LEADER — the boot session, the one it registered
|
|
1382
|
+
// itself under (`hqSessionId`) — not for whatever else the browser
|
|
1383
|
+
// has open. This used to abort every controller in the map and clear
|
|
1384
|
+
// it, so a remote "interrupt" issued against the leader also killed
|
|
1385
|
+
// the three other tabs' in-flight runs. Deleting the entries was
|
|
1386
|
+
// wrong on its own terms too: the run's own `end()` owns removal, and
|
|
1387
|
+
// clearing early makes `isRunActive` lie to every tab still running.
|
|
1285
1388
|
interruptLeader: () => {
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
c.abort();
|
|
1294
|
-
aborted = true;
|
|
1389
|
+
const leaderId = opts.session.id;
|
|
1390
|
+
const controller = abortControllers.get(leaderId);
|
|
1391
|
+
if (!controller) return false;
|
|
1392
|
+
controller.abort();
|
|
1393
|
+
try {
|
|
1394
|
+
void Promise.resolve(opts.stopSessionFleet?.(leaderId)).catch(() => void 0);
|
|
1395
|
+
} catch {
|
|
1295
1396
|
}
|
|
1296
|
-
|
|
1297
|
-
return aborted;
|
|
1397
|
+
return true;
|
|
1298
1398
|
},
|
|
1299
1399
|
allowRunCommand: () => opts.hqAllowExec === true
|
|
1300
1400
|
}
|
|
@@ -1343,6 +1443,16 @@ async function runWebUI(opts) {
|
|
|
1343
1443
|
...opts.vectorMemoryModelCacheDir ? { vectorMemoryModelCacheDir: opts.vectorMemoryModelCacheDir } : {}
|
|
1344
1444
|
});
|
|
1345
1445
|
const wss = httpServer ? new WebSocketServer({ server: httpServer.server, maxPayload: 20 * 1024 * 1024 }) : new WebSocketServer({ port: httpPort, host, maxPayload: 20 * 1024 * 1024 });
|
|
1446
|
+
wss.on("error", (err) => {
|
|
1447
|
+
console.error(
|
|
1448
|
+
JSON.stringify({
|
|
1449
|
+
level: "error",
|
|
1450
|
+
event: "webui_server.error",
|
|
1451
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1452
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1453
|
+
})
|
|
1454
|
+
);
|
|
1455
|
+
});
|
|
1346
1456
|
if (httpServer) {
|
|
1347
1457
|
const boundPort = surface === "simpleui" ? await startDeferredHttpListen({
|
|
1348
1458
|
server: httpServer.server,
|
|
@@ -1381,7 +1491,8 @@ async function runWebUI(opts) {
|
|
|
1381
1491
|
`[WebUI] Frontend not served (run \`pnpm --filter @wrongstack/webui build\`). WS bridge still active on ws://${host}:${httpPort}.`
|
|
1382
1492
|
);
|
|
1383
1493
|
}
|
|
1384
|
-
|
|
1494
|
+
let foregroundSession = opts.agent.ctx.session ?? opts.session;
|
|
1495
|
+
const currentSessionId = () => foregroundSession.id;
|
|
1385
1496
|
const registryBaseDir = globalRoot;
|
|
1386
1497
|
let webuiInstanceRegistered = false;
|
|
1387
1498
|
if (opts.projectRoot) {
|
|
@@ -1433,6 +1544,7 @@ async function runWebUI(opts) {
|
|
|
1433
1544
|
flushAllStreamBuffers
|
|
1434
1545
|
} = createStreamCoalescer({ broadcast, sessionPayload });
|
|
1435
1546
|
const setupEvents = createSetupEvents({
|
|
1547
|
+
sessionContext: (sessionId) => sessionAgentsRef?.peek(sessionId)?.ctx,
|
|
1436
1548
|
events: opts.events,
|
|
1437
1549
|
agent: opts.agent,
|
|
1438
1550
|
subscribeEternalIteration: opts.subscribeEternalIteration,
|
|
@@ -1471,6 +1583,90 @@ async function runWebUI(opts) {
|
|
|
1471
1583
|
broadcast,
|
|
1472
1584
|
broadcastSaved: (providers) => embeddedProviderOperations.broadcastSaved(providers)
|
|
1473
1585
|
});
|
|
1586
|
+
const sessionAgents = createSessionAgentRegistry({
|
|
1587
|
+
template: opts.agent,
|
|
1588
|
+
...opts.modelsRegistry ? { modelsRegistry: opts.modelsRegistry } : {},
|
|
1589
|
+
isRunActive: (sessionId) => abortControllers.has(sessionId),
|
|
1590
|
+
// A tab that is still on screen must outlive one that was closed, whatever
|
|
1591
|
+
// order their agents were created in.
|
|
1592
|
+
isDisplayed: (sessionId) => {
|
|
1593
|
+
for (const client of clients.values()) {
|
|
1594
|
+
if (client.sessionId === sessionId) return true;
|
|
1595
|
+
if (client.sessionIds?.has(sessionId) === true) return true;
|
|
1596
|
+
}
|
|
1597
|
+
return false;
|
|
1598
|
+
}
|
|
1599
|
+
});
|
|
1600
|
+
sessionAgentsRef = sessionAgents;
|
|
1601
|
+
const stopLiveStatusLogger = startWebUILiveStatusLogger({
|
|
1602
|
+
events: opts.events,
|
|
1603
|
+
dashboard: terminalLogView,
|
|
1604
|
+
getSessionList: () => {
|
|
1605
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1606
|
+
for (const client of clients.values()) {
|
|
1607
|
+
if (client.sessionId) ids.add(client.sessionId);
|
|
1608
|
+
for (const id of client.sessionIds ?? []) ids.add(id);
|
|
1609
|
+
}
|
|
1610
|
+
const currentId = opts.agent.ctx.session?.id ?? opts.session.id;
|
|
1611
|
+
if (ids.size === 0 && currentId) ids.add(currentId);
|
|
1612
|
+
return Array.from(ids).map((id) => {
|
|
1613
|
+
const ctx = sessionAgentsRef?.peek(id)?.ctx;
|
|
1614
|
+
return {
|
|
1615
|
+
id,
|
|
1616
|
+
model: ctx?.model ?? opts.agent.ctx.model ?? "",
|
|
1617
|
+
provider: ctx?.provider?.id ?? opts.agent.ctx.provider?.id ?? "",
|
|
1618
|
+
isRunning: abortControllers.has(id)
|
|
1619
|
+
};
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
});
|
|
1623
|
+
const releaseSessionSalvage = addFatalSalvageHook(() => {
|
|
1624
|
+
try {
|
|
1625
|
+
sessionAgents.flushAllSync();
|
|
1626
|
+
} catch {
|
|
1627
|
+
}
|
|
1628
|
+
});
|
|
1629
|
+
const retireUndisplayedSessions = (sessionIds) => {
|
|
1630
|
+
for (const sessionId of sessionIds) {
|
|
1631
|
+
if (!sessionId || sessionId === opts.session.id) continue;
|
|
1632
|
+
if (sessionId === foregroundSession.id) continue;
|
|
1633
|
+
const orphaned = resolvePendingConfirmsForSession(pendingConfirms, sessionId);
|
|
1634
|
+
if (orphaned > 0) {
|
|
1635
|
+
console.log(
|
|
1636
|
+
JSON.stringify({
|
|
1637
|
+
level: "warn",
|
|
1638
|
+
event: "webui.confirm_orphaned_by_tab_close",
|
|
1639
|
+
sessionId,
|
|
1640
|
+
count: orphaned,
|
|
1641
|
+
message: `Denied ${orphaned} unanswerable permission prompt(s) for closed session ${sessionId}.`
|
|
1642
|
+
})
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1645
|
+
if (abortControllers.has(sessionId)) continue;
|
|
1646
|
+
opts.onSessionRetired?.(sessionId);
|
|
1647
|
+
const agent = sessionAgents.peek(sessionId);
|
|
1648
|
+
if (!agent || agent === opts.agent) continue;
|
|
1649
|
+
void sessionAgents.endAndClose(sessionId);
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
const stopEmptySessionCleanup = opts.sessionStore ? scheduleOwnerlessEmptySessionCleanup({
|
|
1653
|
+
getSessionStore: () => opts.sessionStore,
|
|
1654
|
+
getActiveSessionId: () => foregroundSession.id,
|
|
1655
|
+
getActiveSessionIds: () => collectDisplayedSessionIds({ getSession: () => foregroundSession, clients }),
|
|
1656
|
+
// A run outlives the tab that started it, and a turn that has not
|
|
1657
|
+
// written its first record yet looks exactly like a session nobody
|
|
1658
|
+
// ever used.
|
|
1659
|
+
hasParticipants: (sessionId) => abortControllers.has(sessionId),
|
|
1660
|
+
refreshSessions: async () => {
|
|
1661
|
+
const list = await opts.sessionStore?.list(200);
|
|
1662
|
+
if (!list) return;
|
|
1663
|
+
broadcastEveryone({
|
|
1664
|
+
type: "sessions.list",
|
|
1665
|
+
payload: { sessions: toSessionHistoryEntries(list, foregroundSession.id) }
|
|
1666
|
+
});
|
|
1667
|
+
},
|
|
1668
|
+
logger: consoleLogger
|
|
1669
|
+
}) : null;
|
|
1474
1670
|
const routeContexts = createWebuiRouteContexts({
|
|
1475
1671
|
opts,
|
|
1476
1672
|
profileConfigPath,
|
|
@@ -1484,12 +1680,19 @@ async function runWebUI(opts) {
|
|
|
1484
1680
|
persistPrefs,
|
|
1485
1681
|
pendingConfirms,
|
|
1486
1682
|
abortControllers,
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1683
|
+
getSessionAgent: (sessionId) => sessionAgents.get(sessionId),
|
|
1684
|
+
peekSessionAgent: (sessionId) => sessionAgents.peek(sessionId),
|
|
1685
|
+
onSessionsUndisplayed: retireUndisplayedSessions,
|
|
1686
|
+
isSessionLive: (sessionId) => sessionAgents.isLive(sessionId),
|
|
1687
|
+
getForegroundSession: () => foregroundSession,
|
|
1688
|
+
setForegroundSession: (next) => {
|
|
1689
|
+
foregroundSession = next;
|
|
1490
1690
|
},
|
|
1691
|
+
clients,
|
|
1692
|
+
...opts.stopSessionFleet ? { stopSessionFleet: opts.stopSessionFleet } : {},
|
|
1491
1693
|
send,
|
|
1492
|
-
broadcast
|
|
1694
|
+
broadcast,
|
|
1695
|
+
broadcastEveryone
|
|
1493
1696
|
});
|
|
1494
1697
|
const kanbanHostRoutes = createCliKanbanHostRoutes({
|
|
1495
1698
|
opts,
|
|
@@ -1526,6 +1729,7 @@ async function runWebUI(opts) {
|
|
|
1526
1729
|
prefsCtx: routeContexts.prefsCtx,
|
|
1527
1730
|
projectCtx: routeContexts.projectsCtx,
|
|
1528
1731
|
mailboxRoutes: routeContexts.mailboxRoutes,
|
|
1732
|
+
chimeraRoutes: routeContexts.chimeraRoutes,
|
|
1529
1733
|
sessionCtx: routeContexts.sessionsCtx,
|
|
1530
1734
|
conversationCtx: routeContexts.connectionCtx,
|
|
1531
1735
|
goalHandler,
|
|
@@ -1579,12 +1783,11 @@ async function runWebUI(opts) {
|
|
|
1579
1783
|
send,
|
|
1580
1784
|
sessionPayload,
|
|
1581
1785
|
handleMessage,
|
|
1582
|
-
abortControllers,
|
|
1583
1786
|
pendingConfirms,
|
|
1584
1787
|
buildSessionStartPayload,
|
|
1585
1788
|
loadReplay: async () => {
|
|
1586
1789
|
const activeSession = opts.agent.ctx.session ?? opts.session;
|
|
1587
|
-
await activeSession.flush()
|
|
1790
|
+
await activeSession.flush();
|
|
1588
1791
|
if (opts.sessionStore) {
|
|
1589
1792
|
const data = await opts.sessionStore.load(activeSession.id);
|
|
1590
1793
|
return { messages: data.messages, events: data.events, usage: data.usage };
|
|
@@ -1592,27 +1795,17 @@ async function runWebUI(opts) {
|
|
|
1592
1795
|
const usage = opts.agent.ctx.tokenCounter.total();
|
|
1593
1796
|
return { messages: opts.agent.ctx.messages, usage };
|
|
1594
1797
|
},
|
|
1595
|
-
loadAgentSessions: async () => await opts.agentTranscripts?.loadSessionsFromDisk() ?? [],
|
|
1798
|
+
loadAgentSessions: async (subagentIds) => await opts.agentTranscripts?.loadSessionsFromDisk(subagentIds) ?? [],
|
|
1799
|
+
// What this process is actually holding right now. The browser
|
|
1800
|
+
// reconciles its persisted tab strip against it, so a restarted server
|
|
1801
|
+
// is not dressed in the previous run's tabs.
|
|
1802
|
+
openSessionIds: () => sessionAgents.ids(),
|
|
1596
1803
|
needsSetup: opts.needsSetup ?? false
|
|
1597
1804
|
})
|
|
1598
1805
|
);
|
|
1599
|
-
wss.on("error", (err) => {
|
|
1600
|
-
console.error(
|
|
1601
|
-
JSON.stringify({
|
|
1602
|
-
level: "error",
|
|
1603
|
-
event: "webui_server.error",
|
|
1604
|
-
message: err instanceof Error ? err.message : String(err),
|
|
1605
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1606
|
-
})
|
|
1607
|
-
);
|
|
1608
|
-
});
|
|
1609
1806
|
signalShutdown = createWebuiShutdown({
|
|
1610
1807
|
abortInFlight: () => {
|
|
1611
1808
|
void embeddedAutoHealDispose?.();
|
|
1612
|
-
if (abortController) {
|
|
1613
|
-
abortController.abort();
|
|
1614
|
-
abortController = null;
|
|
1615
|
-
}
|
|
1616
1809
|
for (const c of abortControllers.values()) c.abort();
|
|
1617
1810
|
abortControllers.clear();
|
|
1618
1811
|
},
|
|
@@ -1640,6 +1833,9 @@ async function runWebUI(opts) {
|
|
|
1640
1833
|
}
|
|
1641
1834
|
},
|
|
1642
1835
|
disposeResources: async () => {
|
|
1836
|
+
releaseSessionSalvage();
|
|
1837
|
+
await stopEmptySessionCleanup?.dispose();
|
|
1838
|
+
await sessionAgents.closeAll().catch(() => void 0);
|
|
1643
1839
|
credentialWatcherClose?.();
|
|
1644
1840
|
credentialWatcherClose = void 0;
|
|
1645
1841
|
goalHandler.dispose();
|
|
@@ -1665,11 +1861,12 @@ async function runWebUI(opts) {
|
|
|
1665
1861
|
pid: process.pid,
|
|
1666
1862
|
registryBaseDir,
|
|
1667
1863
|
onStopped: () => {
|
|
1864
|
+
stopLiveStatusLogger();
|
|
1668
1865
|
const muted = terminalLogView.mutedCount;
|
|
1669
1866
|
terminalLogView.stop();
|
|
1670
1867
|
if (muted > 0) {
|
|
1671
1868
|
console.log(
|
|
1672
|
-
`[WebUI] ${muted} progress line(s) kept out of this terminal \u2014 set
|
|
1869
|
+
`[WebUI] ${muted} progress line(s) kept out of this terminal \u2014 set WEBUI_LOGS=1 to stream them.`
|
|
1673
1870
|
);
|
|
1674
1871
|
}
|
|
1675
1872
|
opts.onExit?.();
|
|
@@ -1681,7 +1878,15 @@ async function runWebUI(opts) {
|
|
|
1681
1878
|
function send(ws, msg) {
|
|
1682
1879
|
sendSerialized(ws, JSON.stringify(msg));
|
|
1683
1880
|
}
|
|
1684
|
-
function broadcast(msg) {
|
|
1881
|
+
function broadcast(msg, targetSessionId) {
|
|
1882
|
+
const data = JSON.stringify(msg);
|
|
1883
|
+
const payload = msg.payload;
|
|
1884
|
+
const sessionId = targetSessionId ?? (payload && typeof payload === "object" && typeof payload.sessionId === "string" ? payload.sessionId : void 0);
|
|
1885
|
+
for (const [ws, client] of clients) {
|
|
1886
|
+
if (clientWantsSession(client, sessionId)) sendSerialized(ws, data);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
function broadcastEveryone(msg) {
|
|
1685
1890
|
const data = JSON.stringify(msg);
|
|
1686
1891
|
for (const [ws] of clients) sendSerialized(ws, data);
|
|
1687
1892
|
}
|
|
@@ -1693,4 +1898,4 @@ async function runWebUI(opts) {
|
|
|
1693
1898
|
export {
|
|
1694
1899
|
runWebUI
|
|
1695
1900
|
};
|
|
1696
|
-
//# sourceMappingURL=webui-server-
|
|
1901
|
+
//# sourceMappingURL=webui-server-LZOEH7O6.js.map
|