@wrongstack/cli 0.295.1 → 0.296.3
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/data/providers.json +0 -24
- package/dist/acp-A5OSIU2P.js +824 -0
- package/dist/acp-A5OSIU2P.js.map +7 -0
- package/dist/acp-server-agent.d.ts.map +1 -1
- package/dist/arg-parser.d.ts.map +1 -1
- package/dist/audit-2ZSFGNIF.js +102 -0
- package/dist/audit-2ZSFGNIF.js.map +7 -0
- package/dist/auth-CJWQJVRH.js +504 -0
- package/dist/auth-CJWQJVRH.js.map +7 -0
- package/dist/auth-menu/auth-menu-audit.d.ts +0 -4
- package/dist/auth-menu/auth-menu-audit.d.ts.map +1 -1
- package/dist/bench-7ZDWSNEF.js +278 -0
- package/dist/bench-7ZDWSNEF.js.map +7 -0
- package/dist/boot/brain-decision-log.d.ts +7 -5
- package/dist/boot/brain-decision-log.d.ts.map +1 -1
- package/dist/boot/dispatch-webui.d.ts +5 -8
- package/dist/boot/dispatch-webui.d.ts.map +1 -1
- package/dist/boot/system-prompt-builder.d.ts +3 -0
- package/dist/boot/system-prompt-builder.d.ts.map +1 -1
- package/dist/boot/tui-coordinator-callbacks.d.ts +45 -0
- package/dist/boot/tui-coordinator-callbacks.d.ts.map +1 -0
- package/dist/boot/tui-project-switch.d.ts +2 -3
- package/dist/boot/tui-project-switch.d.ts.map +1 -1
- package/dist/boot/tui-runtime-state.d.ts +5 -6
- package/dist/boot/tui-runtime-state.d.ts.map +1 -1
- package/dist/boot/tui-session-resume.d.ts.map +1 -1
- package/dist/boot/tui-settings-adapter.d.ts +2 -3
- package/dist/boot/tui-settings-adapter.d.ts.map +1 -1
- package/dist/boot.d.ts.map +1 -1
- package/dist/brain-menu/panel-service.d.ts.map +1 -1
- package/dist/chimera-review-task.d.ts +6 -0
- package/dist/chimera-review-task.d.ts.map +1 -0
- package/dist/chimera-reviewer-policy.d.ts +34 -0
- package/dist/chimera-reviewer-policy.d.ts.map +1 -0
- package/dist/chronicle-QQEQFSSE.js +256 -0
- package/dist/chronicle-QQEQFSSE.js.map +7 -0
- package/dist/chunk-5N34EWJR.js +418 -0
- package/dist/chunk-5N34EWJR.js.map +7 -0
- package/dist/chunk-66T43Q4Y.js +90 -0
- package/dist/chunk-66T43Q4Y.js.map +7 -0
- package/dist/chunk-7OCVIDC7.js +11 -0
- package/dist/chunk-7OCVIDC7.js.map +7 -0
- package/dist/chunk-ABXK4VVY.js +84 -0
- package/dist/chunk-ABXK4VVY.js.map +7 -0
- package/dist/chunk-AXVZMEWG.js +306 -0
- package/dist/chunk-AXVZMEWG.js.map +7 -0
- package/dist/chunk-AZESNNIC.js +216 -0
- package/dist/chunk-AZESNNIC.js.map +7 -0
- package/dist/chunk-BGFM3PD7.js +41 -0
- package/dist/chunk-BGFM3PD7.js.map +7 -0
- package/dist/chunk-BQCAJ7FI.js +425 -0
- package/dist/chunk-BQCAJ7FI.js.map +7 -0
- package/dist/chunk-DPJZQCVP.js +13 -0
- package/dist/chunk-DPJZQCVP.js.map +7 -0
- package/dist/chunk-F4PEGAJZ.js +176 -0
- package/dist/chunk-F4PEGAJZ.js.map +7 -0
- package/dist/chunk-IHBYBYGA.js +443 -0
- package/dist/chunk-IHBYBYGA.js.map +7 -0
- package/dist/chunk-JBSQ7ONN.js +1023 -0
- package/dist/chunk-JBSQ7ONN.js.map +7 -0
- package/dist/chunk-JBTFWWKV.js +1325 -0
- package/dist/chunk-JBTFWWKV.js.map +7 -0
- package/dist/chunk-JUHRI6OV.js +100 -0
- package/dist/chunk-JUHRI6OV.js.map +7 -0
- package/dist/chunk-OI7GWJDS.js +3303 -0
- package/dist/chunk-OI7GWJDS.js.map +7 -0
- package/dist/chunk-OP47B3IX.js +142 -0
- package/dist/chunk-OP47B3IX.js.map +7 -0
- package/dist/chunk-PJHET45A.js +158 -0
- package/dist/chunk-PJHET45A.js.map +7 -0
- package/dist/chunk-Q5GTM25S.js +17 -0
- package/dist/chunk-Q5GTM25S.js.map +7 -0
- package/dist/chunk-QD544J2B.js +19 -0
- package/dist/chunk-QD544J2B.js.map +7 -0
- package/dist/chunk-QUNHS6O3.js +258 -0
- package/dist/chunk-QUNHS6O3.js.map +7 -0
- package/dist/chunk-TADVX644.js +480 -0
- package/dist/chunk-TADVX644.js.map +7 -0
- package/dist/chunk-TGDHN4LM.js +81 -0
- package/dist/chunk-TGDHN4LM.js.map +7 -0
- package/dist/chunk-TYO2OVAD.js +50 -0
- package/dist/chunk-TYO2OVAD.js.map +7 -0
- package/dist/chunk-VCYZ3WZV.js +14 -0
- package/dist/chunk-VCYZ3WZV.js.map +7 -0
- package/dist/chunk-VJUEZZYB.js +29 -0
- package/dist/chunk-VJUEZZYB.js.map +7 -0
- package/dist/chunk-WHOIR577.js +1 -0
- package/dist/chunk-WHOIR577.js.map +7 -0
- package/dist/chunk-WY7KKOUK.js +460 -0
- package/dist/chunk-WY7KKOUK.js.map +7 -0
- package/dist/chunk-XJXDOF63.js +28 -0
- package/dist/chunk-XJXDOF63.js.map +7 -0
- package/dist/chunk-XWEGWYDU.js +560 -0
- package/dist/chunk-XWEGWYDU.js.map +7 -0
- package/dist/chunk-YWUPHRGB.js +346 -0
- package/dist/chunk-YWUPHRGB.js.map +7 -0
- package/dist/chunk-Z7YGASQP.js +313 -0
- package/dist/chunk-Z7YGASQP.js.map +7 -0
- package/dist/cli-entry-main.d.ts +2 -0
- package/dist/cli-entry-main.d.ts.map +1 -0
- package/dist/cli-entry-point.d.ts +3 -3
- package/dist/cli-entry-point.d.ts.map +1 -1
- package/dist/cli-main-RTY35OPD.js +29268 -0
- package/dist/cli-main-RTY35OPD.js.map +7 -0
- package/dist/cli-main-helpers.d.ts +21 -0
- package/dist/cli-main-helpers.d.ts.map +1 -0
- package/dist/cli-main.d.ts +25 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/context-limit.d.ts +48 -0
- package/dist/context-limit.d.ts.map +1 -1
- package/dist/diag-doctor-OQ3AA63O.js +170 -0
- package/dist/diag-doctor-OQ3AA63O.js.map +7 -0
- package/dist/execute-deps.d.ts +11 -12
- package/dist/execute-deps.d.ts.map +1 -1
- package/dist/execution-O7I5DYE6.js +4682 -0
- package/dist/execution-O7I5DYE6.js.map +7 -0
- package/dist/execution-chimera-ask.d.ts +10 -0
- package/dist/execution-chimera-ask.d.ts.map +1 -0
- package/dist/execution-chimera-cascade.d.ts +15 -0
- package/dist/execution-chimera-cascade.d.ts.map +1 -0
- package/dist/execution-cleanup.d.ts +23 -0
- package/dist/execution-cleanup.d.ts.map +1 -0
- package/dist/execution-kanban-dispatch.d.ts +10 -0
- package/dist/execution-kanban-dispatch.d.ts.map +1 -0
- package/dist/execution-repl-fleet-callbacks.d.ts +11 -0
- package/dist/execution-repl-fleet-callbacks.d.ts.map +1 -0
- package/dist/execution-storage-observability.d.ts +3 -0
- package/dist/execution-storage-observability.d.ts.map +1 -0
- package/dist/execution-tui-next-step-callbacks.d.ts +20 -0
- package/dist/execution-tui-next-step-callbacks.d.ts.map +1 -0
- package/dist/execution.d.ts +2 -139
- package/dist/execution.d.ts.map +1 -1
- package/dist/export-TPORYVRZ.js +64 -0
- package/dist/export-TPORYVRZ.js.map +7 -0
- package/dist/fleet/host-acp-runner-cache.d.ts +9 -0
- package/dist/fleet/host-acp-runner-cache.d.ts.map +1 -0
- package/dist/fleet/host-acp.d.ts +3 -0
- package/dist/fleet/host-acp.d.ts.map +1 -0
- package/dist/fleet/host-concurrency.d.ts +2 -0
- package/dist/fleet/host-concurrency.d.ts.map +1 -0
- package/dist/fleet/host-context.d.ts +5 -0
- package/dist/fleet/host-context.d.ts.map +1 -0
- package/dist/fleet/host-director-builder.d.ts +24 -0
- package/dist/fleet/host-director-builder.d.ts.map +1 -0
- package/dist/fleet/host-director-event-bridges.d.ts +37 -0
- package/dist/fleet/host-director-event-bridges.d.ts.map +1 -0
- package/dist/fleet/host-director-services.d.ts +20 -0
- package/dist/fleet/host-director-services.d.ts.map +1 -0
- package/dist/fleet/host-event-bridge.d.ts +17 -0
- package/dist/fleet/host-event-bridge.d.ts.map +1 -0
- package/dist/fleet/host-helpers.d.ts +22 -0
- package/dist/fleet/host-helpers.d.ts.map +1 -0
- package/dist/fleet/host-learning-tracker.d.ts +11 -0
- package/dist/fleet/host-learning-tracker.d.ts.map +1 -0
- package/dist/fleet/host-learning.d.ts +4 -0
- package/dist/fleet/host-learning.d.ts.map +1 -0
- package/dist/fleet/host-lifecycle-events.d.ts +4 -0
- package/dist/fleet/host-lifecycle-events.d.ts.map +1 -0
- package/dist/fleet/host-paths.d.ts +3 -0
- package/dist/fleet/host-paths.d.ts.map +1 -0
- package/dist/fleet/host-provider.d.ts +22 -0
- package/dist/fleet/host-provider.d.ts.map +1 -0
- package/dist/fleet/host-session-writer.d.ts +3 -0
- package/dist/fleet/host-session-writer.d.ts.map +1 -0
- package/dist/fleet/host-shadow-pass.d.ts +21 -0
- package/dist/fleet/host-shadow-pass.d.ts.map +1 -0
- package/dist/fleet/host-spawn-types.d.ts +25 -0
- package/dist/fleet/host-spawn-types.d.ts.map +1 -0
- package/dist/fleet/host-status.d.ts +48 -0
- package/dist/fleet/host-status.d.ts.map +1 -0
- package/dist/fleet/host-subagent-factory.d.ts +16 -0
- package/dist/fleet/host-subagent-factory.d.ts.map +1 -0
- package/dist/fleet/host-supervisor.d.ts +16 -0
- package/dist/fleet/host-supervisor.d.ts.map +1 -0
- package/dist/fleet/host-task-result-report.d.ts +11 -0
- package/dist/fleet/host-task-result-report.d.ts.map +1 -0
- package/dist/fleet/host-types.d.ts +89 -0
- package/dist/fleet/host-types.d.ts.map +1 -0
- package/dist/fleet/host.d.ts +10 -357
- package/dist/fleet/host.d.ts.map +1 -1
- package/dist/hq-JUYKUNJX.js +547 -0
- package/dist/hq-JUYKUNJX.js.map +7 -0
- package/dist/hq-command-controller.d.ts +2 -2
- package/dist/hq-command-controller.d.ts.map +1 -1
- package/dist/hq-server/auth.d.ts +15 -4
- package/dist/hq-server/auth.d.ts.map +1 -1
- package/dist/hq-server/project-root.d.ts +5 -0
- package/dist/hq-server/project-root.d.ts.map +1 -0
- package/dist/hq-server/routes/auth-handlers.d.ts +25 -0
- package/dist/hq-server/routes/auth-handlers.d.ts.map +1 -0
- package/dist/hq-server/routes/session-handlers.d.ts +8 -0
- package/dist/hq-server/routes/session-handlers.d.ts.map +1 -0
- package/dist/hq-server/routes.d.ts +8 -6
- package/dist/hq-server/routes.d.ts.map +1 -1
- package/dist/hq-server/snapshot.d.ts +44 -0
- package/dist/hq-server/snapshot.d.ts.map +1 -1
- package/dist/hq-server/types.d.ts +22 -4
- package/dist/hq-server/types.d.ts.map +1 -1
- package/dist/hq-server/utils.d.ts +8 -0
- package/dist/hq-server/utils.d.ts.map +1 -1
- package/dist/hq-server/ws.d.ts +1 -1
- package/dist/hq-server/ws.d.ts.map +1 -1
- package/dist/hq-server-6AQ3BXZA.js +39 -0
- package/dist/hq-server-6AQ3BXZA.js.map +7 -0
- package/dist/hq-server.d.ts +3 -3
- package/dist/hq-server.d.ts.map +1 -1
- package/dist/hq-ttl-U2FAYFAT.js +8 -0
- package/dist/hq-ttl-U2FAYFAT.js.map +7 -0
- package/dist/hq-tunnel-RFKGC4HI.js +114 -0
- package/dist/hq-tunnel-RFKGC4HI.js.map +7 -0
- package/dist/hq-tunnel.d.ts +1 -1
- package/dist/hq-tunnel.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2503 -53026
- package/dist/index.js.map +4 -4
- package/dist/init-E2NDDOHI.js +27 -0
- package/dist/init-E2NDDOHI.js.map +7 -0
- package/dist/kanban-dispatch-route.d.ts +14 -0
- package/dist/kanban-dispatch-route.d.ts.map +1 -0
- package/dist/kanban-hq-sync.d.ts +2 -0
- package/dist/kanban-hq-sync.d.ts.map +1 -1
- package/dist/kanban-run-mirror-EKVNGXWW.js +12 -0
- package/dist/kanban-run-mirror-EKVNGXWW.js.map +7 -0
- package/dist/live-settings-input.d.ts +69 -0
- package/dist/live-settings-input.d.ts.map +1 -0
- package/dist/mailbox-serve-2UWHEN3M.js +302 -0
- package/dist/mailbox-serve-2UWHEN3M.js.map +7 -0
- package/dist/mcp-CYLPUEHC.js +413 -0
- package/dist/mcp-CYLPUEHC.js.map +7 -0
- package/dist/modeldiag-HENYJ4N7.js +1290 -0
- package/dist/modeldiag-HENYJ4N7.js.map +7 -0
- package/dist/permissions-FNYLZIL4.js +115 -0
- package/dist/permissions-FNYLZIL4.js.map +7 -0
- package/dist/picker-key-state.d.ts +7 -0
- package/dist/picker-key-state.d.ts.map +1 -0
- package/dist/picker-model-picker.d.ts +6 -0
- package/dist/picker-model-picker.d.ts.map +1 -0
- package/dist/picker-ui.d.ts +17 -0
- package/dist/picker-ui.d.ts.map +1 -0
- package/dist/picker.d.ts +4 -37
- package/dist/picker.d.ts.map +1 -1
- package/dist/plugin-usage-OU6X6LM3.js +40 -0
- package/dist/plugin-usage-OU6X6LM3.js.map +7 -0
- package/dist/plugins-4LJ5IUBI.js +24 -0
- package/dist/plugins-4LJ5IUBI.js.map +7 -0
- package/dist/pre-launch.d.ts +0 -1
- package/dist/pre-launch.d.ts.map +1 -1
- package/dist/project-BLQ3ALVL.js +76 -0
- package/dist/project-BLQ3ALVL.js.map +7 -0
- package/dist/project-manifest-GE25PW4S.js +18 -0
- package/dist/project-manifest-GE25PW4S.js.map +7 -0
- package/dist/project-picker-BOCWHBXP.js +17 -0
- package/dist/project-picker-BOCWHBXP.js.map +7 -0
- package/dist/projects-XE76NFNZ.js +38 -0
- package/dist/projects-XE76NFNZ.js.map +7 -0
- package/dist/provider-helpers.d.ts +22 -6
- package/dist/provider-helpers.d.ts.map +1 -1
- package/dist/providers-models-GDELBHZK.js +589 -0
- package/dist/providers-models-GDELBHZK.js.map +7 -0
- package/dist/quick-VIO2BEHS.js +10 -0
- package/dist/quick-VIO2BEHS.js.map +7 -0
- package/dist/repl-auto-proceed.d.ts +19 -0
- package/dist/repl-auto-proceed.d.ts.map +1 -0
- package/dist/repl-client-registration.d.ts +7 -0
- package/dist/repl-client-registration.d.ts.map +1 -0
- package/dist/repl-input.d.ts +11 -0
- package/dist/repl-input.d.ts.map +1 -0
- package/dist/repl-options.d.ts +54 -0
- package/dist/repl-options.d.ts.map +1 -0
- package/dist/repl-rendering.d.ts +4 -0
- package/dist/repl-rendering.d.ts.map +1 -0
- package/dist/repl-suggestions.d.ts +21 -0
- package/dist/repl-suggestions.d.ts.map +1 -0
- package/dist/repl.d.ts +3 -162
- package/dist/repl.d.ts.map +1 -1
- package/dist/replay-KCTXNMZQ.js +75 -0
- package/dist/replay-KCTXNMZQ.js.map +7 -0
- package/dist/rewind-EJMVFVLS.js +185 -0
- package/dist/rewind-EJMVFVLS.js.map +7 -0
- package/dist/sdd-runtime-JG4EEKL6.js +54 -0
- package/dist/sdd-runtime-JG4EEKL6.js.map +7 -0
- package/dist/services/sdd/project-context.d.ts +1 -1
- package/dist/services/sdd/project-context.d.ts.map +1 -1
- package/dist/services/sdd/task-manager.d.ts.map +1 -1
- package/dist/services/statusline-config.d.ts +1 -1
- package/dist/services/statusline-config.d.ts.map +1 -1
- package/dist/session-event-wiring.d.ts +2 -2
- package/dist/session-event-wiring.d.ts.map +1 -1
- package/dist/sessions-config-NLCMK3BY.js +409 -0
- package/dist/sessions-config-NLCMK3BY.js.map +7 -0
- package/dist/settings-menu.d.ts +3 -18
- package/dist/settings-menu.d.ts.map +1 -1
- package/dist/simpleui-dist-JIFGUJO7.js +93 -0
- package/dist/simpleui-dist-JIFGUJO7.js.map +7 -0
- package/dist/slash-commands/brain.d.ts.map +1 -1
- package/dist/slash-commands/clear.d.ts.map +1 -1
- package/dist/slash-commands/fallback.d.ts.map +1 -1
- package/dist/slash-commands/kanban-agent-helpers.d.ts +18 -0
- package/dist/slash-commands/kanban-agent-helpers.d.ts.map +1 -0
- package/dist/slash-commands/kanban-format.d.ts +16 -0
- package/dist/slash-commands/kanban-format.d.ts.map +1 -0
- package/dist/slash-commands/kanban-help.d.ts +2 -0
- package/dist/slash-commands/kanban-help.d.ts.map +1 -0
- package/dist/slash-commands/kanban.d.ts.map +1 -1
- package/dist/slash-commands/mailbox-demo.d.ts.map +1 -1
- package/dist/slash-commands/mailbox.d.ts +2 -1
- package/dist/slash-commands/mailbox.d.ts.map +1 -1
- package/dist/slash-commands/memory-compact.d.ts +5 -0
- package/dist/slash-commands/memory-compact.d.ts.map +1 -0
- package/dist/slash-commands/memory-flags.d.ts +36 -0
- package/dist/slash-commands/memory-flags.d.ts.map +1 -0
- package/dist/slash-commands/memory-formatters.d.ts +16 -0
- package/dist/slash-commands/memory-formatters.d.ts.map +1 -0
- package/dist/slash-commands/memory.d.ts.map +1 -1
- package/dist/slash-commands/mouse.d.ts +2 -2
- package/dist/slash-commands/review.d.ts.map +1 -1
- package/dist/slash-commands/sdd-command-helpers.d.ts +23 -0
- package/dist/slash-commands/sdd-command-helpers.d.ts.map +1 -0
- package/dist/slash-commands/sdd-format.d.ts +32 -0
- package/dist/slash-commands/sdd-format.d.ts.map +1 -0
- package/dist/slash-commands/sdd.d.ts.map +1 -1
- package/dist/slash-commands/settings.d.ts.map +1 -1
- package/dist/subcommands/handlers/chronicle.d.ts.map +1 -1
- package/dist/subcommands/handlers/mailbox-serve.d.ts.map +1 -1
- package/dist/subcommands/handlers/model-smoke-test.d.ts +51 -0
- package/dist/subcommands/handlers/model-smoke-test.d.ts.map +1 -0
- package/dist/subcommands/handlers/modeldiag-profiles.d.ts +64 -0
- package/dist/subcommands/handlers/modeldiag-profiles.d.ts.map +1 -0
- package/dist/subcommands/handlers/modeldiag.d.ts.map +1 -1
- package/dist/subcommands/handlers/per-subcommand-deep-help-table.d.ts +3 -0
- package/dist/subcommands/handlers/per-subcommand-deep-help-table.d.ts.map +1 -0
- package/dist/subcommands/handlers/per-subcommand-help-data.d.ts +3 -0
- package/dist/subcommands/handlers/per-subcommand-help-data.d.ts.map +1 -0
- package/dist/subcommands/handlers/per-subcommand-help-table.d.ts +36 -0
- package/dist/subcommands/handlers/per-subcommand-help-table.d.ts.map +1 -0
- package/dist/subcommands/handlers/per-subcommand-help-types.d.ts +18 -0
- package/dist/subcommands/handlers/per-subcommand-help-types.d.ts.map +1 -0
- package/dist/subcommands/handlers/per-subcommand-help.d.ts +2 -80
- package/dist/subcommands/handlers/per-subcommand-help.d.ts.map +1 -1
- package/dist/subcommands/handlers/rewind.d.ts.map +1 -1
- package/dist/subcommands/index.d.ts +7 -0
- package/dist/subcommands/index.d.ts.map +1 -1
- package/dist/tools-skills-UNKLOB7M.js +29 -0
- package/dist/tools-skills-UNKLOB7M.js.map +7 -0
- package/dist/update-YC4TYW2Y.js +16 -0
- package/dist/update-YC4TYW2Y.js.map +7 -0
- package/dist/update-check-UMYTOH7Z.js +18 -0
- package/dist/update-check-UMYTOH7Z.js.map +7 -0
- package/dist/version-help-DROHZRW6.js +11 -0
- package/dist/version-help-DROHZRW6.js.map +7 -0
- package/dist/webui-server/connection-handler.d.ts.map +1 -1
- package/dist/webui-server/kanban-host-adapter.d.ts +5 -1
- package/dist/webui-server/kanban-host-adapter.d.ts.map +1 -1
- package/dist/webui-server/listen-helpers.d.ts +18 -0
- package/dist/webui-server/listen-helpers.d.ts.map +1 -0
- package/dist/webui-server/node-pty-loader.d.ts +6 -0
- package/dist/webui-server/node-pty-loader.d.ts.map +1 -0
- package/dist/webui-server-ZF5KVGC5.js +1460 -0
- package/dist/webui-server-ZF5KVGC5.js.map +7 -0
- package/dist/webui-server-options.d.ts +191 -0
- package/dist/webui-server-options.d.ts.map +1 -0
- package/dist/webui-server.d.ts +2 -178
- package/dist/webui-server.d.ts.map +1 -1
- package/dist/wiring/brain-and-orchestration.d.ts.map +1 -1
- package/dist/wiring/codebase-index.d.ts.map +1 -1
- package/dist/wiring/command-host-state.d.ts +4 -4
- package/dist/wiring/dep-watcher-bridge.d.ts +23 -0
- package/dist/wiring/dep-watcher-bridge.d.ts.map +1 -0
- package/dist/wiring/dep-watcher.d.ts.map +1 -1
- package/dist/wiring/hq-telemetry.d.ts +2 -2
- package/dist/wiring/lifecycle-plugins.d.ts +4 -3
- package/dist/wiring/lifecycle-plugins.d.ts.map +1 -1
- package/dist/wiring/mailbox-bridge-bootstrap.d.ts +1 -1
- package/dist/wiring/management-tools.d.ts +15 -0
- package/dist/wiring/management-tools.d.ts.map +1 -0
- package/dist/wiring/pipeline.d.ts +2 -1
- package/dist/wiring/pipeline.d.ts.map +1 -1
- package/dist/wiring/plugins.d.ts +1 -1
- package/dist/wiring/plugins.d.ts.map +1 -1
- package/dist/wiring/provider-runtime-setup.d.ts +14 -1
- package/dist/wiring/provider-runtime-setup.d.ts.map +1 -1
- package/dist/wiring/provider.d.ts.map +1 -1
- package/dist/wiring/runtime-controller-deps.d.ts +1 -1
- package/dist/wiring/runtime-controller-deps.d.ts.map +1 -1
- package/dist/wiring/sage.d.ts.map +1 -1
- package/dist/wiring/session-registry.d.ts +38 -0
- package/dist/wiring/session-registry.d.ts.map +1 -0
- package/dist/wiring/session.d.ts +10 -6
- package/dist/wiring/session.d.ts.map +1 -1
- package/dist/wiring/tools.d.ts.map +1 -1
- package/package.json +22 -22
- package/dist/cli-recovery-prompt.d.ts +0 -19
- package/dist/cli-recovery-prompt.d.ts.map +0 -1
- package/dist/first-run-auth-gate.d.ts +0 -20
- package/dist/first-run-auth-gate.d.ts.map +0 -1
- package/dist/pre-launch/indexing-question.d.ts +0 -28
- package/dist/pre-launch/indexing-question.d.ts.map +0 -1
|
@@ -0,0 +1,1460 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createKanbanRunMirror
|
|
3
|
+
} from "./chunk-VCYZ3WZV.js";
|
|
4
|
+
import {
|
|
5
|
+
createHqCommandDispatcher
|
|
6
|
+
} from "./chunk-OP47B3IX.js";
|
|
7
|
+
import {
|
|
8
|
+
startCliHqConnection
|
|
9
|
+
} from "./chunk-WY7KKOUK.js";
|
|
10
|
+
import {
|
|
11
|
+
loadConfigProviders,
|
|
12
|
+
mutateConfigProviders
|
|
13
|
+
} from "./chunk-PJHET45A.js";
|
|
14
|
+
import "./chunk-7OCVIDC7.js";
|
|
15
|
+
|
|
16
|
+
// src/webui-server.ts
|
|
17
|
+
import { findPackageJSON } from "node:module";
|
|
18
|
+
import * as os from "node:os";
|
|
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
|
+
import { TOKENS } from "@wrongstack/core/kernel";
|
|
24
|
+
import { createCompatibilityTrustBoundary } from "@wrongstack/core/security";
|
|
25
|
+
import { SkillInstaller } from "@wrongstack/core/skills";
|
|
26
|
+
import { toErrorMessage } from "@wrongstack/core/utils/error";
|
|
27
|
+
import { makeProviderFromConfig } from "@wrongstack/providers";
|
|
28
|
+
import {
|
|
29
|
+
buildSddWizardDeps,
|
|
30
|
+
buildWebUIAccessUrl,
|
|
31
|
+
createCustomModeStore,
|
|
32
|
+
createEmbeddedMessageRouter,
|
|
33
|
+
createEmbeddedProviderOperations,
|
|
34
|
+
createMailboxRouteHandlers,
|
|
35
|
+
envFlag,
|
|
36
|
+
findFreePort,
|
|
37
|
+
GoalWebSocketHandler,
|
|
38
|
+
resolveAuthToken,
|
|
39
|
+
SddBoardWebSocketHandler,
|
|
40
|
+
SddWizardWebSocketHandler,
|
|
41
|
+
SpecsWebSocketHandler,
|
|
42
|
+
TerminalWebSocketHandler,
|
|
43
|
+
WorktreeWebSocketHandler
|
|
44
|
+
} from "@wrongstack/webui-server";
|
|
45
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
46
|
+
|
|
47
|
+
// src/webui-server/client-registration.ts
|
|
48
|
+
import { createWebuiClientPresence } from "@wrongstack/webui-server";
|
|
49
|
+
function createWebuiClientRegistration(deps) {
|
|
50
|
+
const control = deps.hqControl;
|
|
51
|
+
return createWebuiClientPresence({
|
|
52
|
+
projectRoot: deps.projectRoot,
|
|
53
|
+
appConfig: deps.appConfig,
|
|
54
|
+
events: deps.events,
|
|
55
|
+
hqSessionId: deps.hqSessionId,
|
|
56
|
+
getSessionId: deps.getSessionId,
|
|
57
|
+
startHqConnection: (options) => startCliHqConnection({
|
|
58
|
+
...options,
|
|
59
|
+
capabilities: options.capabilities
|
|
60
|
+
}),
|
|
61
|
+
...control ? {
|
|
62
|
+
createCommandHandler: (mailbox) => createHqCommandDispatcher({
|
|
63
|
+
steerMailbox: mailbox,
|
|
64
|
+
interruptLeader: control.interruptLeader,
|
|
65
|
+
allowRunCommand: control.allowRunCommand,
|
|
66
|
+
sessionTag: deps.getSessionId,
|
|
67
|
+
...control.spawnAgent ? { spawnAgent: control.spawnAgent } : {},
|
|
68
|
+
...control.killFleet ? { killFleet: control.killFleet } : {},
|
|
69
|
+
...control.terminateAgent ? { terminateAgent: control.terminateAgent } : {}
|
|
70
|
+
})
|
|
71
|
+
} : {}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/webui-server/connection-handler.ts
|
|
76
|
+
import {
|
|
77
|
+
createConnectionLifecycle,
|
|
78
|
+
verifyClient as verifyWsClient
|
|
79
|
+
} from "@wrongstack/webui-server";
|
|
80
|
+
import { buildReplayPayload, decodeProtocolFrame } from "@wrongstack/webui-server/protocol";
|
|
81
|
+
function createConnectionHandler(deps) {
|
|
82
|
+
const clientHandlers = [
|
|
83
|
+
deps.goalHandler,
|
|
84
|
+
deps.specsHandler,
|
|
85
|
+
deps.sddBoardHandler,
|
|
86
|
+
...deps.sddWizardHandler ? [deps.sddWizardHandler] : [],
|
|
87
|
+
deps.worktreeHandler,
|
|
88
|
+
deps.terminalHandler
|
|
89
|
+
];
|
|
90
|
+
return createConnectionLifecycle({
|
|
91
|
+
clients: deps.clients,
|
|
92
|
+
pendingConfirms: deps.pendingConfirms,
|
|
93
|
+
// ── Auth trust model ────────────────────────────────────────────────
|
|
94
|
+
// Three layered defenses (see verifyClient in @wrongstack/webui-server/ws-auth):
|
|
95
|
+
// 1. DNS-rebinding guard (Host header must be loopback on loopback bind)
|
|
96
|
+
// 2. Shared-token auth via Cookie (ws_token) or URL query param (?token=)
|
|
97
|
+
// 3. Loopback bootstrap: when requireToken=false (default) AND the server
|
|
98
|
+
// is bound to a loopback interface, browser clients bypass token auth
|
|
99
|
+
// entirely — the Origin header being http://localhost is sufficient.
|
|
100
|
+
//
|
|
101
|
+
// COOKIE PERSISTENCE AFTER TOKEN ROTATION:
|
|
102
|
+
// The HttpOnly ws_token cookie, once set by /ws-auth, survives a process
|
|
103
|
+
// restart. On the next process the expectedToken is a fresh random value,
|
|
104
|
+
// so the old cookie alone would fail the token check. However, on a
|
|
105
|
+
// loopback bind with requireToken=false (default), the loopback-bootstrap
|
|
106
|
+
// path at layer 3 accepts the connection before the cookie is ever
|
|
107
|
+
// compared — the old cookie is harmless because no token is needed.
|
|
108
|
+
//
|
|
109
|
+
// This means a token rotation (e.g. the operator sets a new accessToken
|
|
110
|
+
// in config) does NOT invalidate already-distributed cookies for loopback
|
|
111
|
+
// clients. The mitigating factors:
|
|
112
|
+
// - Loopback bind + requireToken=false already trusts any local process
|
|
113
|
+
// that can reach 127.0.0.1 — a trivial perimeter.
|
|
114
|
+
// - A LAN/0.0.0.0 bind with requireToken=true DOES enforce cookie
|
|
115
|
+
// matching against the new expectedToken on every reconnect.
|
|
116
|
+
// If stronger isolation is needed, set requireToken=true or rebind the
|
|
117
|
+
// server to a non-loopback interface with token enforcement.
|
|
118
|
+
authenticate: (ws, req) => {
|
|
119
|
+
const allowed = verifyWsClient({
|
|
120
|
+
origin: req.headers.origin,
|
|
121
|
+
url: req.url ?? "/",
|
|
122
|
+
hostHeader: req.headers.host,
|
|
123
|
+
remoteAddress: req.socket.remoteAddress,
|
|
124
|
+
cookieHeader: req.headers.cookie,
|
|
125
|
+
wsHost: deps.host,
|
|
126
|
+
expectedToken: deps.wsToken,
|
|
127
|
+
requireToken: deps.requireToken,
|
|
128
|
+
allowedHostnames: deps.publicHostnames,
|
|
129
|
+
allowBrowserUrlToken: Boolean(deps.publicWsUrl)
|
|
130
|
+
});
|
|
131
|
+
if (!allowed) ws.close(4003, "Forbidden");
|
|
132
|
+
return allowed;
|
|
133
|
+
},
|
|
134
|
+
createClient: (ws) => ({ ws, sessionId: deps.currentSessionId() }),
|
|
135
|
+
// Enrich parse-failure / unknown-type logs with per-connection identity
|
|
136
|
+
// so operators can identify which client sent a malformed frame. The
|
|
137
|
+
// `connectionId`/`sessionId` fields are read from the `client` argument
|
|
138
|
+
// directly inside `logRejection`; the CLI's `ConnectedClient` only has
|
|
139
|
+
// `sessionId` (no per-socket `connId`), so the verbose payload carries
|
|
140
|
+
// `sessionId` only. Server-wide identity (`agentId`/`projectRoot`) isn't
|
|
141
|
+
// in scope at this handler and is omitted.
|
|
142
|
+
verboseWsLogging: true,
|
|
143
|
+
registerClient: (ws) => {
|
|
144
|
+
for (const handler of clientHandlers) handler.addClient(ws);
|
|
145
|
+
},
|
|
146
|
+
decode: (raw) => decodeProtocolFrame(raw, "client"),
|
|
147
|
+
dispatch: deps.handleMessage,
|
|
148
|
+
send: deps.send,
|
|
149
|
+
sessionPayload: deps.sessionPayload,
|
|
150
|
+
rateLimitMax: deps.rateLimitMax,
|
|
151
|
+
onClose: (ws) => deps.abortControllers.delete(ws),
|
|
152
|
+
buildInitialPayload: async () => {
|
|
153
|
+
const payload = { ...await deps.buildSessionStartPayload({}, deps.needsSetup) };
|
|
154
|
+
try {
|
|
155
|
+
const replay = await deps.loadReplay?.();
|
|
156
|
+
if (replay) Object.assign(payload, buildReplayPayload(replay));
|
|
157
|
+
} catch {
|
|
158
|
+
console.debug("[WebUI] Failed to load replay");
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const sessions = await deps.loadAgentSessions?.();
|
|
162
|
+
if (sessions?.length) payload["agentSessions"] = sessions;
|
|
163
|
+
} catch {
|
|
164
|
+
console.debug("[WebUI] Failed to load agent sessions");
|
|
165
|
+
}
|
|
166
|
+
return payload;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/webui-server/kanban-host-adapter.ts
|
|
172
|
+
import { PhaseGraphBuilder } from "@wrongstack/core/goal";
|
|
173
|
+
import { resolveWstackPaths } from "@wrongstack/core/utils";
|
|
174
|
+
import { exportBoardToTaskGraph } from "@wrongstack/kanban";
|
|
175
|
+
import {
|
|
176
|
+
startSddRunFromGraph
|
|
177
|
+
} from "@wrongstack/webui-server";
|
|
178
|
+
function createCliKanbanHostRoutes(deps) {
|
|
179
|
+
const { opts, send, goalHandler } = deps;
|
|
180
|
+
let isolatedSeq = 0;
|
|
181
|
+
const runIsolatedTurn = opts.sddSubagentFactory ? async (prompt, name) => {
|
|
182
|
+
const result = await opts.sddSubagentFactory({
|
|
183
|
+
id: `sdd-kanban-${name.toLowerCase().replace(/\s+/g, "-")}-${isolatedSeq++}`,
|
|
184
|
+
role: "executor",
|
|
185
|
+
name,
|
|
186
|
+
disabledTools: ["delegate", "write", "edit", "patch", "bash", "exec"],
|
|
187
|
+
allowedCapabilities: ["fs.read", "net.outbound"]
|
|
188
|
+
});
|
|
189
|
+
try {
|
|
190
|
+
const res = await result.agent.run([{ type: "text", text: prompt }]);
|
|
191
|
+
return res.finalText ?? "";
|
|
192
|
+
} finally {
|
|
193
|
+
await result.dispose?.();
|
|
194
|
+
}
|
|
195
|
+
} : void 0;
|
|
196
|
+
return {
|
|
197
|
+
meta: async (ws) => {
|
|
198
|
+
const tools = (opts.agent.ctx.tools ?? []).map((tool) => ({
|
|
199
|
+
name: tool.name,
|
|
200
|
+
description: tool.description ?? ""
|
|
201
|
+
}));
|
|
202
|
+
const skills = opts.skillLoader ? (await opts.skillLoader.list()).map((skill) => ({
|
|
203
|
+
name: skill.name,
|
|
204
|
+
description: skill.description,
|
|
205
|
+
source: skill.source
|
|
206
|
+
})) : [];
|
|
207
|
+
send(ws, {
|
|
208
|
+
type: "kanban.meta",
|
|
209
|
+
payload: {
|
|
210
|
+
success: true,
|
|
211
|
+
data: {
|
|
212
|
+
tools,
|
|
213
|
+
skills,
|
|
214
|
+
fallbackProfiles: opts.appConfig?.fallbackProfiles ?? {},
|
|
215
|
+
sessionProvider: opts.agent.ctx.provider,
|
|
216
|
+
sessionModel: opts.agent.ctx.model
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
supervisorStatus: async (ws, msg) => {
|
|
222
|
+
const boardId = msg.payload?.boardId;
|
|
223
|
+
if (!boardId || !deps.kanbanSupervisor) {
|
|
224
|
+
send(ws, {
|
|
225
|
+
type: "kanban.supervisor.status",
|
|
226
|
+
payload: { success: false, error: "Kanban supervisor is unavailable." }
|
|
227
|
+
});
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const snapshot = deps.kanbanSupervisor.getSnapshot(boardId) ?? (await deps.kanbanSupervisor.auditNow(boardId))[0];
|
|
231
|
+
send(ws, {
|
|
232
|
+
type: "kanban.supervisor.status",
|
|
233
|
+
payload: { success: true, data: snapshot ?? null }
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
supervisorAudit: async (ws, msg) => {
|
|
237
|
+
const boardId = msg.payload?.boardId;
|
|
238
|
+
if (!deps.kanbanSupervisor) {
|
|
239
|
+
send(ws, {
|
|
240
|
+
type: "kanban.supervisor.audit",
|
|
241
|
+
payload: { success: false, error: "Kanban supervisor is unavailable." }
|
|
242
|
+
});
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const snapshots = await deps.kanbanSupervisor.auditNow(boardId);
|
|
246
|
+
send(ws, {
|
|
247
|
+
type: "kanban.supervisor.audit",
|
|
248
|
+
payload: { success: true, data: boardId ? snapshots[0] ?? null : snapshots }
|
|
249
|
+
});
|
|
250
|
+
},
|
|
251
|
+
runStart: async (ws, msg) => {
|
|
252
|
+
const payload = msg.payload ?? {};
|
|
253
|
+
const boardId = payload.boardId;
|
|
254
|
+
const engine = payload.engine === "goal" ? "goal" : payload.engine === "sdd" ? "sdd" : void 0;
|
|
255
|
+
const projectRoot = opts.projectRoot ?? "";
|
|
256
|
+
const failRun = (error) => send(ws, { type: "kanban.run.start", payload: { success: false, error } });
|
|
257
|
+
if (!boardId || !engine) return failRun("boardId and engine required");
|
|
258
|
+
if (!projectRoot) return failRun("No project root");
|
|
259
|
+
try {
|
|
260
|
+
const exported = await exportBoardToTaskGraph(projectRoot, boardId, {
|
|
261
|
+
preserveOriginTaskIds: true
|
|
262
|
+
});
|
|
263
|
+
if (!exported) return failRun(`Board not found: ${boardId}`);
|
|
264
|
+
if (exported.graph.nodes.size === 0) return failRun("Board has no tasks to run");
|
|
265
|
+
if (engine === "goal") {
|
|
266
|
+
deps.kanbanRunMirror?.bindGoalNext(boardId);
|
|
267
|
+
const graph = await PhaseGraphBuilder.fromTaskGraph(exported.graph, {
|
|
268
|
+
title: exported.board.title,
|
|
269
|
+
description: exported.board.description ?? exported.board.title
|
|
270
|
+
});
|
|
271
|
+
const phases = Array.from(graph.phases.values()).map((phase) => ({
|
|
272
|
+
name: phase.name,
|
|
273
|
+
description: phase.description,
|
|
274
|
+
priority: phase.priority,
|
|
275
|
+
estimateHours: phase.estimateHours,
|
|
276
|
+
parallelizable: phase.parallelizable,
|
|
277
|
+
taskTemplates: Array.from(phase.taskGraph.nodes.values()).map((task) => ({
|
|
278
|
+
title: task.title,
|
|
279
|
+
description: task.description,
|
|
280
|
+
type: task.type,
|
|
281
|
+
priority: task.priority,
|
|
282
|
+
estimateHours: task.estimateHours ?? 0,
|
|
283
|
+
...task.tags ? { tags: task.tags } : {}
|
|
284
|
+
}))
|
|
285
|
+
}));
|
|
286
|
+
await goalHandler.handleMessage(ws, {
|
|
287
|
+
type: "goal.start",
|
|
288
|
+
payload: {
|
|
289
|
+
title: exported.board.title,
|
|
290
|
+
phases,
|
|
291
|
+
autonomous: payload.autonomous ?? false
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
send(ws, {
|
|
295
|
+
type: "kanban.run.start",
|
|
296
|
+
payload: { success: true, data: { engine: "goal", boardId } }
|
|
297
|
+
});
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (!opts.sddSubagentFactory) {
|
|
301
|
+
return failRun("SDD runs need the multi-agent host, which is not available here.");
|
|
302
|
+
}
|
|
303
|
+
const paths = resolveWstackPaths({ projectRoot });
|
|
304
|
+
const handle = await startSddRunFromGraph(
|
|
305
|
+
exported.graph,
|
|
306
|
+
{
|
|
307
|
+
agent: opts.agent,
|
|
308
|
+
events: opts.events,
|
|
309
|
+
projectRoot,
|
|
310
|
+
subagentFactory: opts.sddSubagentFactory,
|
|
311
|
+
projectSddBoards: paths.projectSddBoards,
|
|
312
|
+
...opts.brain ? { brain: opts.brain } : {},
|
|
313
|
+
...runIsolatedTurn ? { runIsolatedTurn } : {}
|
|
314
|
+
},
|
|
315
|
+
{ worktrees: true }
|
|
316
|
+
);
|
|
317
|
+
deps.kanbanRunMirror?.bind("sdd", handle.runId, boardId);
|
|
318
|
+
void handle.completion.catch(() => {
|
|
319
|
+
});
|
|
320
|
+
const started = {
|
|
321
|
+
type: "sdd.run.started",
|
|
322
|
+
payload: { runId: handle.runId, graphId: exported.graph.id }
|
|
323
|
+
};
|
|
324
|
+
send(ws, started);
|
|
325
|
+
deps.broadcast?.(started);
|
|
326
|
+
send(ws, {
|
|
327
|
+
type: "kanban.run.start",
|
|
328
|
+
payload: { success: true, data: { engine: "sdd", boardId, runId: handle.runId } }
|
|
329
|
+
});
|
|
330
|
+
} catch (err) {
|
|
331
|
+
failRun(err instanceof Error ? err.message : String(err));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/webui-server/kanban-supervisor.ts
|
|
338
|
+
import {
|
|
339
|
+
createKanbanSupervisor
|
|
340
|
+
} from "@wrongstack/webui-server";
|
|
341
|
+
|
|
342
|
+
// src/webui-server/lifecycle.ts
|
|
343
|
+
import {
|
|
344
|
+
announceWebuiReady,
|
|
345
|
+
createWebuiShutdown,
|
|
346
|
+
registerWebuiInstance,
|
|
347
|
+
registerWebuiSignalHandlers
|
|
348
|
+
} from "@wrongstack/webui-server";
|
|
349
|
+
|
|
350
|
+
// src/webui-server/logger-shim.ts
|
|
351
|
+
var structuredLine = (level, message) => JSON.stringify({
|
|
352
|
+
level,
|
|
353
|
+
event: "webui.goal",
|
|
354
|
+
message,
|
|
355
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
356
|
+
});
|
|
357
|
+
var consoleLogger = {
|
|
358
|
+
level: "debug",
|
|
359
|
+
error(msg, _ctx) {
|
|
360
|
+
console.error(structuredLine("error", msg));
|
|
361
|
+
},
|
|
362
|
+
warn(msg, _ctx) {
|
|
363
|
+
console.warn(structuredLine("warn", msg));
|
|
364
|
+
},
|
|
365
|
+
info(msg, _ctx) {
|
|
366
|
+
console.log(structuredLine("info", msg));
|
|
367
|
+
},
|
|
368
|
+
debug(msg, _ctx) {
|
|
369
|
+
console.debug(structuredLine("debug", msg));
|
|
370
|
+
},
|
|
371
|
+
trace(msg, _ctx) {
|
|
372
|
+
console.debug(structuredLine("trace", msg));
|
|
373
|
+
},
|
|
374
|
+
child(_bindings) {
|
|
375
|
+
return this;
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// src/webui-server/prefs-seeding.ts
|
|
380
|
+
import * as fs from "node:fs/promises";
|
|
381
|
+
import * as path from "node:path";
|
|
382
|
+
import { DefaultSecretVault } from "@wrongstack/core/security";
|
|
383
|
+
import {
|
|
384
|
+
PREF_KEYS,
|
|
385
|
+
persistPrefsToConfig,
|
|
386
|
+
prefSnapshot as snapshotPrefs,
|
|
387
|
+
seedContextMeta
|
|
388
|
+
} from "@wrongstack/webui-server";
|
|
389
|
+
async function seedConfigToMeta(opts) {
|
|
390
|
+
let config = opts.appConfig;
|
|
391
|
+
if (!config || Object.keys(config).length === 0) {
|
|
392
|
+
if (!opts.profileConfigPath) return;
|
|
393
|
+
try {
|
|
394
|
+
config = JSON.parse(await fs.readFile(opts.profileConfigPath, "utf8"));
|
|
395
|
+
} catch {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
seedContextMeta(config, { meta: opts.agent.ctx.meta });
|
|
400
|
+
}
|
|
401
|
+
function createPrefsSeeding(opts) {
|
|
402
|
+
const writeLock = { lock: Promise.resolve() };
|
|
403
|
+
const patchLiveAppConfig = (patch) => {
|
|
404
|
+
if (!opts.appConfig) return;
|
|
405
|
+
opts.appConfig = { ...opts.appConfig, ...patch };
|
|
406
|
+
};
|
|
407
|
+
const persistPrefs = async (payload) => {
|
|
408
|
+
if (Array.isArray(payload["fallbackModels"])) {
|
|
409
|
+
patchLiveAppConfig({ fallbackModels: payload["fallbackModels"] });
|
|
410
|
+
}
|
|
411
|
+
if (payload["fallbackProfiles"] && typeof payload["fallbackProfiles"] === "object" && !Array.isArray(payload["fallbackProfiles"])) {
|
|
412
|
+
patchLiveAppConfig({
|
|
413
|
+
fallbackProfiles: payload["fallbackProfiles"]
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
if (Array.isArray(payload["favoriteModels"])) {
|
|
417
|
+
patchLiveAppConfig({ favoriteModels: payload["favoriteModels"] });
|
|
418
|
+
}
|
|
419
|
+
if (typeof payload["favoriteModelsOnly"] === "boolean") {
|
|
420
|
+
patchLiveAppConfig({ favoriteModelsOnly: payload["favoriteModelsOnly"] });
|
|
421
|
+
}
|
|
422
|
+
if (Array.isArray(payload["modelAvailabilitySchedule"])) {
|
|
423
|
+
patchLiveAppConfig({
|
|
424
|
+
modelAvailabilitySchedule: payload["modelAvailabilitySchedule"]
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
if (typeof payload["fallbackAuto"] === "boolean") {
|
|
428
|
+
patchLiveAppConfig({ fallbackAuto: payload["fallbackAuto"] });
|
|
429
|
+
}
|
|
430
|
+
if (typeof payload["uiLocale"] === "string") {
|
|
431
|
+
patchLiveAppConfig({ uiLocale: payload["uiLocale"] });
|
|
432
|
+
}
|
|
433
|
+
if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"])) {
|
|
434
|
+
patchLiveAppConfig({ modelMatrix: payload["modelMatrix"] });
|
|
435
|
+
}
|
|
436
|
+
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") });
|
|
439
|
+
await persistPrefsToConfig(
|
|
440
|
+
{
|
|
441
|
+
profileConfigPath: opts.profileConfigPath,
|
|
442
|
+
vault,
|
|
443
|
+
logger: {
|
|
444
|
+
warn(message) {
|
|
445
|
+
console.error(
|
|
446
|
+
JSON.stringify({
|
|
447
|
+
level: "warn",
|
|
448
|
+
event: "webui.prefs.persist_failed",
|
|
449
|
+
message,
|
|
450
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
451
|
+
})
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
writeLock,
|
|
457
|
+
payload
|
|
458
|
+
);
|
|
459
|
+
};
|
|
460
|
+
return {
|
|
461
|
+
prefSnapshot: () => snapshotPrefs(opts.agent.ctx.meta),
|
|
462
|
+
persistPrefs
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// src/webui-server/provider-config.ts
|
|
467
|
+
import * as path2 from "node:path";
|
|
468
|
+
import { DefaultSecretVault as DefaultSecretVault2 } from "@wrongstack/core/security";
|
|
469
|
+
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");
|
|
474
|
+
return new DefaultSecretVault2({ keyFile });
|
|
475
|
+
}
|
|
476
|
+
async function loadSavedProviders(globalConfigPath) {
|
|
477
|
+
if (!globalConfigPath) return {};
|
|
478
|
+
return loadConfigProviders(globalConfigPath, getVault(globalConfigPath));
|
|
479
|
+
}
|
|
480
|
+
async function saveProviders(globalConfigPath, providers) {
|
|
481
|
+
if (!globalConfigPath) return;
|
|
482
|
+
await mutateConfigProviders(
|
|
483
|
+
globalConfigPath,
|
|
484
|
+
getVault(globalConfigPath),
|
|
485
|
+
(existing) => {
|
|
486
|
+
for (const key of Object.keys(existing)) delete existing[key];
|
|
487
|
+
Object.assign(existing, providers);
|
|
488
|
+
}
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
function createProviderConfigStore(globalConfigPath, configProvidersRef) {
|
|
492
|
+
return {
|
|
493
|
+
load: () => configProvidersRef ? Promise.resolve(configProvidersRef()) : loadSavedProviders(globalConfigPath),
|
|
494
|
+
save: (providers) => saveProviders(globalConfigPath, providers)
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/webui-server/session-start-payload.ts
|
|
499
|
+
import * as path3 from "node:path";
|
|
500
|
+
import { DEFAULT_CONTEXT_WINDOW_MODE_ID } from "@wrongstack/core/types";
|
|
501
|
+
import { protocolAdvertisement } from "@wrongstack/webui-server/protocol";
|
|
502
|
+
|
|
503
|
+
// src/webui-server/cost-helpers.ts
|
|
504
|
+
import {
|
|
505
|
+
computeUsageCost,
|
|
506
|
+
getCostRates
|
|
507
|
+
} from "@wrongstack/webui-server";
|
|
508
|
+
|
|
509
|
+
// src/webui-server/session-start-payload.ts
|
|
510
|
+
function createSessionStartPayloadBuilder(deps) {
|
|
511
|
+
return async function buildSessionStartPayload(overrides, needsSetup = false) {
|
|
512
|
+
let maxContext = 0;
|
|
513
|
+
let inputCost = 0;
|
|
514
|
+
let outputCost = 0;
|
|
515
|
+
let cacheReadCost = 0;
|
|
516
|
+
try {
|
|
517
|
+
if (deps.modelsRegistry) {
|
|
518
|
+
const m = await deps.modelsRegistry.getModel(
|
|
519
|
+
deps.agent.ctx.provider.id,
|
|
520
|
+
deps.agent.ctx.model
|
|
521
|
+
);
|
|
522
|
+
const registryMax = m?.capabilities.maxContext;
|
|
523
|
+
maxContext = registryMax ?? deps.agent.ctx.provider.capabilities?.maxContext ?? 0;
|
|
524
|
+
const rates = getCostRates(m);
|
|
525
|
+
inputCost = rates.input;
|
|
526
|
+
outputCost = rates.output;
|
|
527
|
+
cacheReadCost = rates.cacheRead;
|
|
528
|
+
} else {
|
|
529
|
+
maxContext = deps.agent.ctx.provider.capabilities?.maxContext ?? 0;
|
|
530
|
+
}
|
|
531
|
+
} catch {
|
|
532
|
+
}
|
|
533
|
+
const lastInputTokens = typeof deps.agent.ctx.lastRequestTokens === "number" && deps.agent.ctx.lastRequestTokens > 0 ? deps.agent.ctx.lastRequestTokens : 0;
|
|
534
|
+
return {
|
|
535
|
+
sessionId: deps.agent.ctx.session?.id ?? deps.session.id,
|
|
536
|
+
model: deps.agent.ctx.model,
|
|
537
|
+
provider: deps.agent.ctx.provider.id,
|
|
538
|
+
mode: deps.modeId ?? "default",
|
|
539
|
+
projectName: deps.projectRoot ? path3.basename(deps.projectRoot) : void 0,
|
|
540
|
+
// Frontend reads `projectRoot` from session.start (ws-handlers setEnv) —
|
|
541
|
+
// omitting it left the store's projectRoot empty after a project switch.
|
|
542
|
+
projectRoot: deps.projectRoot ?? deps.agent.ctx.projectRoot ?? "",
|
|
543
|
+
cwd: deps.projectRoot ?? deps.agent.ctx.projectRoot ?? "",
|
|
544
|
+
needsSetup,
|
|
545
|
+
// true when provider/model not configured and running in --webui mode
|
|
546
|
+
contextMode: String(
|
|
547
|
+
deps.agent.ctx.meta?.["contextWindowMode"] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID
|
|
548
|
+
),
|
|
549
|
+
maxContext,
|
|
550
|
+
inputCost,
|
|
551
|
+
outputCost,
|
|
552
|
+
cacheReadCost,
|
|
553
|
+
lastInputTokens,
|
|
554
|
+
providerStatuses: deps.statusTracker?.getAllStatuses() ?? [],
|
|
555
|
+
...protocolAdvertisement(),
|
|
556
|
+
...overrides
|
|
557
|
+
};
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// src/webui-server/setup-events.ts
|
|
562
|
+
import {
|
|
563
|
+
createEternalSubscription,
|
|
564
|
+
setupEvents as setupCanonicalEvents
|
|
565
|
+
} from "@wrongstack/webui-server";
|
|
566
|
+
function createSetupEvents(deps) {
|
|
567
|
+
let fleetConcurrency = 0;
|
|
568
|
+
let fleetConcurrencyMax = 4;
|
|
569
|
+
const emitConcurrency = () => {
|
|
570
|
+
deps.broadcast({
|
|
571
|
+
type: "fleet.concurrency_update",
|
|
572
|
+
payload: deps.sessionPayload({ fleetConcurrency, fleetConcurrencyMax })
|
|
573
|
+
});
|
|
574
|
+
};
|
|
575
|
+
return function setupEvents() {
|
|
576
|
+
for (const unsubscribe of deps.eventUnsubscribers) unsubscribe();
|
|
577
|
+
deps.eventUnsubscribers.length = 0;
|
|
578
|
+
const maxIterations = deps.agent.ctx?.meta?.["maxIterations"];
|
|
579
|
+
const disposeCanonical = setupCanonicalEvents({
|
|
580
|
+
events: deps.events,
|
|
581
|
+
broadcast: (_clients, message) => deps.broadcast(message),
|
|
582
|
+
clients: deps.getClients(),
|
|
583
|
+
config: {
|
|
584
|
+
tools: {
|
|
585
|
+
...typeof maxIterations === "number" ? { maxIterations } : {}
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
context: deps.agent.ctx,
|
|
589
|
+
pendingConfirms: deps.pendingConfirms,
|
|
590
|
+
globalConfigPath: deps.globalConfigPath,
|
|
591
|
+
onFleetBroadcaster: deps.onFleetBroadcaster,
|
|
592
|
+
projection: {
|
|
593
|
+
scrubObject: (value) => deps.secretScrubber.scrubObject(value),
|
|
594
|
+
queueTextDelta: deps.queueTextDelta,
|
|
595
|
+
queueThinkingDelta: deps.queueThinkingDelta,
|
|
596
|
+
queueToolProgress: (payload) => deps.queueToolProgress(payload),
|
|
597
|
+
flushThinkingDelta: deps.flushThinkingDelta,
|
|
598
|
+
flushAllStreamBuffers: deps.flushAllStreamBuffers
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
deps.eventUnsubscribers.push(disposeCanonical);
|
|
602
|
+
deps.broadcast({
|
|
603
|
+
type: "subagent.event",
|
|
604
|
+
payload: deps.sessionPayload({
|
|
605
|
+
kind: "leader_updated",
|
|
606
|
+
subagentId: "leader",
|
|
607
|
+
isLeader: true,
|
|
608
|
+
name: "Leader",
|
|
609
|
+
status: "running"
|
|
610
|
+
})
|
|
611
|
+
});
|
|
612
|
+
emitConcurrency();
|
|
613
|
+
deps.eventUnsubscribers.push(
|
|
614
|
+
deps.events.on("concurrency.changed", (event) => {
|
|
615
|
+
fleetConcurrencyMax = Math.max(1, event.n);
|
|
616
|
+
emitConcurrency();
|
|
617
|
+
}),
|
|
618
|
+
deps.events.on("subagent.spawned", () => {
|
|
619
|
+
fleetConcurrency += 1;
|
|
620
|
+
emitConcurrency();
|
|
621
|
+
}),
|
|
622
|
+
deps.events.on("subagent.task_completed", () => {
|
|
623
|
+
fleetConcurrency = Math.max(0, fleetConcurrency - 1);
|
|
624
|
+
emitConcurrency();
|
|
625
|
+
})
|
|
626
|
+
);
|
|
627
|
+
if (deps.subscribeEternalIteration) {
|
|
628
|
+
const subscription = createEternalSubscription(
|
|
629
|
+
deps.subscribeEternalIteration,
|
|
630
|
+
(_clients, message) => deps.broadcast(message),
|
|
631
|
+
deps.getClients
|
|
632
|
+
);
|
|
633
|
+
deps.eventUnsubscribers.push(() => subscription.dispose());
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/webui-server/static-serve.ts
|
|
639
|
+
import {
|
|
640
|
+
ensureDistDir,
|
|
641
|
+
resolveDistDir,
|
|
642
|
+
startStaticServe
|
|
643
|
+
} from "@wrongstack/webui-server";
|
|
644
|
+
|
|
645
|
+
// src/webui-server/stream-coalescer.ts
|
|
646
|
+
import {
|
|
647
|
+
createStreamCoalescer
|
|
648
|
+
} from "@wrongstack/webui-server";
|
|
649
|
+
|
|
650
|
+
// src/webui-server/terminal-log-view.ts
|
|
651
|
+
import { format } from "node:util";
|
|
652
|
+
var DEFAULT_MAX_LINES = 5;
|
|
653
|
+
var DEFAULT_REFRESH_MS = 5e3;
|
|
654
|
+
var CLEAR_VISIBLE_TERMINAL = "\x1B[2J\x1B[H";
|
|
655
|
+
function startBoundedTerminalLogView(options = {}) {
|
|
656
|
+
const isTTY = options.isTTY ?? process.stdout.isTTY === true;
|
|
657
|
+
if (!isTTY) {
|
|
658
|
+
return { enabled: false, redraw: () => {
|
|
659
|
+
}, stop: () => {
|
|
660
|
+
} };
|
|
661
|
+
}
|
|
662
|
+
const maxLines = Math.max(1, Math.trunc(options.maxLines ?? DEFAULT_MAX_LINES));
|
|
663
|
+
const refreshMs = Math.max(100, Math.trunc(options.refreshMs ?? DEFAULT_REFRESH_MS));
|
|
664
|
+
const target = options.consoleTarget ?? console;
|
|
665
|
+
const write = options.write ?? ((chunk) => process.stdout.write(chunk));
|
|
666
|
+
const setIntervalFn = options.setIntervalFn ?? setInterval;
|
|
667
|
+
const clearIntervalFn = options.clearIntervalFn ?? clearInterval;
|
|
668
|
+
const lines = [];
|
|
669
|
+
let stopped = false;
|
|
670
|
+
const originals = {
|
|
671
|
+
log: target.log,
|
|
672
|
+
info: target.info,
|
|
673
|
+
warn: target.warn,
|
|
674
|
+
error: target.error,
|
|
675
|
+
debug: target.debug
|
|
676
|
+
};
|
|
677
|
+
const wrappers = {};
|
|
678
|
+
const remember = (args) => {
|
|
679
|
+
for (const line of format(...args).split(/\r?\n/u)) {
|
|
680
|
+
lines.push(line);
|
|
681
|
+
if (lines.length > maxLines) lines.shift();
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
for (const level of Object.keys(originals)) {
|
|
685
|
+
const original = originals[level];
|
|
686
|
+
const wrapper = (...args) => {
|
|
687
|
+
remember(args);
|
|
688
|
+
original.apply(target, args);
|
|
689
|
+
};
|
|
690
|
+
wrappers[level] = wrapper;
|
|
691
|
+
target[level] = wrapper;
|
|
692
|
+
}
|
|
693
|
+
const redraw = () => {
|
|
694
|
+
if (stopped || lines.length === 0) return;
|
|
695
|
+
write(`${CLEAR_VISIBLE_TERMINAL}${lines.join("\n")}
|
|
696
|
+
`);
|
|
697
|
+
};
|
|
698
|
+
const timer = setIntervalFn(redraw, refreshMs);
|
|
699
|
+
timer.unref?.();
|
|
700
|
+
return {
|
|
701
|
+
enabled: true,
|
|
702
|
+
redraw,
|
|
703
|
+
stop: () => {
|
|
704
|
+
if (stopped) return;
|
|
705
|
+
stopped = true;
|
|
706
|
+
clearIntervalFn(timer);
|
|
707
|
+
for (const level of Object.keys(originals)) {
|
|
708
|
+
if (target[level] === wrappers[level]) target[level] = originals[level];
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
|
|
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
|
+
// src/webui-server.ts
|
|
815
|
+
async function runWebUI(opts) {
|
|
816
|
+
const trustBoundary = opts.trustBoundary ?? createCompatibilityTrustBoundary({ policyId: "cli-webui-trusted-host-compat-v1" });
|
|
817
|
+
const host = opts.host ?? process.env["WEBUI_HOST"] ?? process.env["WS_HOST"] ?? "127.0.0.1";
|
|
818
|
+
const publicUrl = opts.publicUrl ?? process.env["WEBUI_PUBLIC_URL"];
|
|
819
|
+
const publicWsUrl = opts.publicWsUrl ?? process.env["WEBUI_PUBLIC_WS_URL"];
|
|
820
|
+
const requireToken = opts.requireToken ?? envFlag("WEBUI_REQUIRE_TOKEN");
|
|
821
|
+
const surface = opts.surface ?? "webui";
|
|
822
|
+
const surfaceDefaults = surface === "simpleui" ? { http: 3466 } : { http: 3456 };
|
|
823
|
+
const requestedHttpPort = opts.httpPort ?? opts.port ?? surfaceDefaults.http;
|
|
824
|
+
const strictPort = process.env["WEBUI_STRICT_PORT"] === "1" || process.env["WEBUI_STRICT_PORT"] === "true";
|
|
825
|
+
let httpPort = requestedHttpPort;
|
|
826
|
+
if (!strictPort) {
|
|
827
|
+
httpPort = await findFreePort(host, requestedHttpPort);
|
|
828
|
+
}
|
|
829
|
+
const wsPort = httpPort;
|
|
830
|
+
const globalRoot = opts.globalConfigPath ? path5.dirname(opts.globalConfigPath) : wstackGlobalRoot();
|
|
831
|
+
const profileConfigPath = opts.profileConfigPath ?? opts.globalConfigPath ?? path5.join(globalRoot, "config.json");
|
|
832
|
+
const rateLimitMax = Number.parseInt(process.env["WEBUI_RATE_LIMIT"] ?? "0", 10);
|
|
833
|
+
const clients = /* @__PURE__ */ new Map();
|
|
834
|
+
const pendingConfirms = /* @__PURE__ */ new Map();
|
|
835
|
+
const secretScrubber = new DefaultSecretScrubber();
|
|
836
|
+
let abortController = null;
|
|
837
|
+
const abortControllers = /* @__PURE__ */ new Map();
|
|
838
|
+
const profileDir = path5.dirname(profileConfigPath);
|
|
839
|
+
let customModeStoreP = null;
|
|
840
|
+
const getCustomModeStore = () => {
|
|
841
|
+
customModeStoreP ??= (async () => {
|
|
842
|
+
const store = createCustomModeStore(profileDir);
|
|
843
|
+
await store.load();
|
|
844
|
+
return store;
|
|
845
|
+
})();
|
|
846
|
+
return customModeStoreP;
|
|
847
|
+
};
|
|
848
|
+
const goalStoreDir = opts.projectRoot ? path5.join(opts.projectRoot, ".wrongstack", "goal") : path5.join(os.tmpdir(), ".wrongstack", "goal");
|
|
849
|
+
const kanbanRunMirror = opts.projectRoot ? createKanbanRunMirror({
|
|
850
|
+
projectRoot: opts.projectRoot,
|
|
851
|
+
events: opts.events,
|
|
852
|
+
broadcast,
|
|
853
|
+
log: (m) => consoleLogger.info(m)
|
|
854
|
+
}) : null;
|
|
855
|
+
const kanbanSupervisor = opts.projectRoot ? createKanbanSupervisor({
|
|
856
|
+
projectRoot: opts.projectRoot,
|
|
857
|
+
broadcast,
|
|
858
|
+
...opts.onKanbanDispatch ? { dispatchTask: opts.onKanbanDispatch } : {},
|
|
859
|
+
log: (message) => consoleLogger.info(message)
|
|
860
|
+
}) : null;
|
|
861
|
+
const goalHandler = new GoalWebSocketHandler(
|
|
862
|
+
opts.agent,
|
|
863
|
+
opts.agent.ctx,
|
|
864
|
+
consoleLogger,
|
|
865
|
+
goalStoreDir,
|
|
866
|
+
opts.events,
|
|
867
|
+
opts.projectRoot,
|
|
868
|
+
kanbanRunMirror ? (graphId, state) => kanbanRunMirror.onGoalState(graphId, state) : void 0
|
|
869
|
+
);
|
|
870
|
+
const worktreeHandler = new WorktreeWebSocketHandler(opts.events, consoleLogger);
|
|
871
|
+
const loadNodePtyViaWebui = createNodePtyLoader(import.meta.url, consoleLogger);
|
|
872
|
+
const terminalHandler = new TerminalWebSocketHandler(
|
|
873
|
+
() => opts.agent.ctx.cwd ?? opts.projectRoot ?? process.cwd(),
|
|
874
|
+
consoleLogger,
|
|
875
|
+
loadNodePtyViaWebui,
|
|
876
|
+
void 0,
|
|
877
|
+
trustBoundary
|
|
878
|
+
);
|
|
879
|
+
const specsPaths = opts.projectRoot ? resolveWstackPaths2({ projectRoot: opts.projectRoot }) : null;
|
|
880
|
+
const specsHandler = new SpecsWebSocketHandler(
|
|
881
|
+
specsPaths?.projectSpecs ?? path5.join(os.tmpdir(), ".wrongstack", "specs"),
|
|
882
|
+
specsPaths?.projectTaskGraphs ?? path5.join(os.tmpdir(), ".wrongstack", "task-graphs")
|
|
883
|
+
);
|
|
884
|
+
const sddBoardHandler = new SddBoardWebSocketHandler(
|
|
885
|
+
specsPaths?.projectSddBoards ?? path5.join(os.tmpdir(), ".wrongstack", "sdd-boards"),
|
|
886
|
+
opts.events,
|
|
887
|
+
specsPaths && opts.projectRoot ? {
|
|
888
|
+
projectRoot: opts.projectRoot,
|
|
889
|
+
paths: {
|
|
890
|
+
projectSpecs: specsPaths.projectSpecs,
|
|
891
|
+
projectTaskGraphs: specsPaths.projectTaskGraphs,
|
|
892
|
+
projectSddSession: specsPaths.projectSddSession,
|
|
893
|
+
projectSddBoards: specsPaths.projectSddBoards
|
|
894
|
+
}
|
|
895
|
+
} : void 0,
|
|
896
|
+
{ trustBoundary, logger: consoleLogger }
|
|
897
|
+
);
|
|
898
|
+
const sddWizardHandler = opts.sddSubagentFactory && specsPaths ? new SddWizardWebSocketHandler(
|
|
899
|
+
buildSddWizardDeps({
|
|
900
|
+
agent: opts.agent,
|
|
901
|
+
events: opts.events,
|
|
902
|
+
projectRoot: opts.projectRoot ?? process.cwd(),
|
|
903
|
+
subagentFactory: opts.sddSubagentFactory,
|
|
904
|
+
// Failure supervisor parity with /sdd execute — omit only when
|
|
905
|
+
// the host never bound a BrainArbiter.
|
|
906
|
+
...opts.brain ? { brain: opts.brain } : {},
|
|
907
|
+
paths: {
|
|
908
|
+
projectSpecs: specsPaths.projectSpecs,
|
|
909
|
+
projectTaskGraphs: specsPaths.projectTaskGraphs,
|
|
910
|
+
projectSddBoards: specsPaths.projectSddBoards,
|
|
911
|
+
projectDir: specsPaths.projectDir,
|
|
912
|
+
projectSddSession: specsPaths.projectSddSession
|
|
913
|
+
}
|
|
914
|
+
})
|
|
915
|
+
) : null;
|
|
916
|
+
await seedConfigToMeta(opts);
|
|
917
|
+
if (typeof opts.agent.ctx?.meta?.["yolo"] === "boolean") {
|
|
918
|
+
opts.onYoloSwitch?.(opts.agent.ctx.meta["yolo"]);
|
|
919
|
+
}
|
|
920
|
+
const { prefSnapshot, persistPrefs } = createPrefsSeeding(opts);
|
|
921
|
+
const sessionStartedAt = Date.now();
|
|
922
|
+
const buildSessionStartPayload = createSessionStartPayloadBuilder(opts);
|
|
923
|
+
const { register: registerWebuiClient, unregister: unregisterWebuiClient } = createWebuiClientRegistration({
|
|
924
|
+
projectRoot: opts.projectRoot,
|
|
925
|
+
appConfig: opts.appConfig,
|
|
926
|
+
events: opts.events,
|
|
927
|
+
hqSessionId: opts.session.id,
|
|
928
|
+
getSessionId: () => opts.agent.ctx.session?.id ?? opts.session.id,
|
|
929
|
+
// Two-way HQ control for the WebUI client: steer/btw/queue/broadcast
|
|
930
|
+
// land in the project mailbox (the WebUI's agent reads it like any
|
|
931
|
+
// other surface), and abort tears down every in-flight run on this
|
|
932
|
+
// instance. Without this block the WebUI would be HQ-invisible to the
|
|
933
|
+
// Control tab — telemetry only.
|
|
934
|
+
hqControl: {
|
|
935
|
+
interruptLeader: () => {
|
|
936
|
+
let aborted = false;
|
|
937
|
+
if (abortController) {
|
|
938
|
+
abortController.abort();
|
|
939
|
+
abortController = null;
|
|
940
|
+
aborted = true;
|
|
941
|
+
}
|
|
942
|
+
for (const c of abortControllers.values()) {
|
|
943
|
+
c.abort();
|
|
944
|
+
aborted = true;
|
|
945
|
+
}
|
|
946
|
+
abortControllers.clear();
|
|
947
|
+
return aborted;
|
|
948
|
+
},
|
|
949
|
+
allowRunCommand: () => opts.hqAllowExec === true
|
|
950
|
+
}
|
|
951
|
+
});
|
|
952
|
+
registerWebuiClient();
|
|
953
|
+
const wsToken = resolveAuthToken(opts.accessToken);
|
|
954
|
+
const publicHostnames = [publicUrl, publicWsUrl].map((value) => {
|
|
955
|
+
if (!value) return void 0;
|
|
956
|
+
try {
|
|
957
|
+
return new URL(value).hostname;
|
|
958
|
+
} catch {
|
|
959
|
+
return void 0;
|
|
960
|
+
}
|
|
961
|
+
}).filter((value) => Boolean(value));
|
|
962
|
+
const accessUrl = buildWebUIAccessUrl({
|
|
963
|
+
host,
|
|
964
|
+
port: httpPort,
|
|
965
|
+
token: wsToken,
|
|
966
|
+
publicUrl
|
|
967
|
+
});
|
|
968
|
+
let fleetBroadcastCli = null;
|
|
969
|
+
const httpServer = await startStaticServe({
|
|
970
|
+
host,
|
|
971
|
+
httpPort,
|
|
972
|
+
globalRoot,
|
|
973
|
+
distDir: opts.frontendDistDir,
|
|
974
|
+
ensureDistDeps: {
|
|
975
|
+
resolvePackageJson: (id) => {
|
|
976
|
+
const packageJson = findPackageJSON(id, import.meta.url);
|
|
977
|
+
if (!packageJson) throw new Error(`Package not found: ${id}`);
|
|
978
|
+
return packageJson;
|
|
979
|
+
}
|
|
980
|
+
},
|
|
981
|
+
onFleetPing: () => {
|
|
982
|
+
void fleetBroadcastCli?.();
|
|
983
|
+
},
|
|
984
|
+
onTechStackEvent: (event) => broadcast(event),
|
|
985
|
+
// Read through the live agent context so a mid-session model switch or a
|
|
986
|
+
// credential hot-reload takes effect on the next analyze.
|
|
987
|
+
getLlm: () => opts.agent.ctx.provider && opts.agent.ctx.model ? { provider: opts.agent.ctx.provider, model: opts.agent.ctx.model } : void 0,
|
|
988
|
+
projectRoot: opts.projectRoot,
|
|
989
|
+
publicWsUrl,
|
|
990
|
+
apiToken: wsToken,
|
|
991
|
+
requireToken,
|
|
992
|
+
// SimpleUI only: defer listen until after the WebSocketServer is
|
|
993
|
+
// attached, so a WS upgrade request cannot arrive between listen()
|
|
994
|
+
// and the WS handler registration.
|
|
995
|
+
deferListen: surface === "simpleui"
|
|
996
|
+
});
|
|
997
|
+
const wss = httpServer ? new WebSocketServer({ server: httpServer.server, maxPayload: 20 * 1024 * 1024 }) : new WebSocketServer({ port: httpPort, host, maxPayload: 20 * 1024 * 1024 });
|
|
998
|
+
if (httpServer && surface === "simpleui") {
|
|
999
|
+
await startDeferredHttpListen({
|
|
1000
|
+
server: httpServer.server,
|
|
1001
|
+
host,
|
|
1002
|
+
httpPort,
|
|
1003
|
+
logger: consoleLogger
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
let ipv6LoopbackServer = null;
|
|
1007
|
+
if (httpServer && host === "127.0.0.1") {
|
|
1008
|
+
ipv6LoopbackServer = await startIpv6LoopbackProxy({
|
|
1009
|
+
primary: httpServer.server,
|
|
1010
|
+
httpPort,
|
|
1011
|
+
logger: consoleLogger
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
console.log(`[WebUI] WebSocket server starting on ws://${host}:${httpPort}`);
|
|
1015
|
+
if (httpServer) {
|
|
1016
|
+
announceWebuiReady({
|
|
1017
|
+
surface,
|
|
1018
|
+
server: httpServer.server,
|
|
1019
|
+
host,
|
|
1020
|
+
httpPort,
|
|
1021
|
+
open: !!opts.open,
|
|
1022
|
+
wsToken,
|
|
1023
|
+
publicUrl
|
|
1024
|
+
});
|
|
1025
|
+
} else {
|
|
1026
|
+
console.warn(
|
|
1027
|
+
`[WebUI] Frontend not served (run \`pnpm --filter @wrongstack/webui build\`). WS bridge still active on ws://${host}:${httpPort}.`
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
const registryBaseDir = globalRoot;
|
|
1031
|
+
if (opts.projectRoot) {
|
|
1032
|
+
registerWebuiInstance({
|
|
1033
|
+
pid: process.pid,
|
|
1034
|
+
surface,
|
|
1035
|
+
host,
|
|
1036
|
+
httpPort,
|
|
1037
|
+
publicUrl,
|
|
1038
|
+
projectRoot: opts.projectRoot,
|
|
1039
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1040
|
+
registryBaseDir
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
const eventUnsubscribers = [];
|
|
1044
|
+
const currentSessionId = () => opts.agent.ctx.session?.id ?? opts.session.id;
|
|
1045
|
+
const sessionPayload = (payload) => {
|
|
1046
|
+
const provided = payload["sessionId"];
|
|
1047
|
+
const sessionId = typeof provided === "string" && provided.length > 0 ? provided : currentSessionId();
|
|
1048
|
+
return { ...payload, sessionId };
|
|
1049
|
+
};
|
|
1050
|
+
const {
|
|
1051
|
+
queueTextDelta,
|
|
1052
|
+
queueThinkingDelta,
|
|
1053
|
+
queueToolProgress,
|
|
1054
|
+
flushThinkingDelta,
|
|
1055
|
+
flushAllStreamBuffers
|
|
1056
|
+
} = createStreamCoalescer({ broadcast, sessionPayload });
|
|
1057
|
+
const setupEvents = createSetupEvents({
|
|
1058
|
+
events: opts.events,
|
|
1059
|
+
agent: opts.agent,
|
|
1060
|
+
subscribeEternalIteration: opts.subscribeEternalIteration,
|
|
1061
|
+
broadcast,
|
|
1062
|
+
sessionPayload,
|
|
1063
|
+
currentSessionId,
|
|
1064
|
+
queueTextDelta,
|
|
1065
|
+
queueThinkingDelta,
|
|
1066
|
+
queueToolProgress,
|
|
1067
|
+
flushThinkingDelta,
|
|
1068
|
+
flushAllStreamBuffers,
|
|
1069
|
+
pendingConfirms,
|
|
1070
|
+
secretScrubber,
|
|
1071
|
+
getClients: () => clients,
|
|
1072
|
+
eventUnsubscribers,
|
|
1073
|
+
globalConfigPath: path5.join(globalRoot, "config.json"),
|
|
1074
|
+
onFleetBroadcaster: (fn) => {
|
|
1075
|
+
fleetBroadcastCli = fn;
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
const wsHandlerCtx = {
|
|
1079
|
+
providerStore: createProviderConfigStore(
|
|
1080
|
+
profileConfigPath,
|
|
1081
|
+
// Use the in-memory merged config providers so the WebUI sees the
|
|
1082
|
+
// same provider list the agent uses. Without this, providers stored
|
|
1083
|
+
// only in the project-local config (config.local.json) would be
|
|
1084
|
+
// invisible to the WebUI's saved-providers panel because the store
|
|
1085
|
+
// reads exclusively from the global config file.
|
|
1086
|
+
() => opts.appConfig?.providers ?? {}
|
|
1087
|
+
),
|
|
1088
|
+
modelsRegistry: opts.modelsRegistry,
|
|
1089
|
+
send,
|
|
1090
|
+
broadcast,
|
|
1091
|
+
log: (m) => console.log(m)
|
|
1092
|
+
};
|
|
1093
|
+
const embeddedProviderOperations = createEmbeddedProviderOperations(wsHandlerCtx);
|
|
1094
|
+
const watchConfigPath = profileConfigPath;
|
|
1095
|
+
let credentialWatcherClose;
|
|
1096
|
+
if (watchConfigPath && process.env["WRONGSTACK_DISABLE_CONFIG_WATCH"] !== "1") {
|
|
1097
|
+
let lastActiveCfg = JSON.stringify(
|
|
1098
|
+
opts.appConfig?.providers?.[opts.agent.ctx.provider.id] ?? null
|
|
1099
|
+
);
|
|
1100
|
+
let lastUiLocale = opts.appConfig?.uiLocale;
|
|
1101
|
+
const watcher = watchProviderConfig(
|
|
1102
|
+
watchConfigPath,
|
|
1103
|
+
// Vault key lives at ~/.wrongstack/.key (derived from globalConfigPath),
|
|
1104
|
+
// not inside the profile directory — resolve via global root to stay correct.
|
|
1105
|
+
getVault(opts.globalConfigPath),
|
|
1106
|
+
(snapshot) => {
|
|
1107
|
+
try {
|
|
1108
|
+
if (opts.appConfig && !Object.isFrozen(opts.appConfig)) {
|
|
1109
|
+
opts.appConfig.providers = snapshot.providers;
|
|
1110
|
+
if (snapshot.uiLocale) opts.appConfig.uiLocale = snapshot.uiLocale;
|
|
1111
|
+
}
|
|
1112
|
+
} catch {
|
|
1113
|
+
}
|
|
1114
|
+
const routingChanged = snapshot.fallbackModels !== void 0 || snapshot.fallbackProfiles !== void 0 || snapshot.favoriteModels !== void 0 || snapshot.favoriteModelsOnly !== void 0 || snapshot.modelMatrix !== void 0 || snapshot.fallbackAuto !== void 0;
|
|
1115
|
+
if (routingChanged) {
|
|
1116
|
+
const configStore = opts.agent.container?.safeResolve?.(TOKENS.ConfigStore);
|
|
1117
|
+
configStore?.update({
|
|
1118
|
+
...snapshot.fallbackModels !== void 0 ? { fallbackModels: snapshot.fallbackModels } : {},
|
|
1119
|
+
...snapshot.fallbackProfiles !== void 0 ? { fallbackProfiles: snapshot.fallbackProfiles } : {},
|
|
1120
|
+
...snapshot.favoriteModels !== void 0 ? { favoriteModels: snapshot.favoriteModels } : {},
|
|
1121
|
+
...snapshot.favoriteModelsOnly !== void 0 ? { favoriteModelsOnly: snapshot.favoriteModelsOnly } : {},
|
|
1122
|
+
...snapshot.modelMatrix !== void 0 ? { modelMatrix: snapshot.modelMatrix } : {},
|
|
1123
|
+
...snapshot.fallbackAuto !== void 0 ? { fallbackAuto: snapshot.fallbackAuto } : {}
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
embeddedProviderOperations.broadcastSaved(snapshot.providers);
|
|
1127
|
+
if (snapshot.uiLocale !== lastUiLocale) {
|
|
1128
|
+
lastUiLocale = snapshot.uiLocale;
|
|
1129
|
+
if (snapshot.uiLocale) {
|
|
1130
|
+
opts.agent.ctx.meta["uiLocale"] = snapshot.uiLocale;
|
|
1131
|
+
broadcast({ type: "prefs.updated", payload: { uiLocale: snapshot.uiLocale } });
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
const activeId = opts.agent.ctx.provider.id;
|
|
1135
|
+
const newCfgStr = JSON.stringify(snapshot.providers[activeId] ?? null);
|
|
1136
|
+
if (newCfgStr === lastActiveCfg) return;
|
|
1137
|
+
lastActiveCfg = newCfgStr;
|
|
1138
|
+
try {
|
|
1139
|
+
const newCfg = snapshot.providers[activeId] ?? {
|
|
1140
|
+
type: activeId,
|
|
1141
|
+
...snapshot.apiKey !== void 0 ? { apiKey: snapshot.apiKey } : {},
|
|
1142
|
+
...snapshot.baseUrl !== void 0 ? { baseUrl: snapshot.baseUrl } : {}
|
|
1143
|
+
};
|
|
1144
|
+
const oldMax = opts.agent.ctx.provider.capabilities?.maxContext;
|
|
1145
|
+
const prov = makeProviderFromConfig(activeId, { ...newCfg, type: activeId });
|
|
1146
|
+
if (oldMax != null && prov.capabilities) prov.capabilities.maxContext = oldMax;
|
|
1147
|
+
opts.agent.ctx.provider = prov;
|
|
1148
|
+
console.log(`[WebUI] Provider credentials reloaded from config.json (${activeId})`);
|
|
1149
|
+
} catch (err) {
|
|
1150
|
+
console.warn(
|
|
1151
|
+
`[WebUI] Credential hot-reload failed for ${activeId}: ${toErrorMessage(err)}`
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
warn: (m) => console.warn(
|
|
1157
|
+
JSON.stringify({
|
|
1158
|
+
level: "warn",
|
|
1159
|
+
event: "webui.config_watcher",
|
|
1160
|
+
message: m,
|
|
1161
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1162
|
+
})
|
|
1163
|
+
)
|
|
1164
|
+
}
|
|
1165
|
+
);
|
|
1166
|
+
credentialWatcherClose = watcher.close;
|
|
1167
|
+
}
|
|
1168
|
+
const brainCtx = {
|
|
1169
|
+
brainSettings: opts.brainSettings,
|
|
1170
|
+
brainRuntime: opts.brainRuntime,
|
|
1171
|
+
getBrainLog: opts.getBrainLog,
|
|
1172
|
+
// Prefer the host-supplied arbiter; otherwise resolve the one bound
|
|
1173
|
+
// in the agent container (if any). Mirrors the former inline lookup.
|
|
1174
|
+
resolveArbiter: () => opts.brain ?? (opts.agent.container.has(TOKENS.BrainArbiter) ? opts.agent.container.resolve(TOKENS.BrainArbiter) : void 0),
|
|
1175
|
+
getSessionId: currentSessionId,
|
|
1176
|
+
send
|
|
1177
|
+
};
|
|
1178
|
+
const introspectionConfigStore = opts.agent.container?.safeResolve?.(TOKENS.ConfigStore);
|
|
1179
|
+
const introspectionCtx = {
|
|
1180
|
+
agent: opts.agent,
|
|
1181
|
+
modelsRegistry: opts.modelsRegistry,
|
|
1182
|
+
configStore: introspectionConfigStore,
|
|
1183
|
+
getConfig: () => {
|
|
1184
|
+
const cfg = introspectionConfigStore?.get() ?? opts.appConfig;
|
|
1185
|
+
if (!cfg)
|
|
1186
|
+
throw new Error(
|
|
1187
|
+
"Introspection route requires a config but neither ConfigStore nor opts.appConfig is available"
|
|
1188
|
+
);
|
|
1189
|
+
return cfg;
|
|
1190
|
+
},
|
|
1191
|
+
getProjectRoot: () => opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "",
|
|
1192
|
+
getSessionId: currentSessionId,
|
|
1193
|
+
getSessionStartedAt: () => sessionStartedAt,
|
|
1194
|
+
getModeId: () => opts.modeId ?? "default",
|
|
1195
|
+
send
|
|
1196
|
+
};
|
|
1197
|
+
const skillsProjectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
|
|
1198
|
+
const skillsPaths = skillsProjectRoot ? resolveWstackPaths2({
|
|
1199
|
+
projectRoot: skillsProjectRoot,
|
|
1200
|
+
globalRoot
|
|
1201
|
+
}) : void 0;
|
|
1202
|
+
const skillsCtx = {
|
|
1203
|
+
skillLoader: opts.skillLoader,
|
|
1204
|
+
skillInstaller: opts.skillLoader ? new SkillInstaller({
|
|
1205
|
+
manifestPath: path5.join(skillsPaths?.configDir ?? profileDir, "installed-skills.json"),
|
|
1206
|
+
projectSkillsDir: skillsPaths?.inProjectSkills ?? path5.join(skillsProjectRoot, ".wrongstack", "skills"),
|
|
1207
|
+
globalSkillsDir: skillsPaths?.globalSkills ?? path5.join(profileDir, "skills"),
|
|
1208
|
+
projectHash: skillsPaths?.projectHash ?? "",
|
|
1209
|
+
skillLoader: opts.skillLoader
|
|
1210
|
+
}) : void 0,
|
|
1211
|
+
projectRoot: skillsProjectRoot,
|
|
1212
|
+
projectSkillsDir: skillsPaths?.inProjectSkills,
|
|
1213
|
+
globalSkillsDir: skillsPaths?.globalSkills
|
|
1214
|
+
};
|
|
1215
|
+
const promptsCtx = {
|
|
1216
|
+
promptLoader: opts.promptLoader,
|
|
1217
|
+
promptUsage: new PromptUsageStore(
|
|
1218
|
+
skillsPaths?.promptUsage ?? path5.join(profileDir, "prompt-usage.json")
|
|
1219
|
+
)
|
|
1220
|
+
};
|
|
1221
|
+
const designCtx = {
|
|
1222
|
+
projectRoot: skillsProjectRoot,
|
|
1223
|
+
agentMeta: opts.agent.ctx
|
|
1224
|
+
};
|
|
1225
|
+
const agentConfigCtx = {
|
|
1226
|
+
agent: opts.agent,
|
|
1227
|
+
modeStore: opts.modeStore,
|
|
1228
|
+
loadSavedProviders: () => loadSavedProviders(profileConfigPath),
|
|
1229
|
+
buildSessionStart: (overrides) => buildSessionStartPayload(overrides),
|
|
1230
|
+
modelsRegistry: opts.modelsRegistry,
|
|
1231
|
+
memoryStore: opts.memoryStore,
|
|
1232
|
+
getConfig: () => opts.appConfig,
|
|
1233
|
+
onMaxContextResolved: opts.onModelContextResolved,
|
|
1234
|
+
persistPrefs,
|
|
1235
|
+
send,
|
|
1236
|
+
broadcast,
|
|
1237
|
+
log: (m) => console.log(m)
|
|
1238
|
+
};
|
|
1239
|
+
const prefsCtx = {
|
|
1240
|
+
meta: opts.agent.ctx.meta,
|
|
1241
|
+
snapshot: prefSnapshot,
|
|
1242
|
+
persist: persistPrefs,
|
|
1243
|
+
setYolo: opts.onYoloSwitch,
|
|
1244
|
+
setAutonomy: opts.onAutonomySwitch,
|
|
1245
|
+
pendingConfirms,
|
|
1246
|
+
configStore: opts.agent.container?.safeResolve?.(TOKENS.ConfigStore),
|
|
1247
|
+
send,
|
|
1248
|
+
broadcast
|
|
1249
|
+
};
|
|
1250
|
+
const projectsCtx = {
|
|
1251
|
+
opts,
|
|
1252
|
+
abortControllers,
|
|
1253
|
+
abortLegacyRun: () => {
|
|
1254
|
+
if (abortController) {
|
|
1255
|
+
abortController.abort();
|
|
1256
|
+
abortController = null;
|
|
1257
|
+
}
|
|
1258
|
+
},
|
|
1259
|
+
buildSessionStart: (overrides) => buildSessionStartPayload(overrides),
|
|
1260
|
+
send,
|
|
1261
|
+
broadcast,
|
|
1262
|
+
log: (m) => console.log(m)
|
|
1263
|
+
};
|
|
1264
|
+
const mailboxRoutes = createMailboxRouteHandlers({
|
|
1265
|
+
getProjectRoot: () => opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "",
|
|
1266
|
+
getGlobalRoot: () => opts.globalConfigPath ? path5.dirname(opts.globalConfigPath) : "",
|
|
1267
|
+
events: opts.events
|
|
1268
|
+
});
|
|
1269
|
+
const sessionsCtx = {
|
|
1270
|
+
opts,
|
|
1271
|
+
buildSessionStart: (overrides) => buildSessionStartPayload(overrides),
|
|
1272
|
+
getCustomModeStore,
|
|
1273
|
+
send,
|
|
1274
|
+
broadcast,
|
|
1275
|
+
log: (m) => console.log(m)
|
|
1276
|
+
};
|
|
1277
|
+
const connectionCtx = {
|
|
1278
|
+
agent: opts.agent,
|
|
1279
|
+
abortControllers,
|
|
1280
|
+
pendingConfirms,
|
|
1281
|
+
send,
|
|
1282
|
+
broadcast,
|
|
1283
|
+
log: (m) => console.log(m)
|
|
1284
|
+
};
|
|
1285
|
+
const kanbanHostRoutes = createCliKanbanHostRoutes({
|
|
1286
|
+
opts,
|
|
1287
|
+
send,
|
|
1288
|
+
broadcast,
|
|
1289
|
+
goalHandler,
|
|
1290
|
+
...kanbanSupervisor ? { kanbanSupervisor } : {},
|
|
1291
|
+
...kanbanRunMirror ? { kanbanRunMirror } : {}
|
|
1292
|
+
});
|
|
1293
|
+
const handleMessage = createEmbeddedMessageRouter({
|
|
1294
|
+
trustBoundary,
|
|
1295
|
+
opts,
|
|
1296
|
+
logger: consoleLogger,
|
|
1297
|
+
send,
|
|
1298
|
+
sendResult,
|
|
1299
|
+
sessionPayload,
|
|
1300
|
+
currentSessionId,
|
|
1301
|
+
shutdown,
|
|
1302
|
+
providerCtx: wsHandlerCtx,
|
|
1303
|
+
brainCtx,
|
|
1304
|
+
introspectionCtx,
|
|
1305
|
+
skillsCtx,
|
|
1306
|
+
promptsCtx,
|
|
1307
|
+
designCtx,
|
|
1308
|
+
agentConfigCtx,
|
|
1309
|
+
prefsCtx,
|
|
1310
|
+
projectCtx: projectsCtx,
|
|
1311
|
+
mailboxRoutes,
|
|
1312
|
+
sessionCtx: sessionsCtx,
|
|
1313
|
+
conversationCtx: connectionCtx,
|
|
1314
|
+
goalHandler,
|
|
1315
|
+
specsHandler,
|
|
1316
|
+
sddBoardHandler,
|
|
1317
|
+
sddWizardHandler,
|
|
1318
|
+
worktreeHandler,
|
|
1319
|
+
terminalHandler,
|
|
1320
|
+
kanbanHostRoutes,
|
|
1321
|
+
statusTracker: opts.statusTracker
|
|
1322
|
+
});
|
|
1323
|
+
const terminalLogView = startBoundedTerminalLogView();
|
|
1324
|
+
const stopped = new Promise((resolve) => {
|
|
1325
|
+
let listeningAnnounced = false;
|
|
1326
|
+
const announceListening = () => {
|
|
1327
|
+
if (listeningAnnounced) return;
|
|
1328
|
+
listeningAnnounced = true;
|
|
1329
|
+
console.log(`[WebUI] WebSocket server running on ws://${host}:${httpPort}`);
|
|
1330
|
+
try {
|
|
1331
|
+
setupEvents();
|
|
1332
|
+
opts.onListening?.({ httpPort, wsPort, host, url: accessUrl });
|
|
1333
|
+
} catch (err) {
|
|
1334
|
+
consoleLogger.error("setup_events_failed", { message: toErrorMessage(err) });
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
wss.on("listening", announceListening);
|
|
1338
|
+
if (httpServer?.server.listening || wss.address()) queueMicrotask(announceListening);
|
|
1339
|
+
wss.on(
|
|
1340
|
+
"connection",
|
|
1341
|
+
createConnectionHandler({
|
|
1342
|
+
host,
|
|
1343
|
+
wsToken,
|
|
1344
|
+
requireToken,
|
|
1345
|
+
publicHostnames,
|
|
1346
|
+
publicWsUrl,
|
|
1347
|
+
clients,
|
|
1348
|
+
currentSessionId,
|
|
1349
|
+
goalHandler,
|
|
1350
|
+
specsHandler,
|
|
1351
|
+
sddBoardHandler,
|
|
1352
|
+
sddWizardHandler,
|
|
1353
|
+
worktreeHandler,
|
|
1354
|
+
terminalHandler,
|
|
1355
|
+
rateLimitMax,
|
|
1356
|
+
send,
|
|
1357
|
+
sessionPayload,
|
|
1358
|
+
handleMessage,
|
|
1359
|
+
abortControllers,
|
|
1360
|
+
pendingConfirms,
|
|
1361
|
+
buildSessionStartPayload,
|
|
1362
|
+
loadReplay: async () => {
|
|
1363
|
+
const activeSession = opts.agent.ctx.session ?? opts.session;
|
|
1364
|
+
await activeSession.flush().catch(() => void 0);
|
|
1365
|
+
if (opts.sessionStore) {
|
|
1366
|
+
const data = await opts.sessionStore.load(activeSession.id);
|
|
1367
|
+
return { messages: data.messages, events: data.events, usage: data.usage };
|
|
1368
|
+
}
|
|
1369
|
+
const usage = opts.agent.ctx.tokenCounter.total();
|
|
1370
|
+
return { messages: opts.agent.ctx.messages, usage };
|
|
1371
|
+
},
|
|
1372
|
+
// Reads the on-disk transcripts when the in-memory ring is cold, which
|
|
1373
|
+
// it always is after a resume — the ring only holds what this process
|
|
1374
|
+
// watched happen.
|
|
1375
|
+
loadAgentSessions: async () => await opts.agentTranscripts?.loadSessionsFromDisk() ?? [],
|
|
1376
|
+
needsSetup: opts.needsSetup ?? false
|
|
1377
|
+
})
|
|
1378
|
+
);
|
|
1379
|
+
wss.on("error", (err) => {
|
|
1380
|
+
console.error(
|
|
1381
|
+
JSON.stringify({
|
|
1382
|
+
level: "error",
|
|
1383
|
+
event: "webui_server.error",
|
|
1384
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1385
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1386
|
+
})
|
|
1387
|
+
);
|
|
1388
|
+
});
|
|
1389
|
+
const signalShutdown = createWebuiShutdown({
|
|
1390
|
+
abortInFlight: () => {
|
|
1391
|
+
if (abortController) {
|
|
1392
|
+
abortController.abort();
|
|
1393
|
+
abortController = null;
|
|
1394
|
+
}
|
|
1395
|
+
for (const c of abortControllers.values()) c.abort();
|
|
1396
|
+
abortControllers.clear();
|
|
1397
|
+
},
|
|
1398
|
+
unsubscribeEvents: () => {
|
|
1399
|
+
flushAllStreamBuffers();
|
|
1400
|
+
for (const unsub of eventUnsubscribers) unsub();
|
|
1401
|
+
},
|
|
1402
|
+
closeClients: () => {
|
|
1403
|
+
for (const [ws] of clients) ws.close();
|
|
1404
|
+
clients.clear();
|
|
1405
|
+
},
|
|
1406
|
+
closeHttpServer: () => {
|
|
1407
|
+
ipv6LoopbackServer?.close();
|
|
1408
|
+
httpServer?.server.close();
|
|
1409
|
+
},
|
|
1410
|
+
wss,
|
|
1411
|
+
pid: process.pid,
|
|
1412
|
+
registryBaseDir,
|
|
1413
|
+
onStopped: () => {
|
|
1414
|
+
terminalLogView.redraw();
|
|
1415
|
+
terminalLogView.stop();
|
|
1416
|
+
resolve();
|
|
1417
|
+
}
|
|
1418
|
+
});
|
|
1419
|
+
registerWebuiSignalHandlers(signalShutdown);
|
|
1420
|
+
});
|
|
1421
|
+
function send(ws, msg) {
|
|
1422
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
1423
|
+
ws.send(JSON.stringify(msg));
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
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
|
+
function broadcast(msg) {
|
|
1442
|
+
const data = JSON.stringify(msg);
|
|
1443
|
+
for (const [ws] of clients) {
|
|
1444
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
1445
|
+
try {
|
|
1446
|
+
ws.send(data);
|
|
1447
|
+
} catch {
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
function sendResult(ws, success, message) {
|
|
1453
|
+
send(ws, { type: "key.operation_result", payload: { success, message } });
|
|
1454
|
+
}
|
|
1455
|
+
return stopped.finally(() => terminalLogView.stop());
|
|
1456
|
+
}
|
|
1457
|
+
export {
|
|
1458
|
+
runWebUI
|
|
1459
|
+
};
|
|
1460
|
+
//# sourceMappingURL=webui-server-ZF5KVGC5.js.map
|