@agimon-ai/doompi-workflow 0.0.1-alpha.76 → 0.0.1-alpha.78
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/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/api-contracts.cjs +7 -0
- package/dist/api-contracts.d.cts +2 -0
- package/dist/api-contracts.d.mts +2 -0
- package/dist/api-contracts.mjs +2 -0
- package/dist/extensions/pi.cjs +63 -0
- package/dist/extensions/pi.cjs.map +1 -0
- package/dist/extensions/pi.d.cts +6 -0
- package/dist/extensions/pi.d.cts.map +1 -0
- package/dist/extensions/pi.d.mts +6 -0
- package/dist/extensions/pi.d.mts.map +1 -0
- package/dist/extensions/pi.mjs +58 -0
- package/dist/extensions/pi.mjs.map +1 -0
- package/dist/extensions/server.cjs +96 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.cts +5 -0
- package/dist/extensions/server.d.cts.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +91 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/extensions/web.mjs +3219 -0
- package/dist/extensions/web.mjs.map +1 -0
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +2 -0
- package/dist/src/constants/workflow.cjs +32 -0
- package/dist/src/constants/workflow.cjs.map +1 -0
- package/dist/src/constants/workflow.mjs +21 -0
- package/dist/src/constants/workflow.mjs.map +1 -0
- package/dist/src/extensions/(backend)/_lib/index.server.cjs +12 -0
- package/dist/src/extensions/(backend)/_lib/index.server.cjs.map +1 -0
- package/dist/src/extensions/(backend)/_lib/index.server.mjs +12 -0
- package/dist/src/extensions/(backend)/_lib/index.server.mjs.map +1 -0
- package/dist/src/extensions/(backend)/api/workflow/route.server.cjs +7 -0
- package/dist/src/extensions/(backend)/api/workflow/route.server.cjs.map +1 -0
- package/dist/src/extensions/(backend)/api/workflow/route.server.mjs +8 -0
- package/dist/src/extensions/(backend)/api/workflow/route.server.mjs.map +1 -0
- package/dist/src/extensions/(backend)/channel/workflow-catalog.server.cjs +7 -0
- package/dist/src/extensions/(backend)/channel/workflow-catalog.server.cjs.map +1 -0
- package/dist/src/extensions/(backend)/channel/workflow-catalog.server.mjs +8 -0
- package/dist/src/extensions/(backend)/channel/workflow-catalog.server.mjs.map +1 -0
- package/dist/src/extensions/(backend)/channel/workflows.server.cjs +7 -0
- package/dist/src/extensions/(backend)/channel/workflows.server.cjs.map +1 -0
- package/dist/src/extensions/(backend)/channel/workflows.server.mjs +8 -0
- package/dist/src/extensions/(backend)/channel/workflows.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.cjs +100 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.mjs +100 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.cjs +11 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.mjs +11 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.cjs +12 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.mjs +12 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.cjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.mjs +8 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.cjs +15 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.mjs +16 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs +17 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs +18 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.cjs +6 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.mjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.cjs +50 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.mjs +47 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.cjs +36 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.mjs +35 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.cjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.mjs +8 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.cjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.mjs +8 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.mjs.map +1 -0
- package/dist/src/schemas/apiContracts.cjs +330 -0
- package/dist/src/schemas/apiContracts.cjs.map +1 -0
- package/dist/src/schemas/apiContracts.d.cts +53 -0
- package/dist/src/schemas/apiContracts.d.cts.map +1 -0
- package/dist/src/schemas/apiContracts.d.mts +53 -0
- package/dist/src/schemas/apiContracts.d.mts.map +1 -0
- package/dist/src/schemas/apiContracts.mjs +327 -0
- package/dist/src/schemas/apiContracts.mjs.map +1 -0
- package/dist/src/schemas/workflowPi.cjs +77 -0
- package/dist/src/schemas/workflowPi.cjs.map +1 -0
- package/dist/src/schemas/workflowPi.d.cts +1 -0
- package/dist/src/schemas/workflowPi.d.mts +1 -0
- package/dist/src/schemas/workflowPi.mjs +74 -0
- package/dist/src/schemas/workflowPi.mjs.map +1 -0
- package/dist/src/services/backgroundWork/index.cjs +61 -0
- package/dist/src/services/backgroundWork/index.cjs.map +1 -0
- package/dist/src/services/backgroundWork/index.mjs +61 -0
- package/dist/src/services/backgroundWork/index.mjs.map +1 -0
- package/dist/src/services/piToolBridge/index.cjs +205 -0
- package/dist/src/services/piToolBridge/index.cjs.map +1 -0
- package/dist/src/services/piToolBridge/index.mjs +195 -0
- package/dist/src/services/piToolBridge/index.mjs.map +1 -0
- package/dist/src/services/serverRuntime/index.cjs +387 -0
- package/dist/src/services/serverRuntime/index.cjs.map +1 -0
- package/dist/src/services/serverRuntime/index.mjs +387 -0
- package/dist/src/services/serverRuntime/index.mjs.map +1 -0
- package/dist/src/services/webWorkflowCatalog/index.cjs +53 -0
- package/dist/src/services/webWorkflowCatalog/index.cjs.map +1 -0
- package/dist/src/services/webWorkflowCatalog/index.mjs +52 -0
- package/dist/src/services/webWorkflowCatalog/index.mjs.map +1 -0
- package/dist/src/services/workflowCatalogChannel/index.cjs +74 -0
- package/dist/src/services/workflowCatalogChannel/index.cjs.map +1 -0
- package/dist/src/services/workflowCatalogChannel/index.mjs +74 -0
- package/dist/src/services/workflowCatalogChannel/index.mjs.map +1 -0
- package/dist/src/services/workflowCatalogDeps/index.cjs +25 -0
- package/dist/src/services/workflowCatalogDeps/index.cjs.map +1 -0
- package/dist/src/services/workflowCatalogDeps/index.mjs +25 -0
- package/dist/src/services/workflowCatalogDeps/index.mjs.map +1 -0
- package/dist/src/services/workflowExecution/index.cjs +205 -0
- package/dist/src/services/workflowExecution/index.cjs.map +1 -0
- package/dist/src/services/workflowExecution/index.d.cts +6 -0
- package/dist/src/services/workflowExecution/index.d.mts +6 -0
- package/dist/src/services/workflowExecution/index.mjs +200 -0
- package/dist/src/services/workflowExecution/index.mjs.map +1 -0
- package/dist/src/services/workflowFence/index.cjs +170 -0
- package/dist/src/services/workflowFence/index.cjs.map +1 -0
- package/dist/src/services/workflowFence/index.mjs +166 -0
- package/dist/src/services/workflowFence/index.mjs.map +1 -0
- package/dist/src/services/workflowHubApi/index.cjs +415 -0
- package/dist/src/services/workflowHubApi/index.cjs.map +1 -0
- package/dist/src/services/workflowHubApi/index.mjs +411 -0
- package/dist/src/services/workflowHubApi/index.mjs.map +1 -0
- package/dist/src/services/workflowLaunchCommand/index.cjs +121 -0
- package/dist/src/services/workflowLaunchCommand/index.cjs.map +1 -0
- package/dist/src/services/workflowLaunchCommand/index.d.cts +52 -0
- package/dist/src/services/workflowLaunchCommand/index.d.cts.map +1 -0
- package/dist/src/services/workflowLaunchCommand/index.d.mts +52 -0
- package/dist/src/services/workflowLaunchCommand/index.d.mts.map +1 -0
- package/dist/src/services/workflowLaunchCommand/index.mjs +118 -0
- package/dist/src/services/workflowLaunchCommand/index.mjs.map +1 -0
- package/dist/src/services/workflowLauncher/index.cjs +181 -0
- package/dist/src/services/workflowLauncher/index.cjs.map +1 -0
- package/dist/src/services/workflowLauncher/index.mjs +177 -0
- package/dist/src/services/workflowLauncher/index.mjs.map +1 -0
- package/dist/src/services/workflowNarration/index.cjs +19 -0
- package/dist/src/services/workflowNarration/index.cjs.map +1 -0
- package/dist/src/services/workflowNarration/index.mjs +19 -0
- package/dist/src/services/workflowNarration/index.mjs.map +1 -0
- package/dist/src/services/workflowResource/index.cjs +27 -0
- package/dist/src/services/workflowResource/index.cjs.map +1 -0
- package/dist/src/services/workflowResource/index.mjs +24 -0
- package/dist/src/services/workflowResource/index.mjs.map +1 -0
- package/dist/src/services/workflowRuns/index.cjs +333 -0
- package/dist/src/services/workflowRuns/index.cjs.map +1 -0
- package/dist/src/services/workflowRuns/index.mjs +319 -0
- package/dist/src/services/workflowRuns/index.mjs.map +1 -0
- package/dist/src/services/workflowTerminal/index.cjs +69 -0
- package/dist/src/services/workflowTerminal/index.cjs.map +1 -0
- package/dist/src/services/workflowTerminal/index.mjs +69 -0
- package/dist/src/services/workflowTerminal/index.mjs.map +1 -0
- package/dist/src/services/workflowTools/index.cjs +221 -0
- package/dist/src/services/workflowTools/index.cjs.map +1 -0
- package/dist/src/services/workflowTools/index.mjs +221 -0
- package/dist/src/services/workflowTools/index.mjs.map +1 -0
- package/dist/src/services/workflowWatcher/index.cjs +78 -0
- package/dist/src/services/workflowWatcher/index.cjs.map +1 -0
- package/dist/src/services/workflowWatcher/index.mjs +74 -0
- package/dist/src/services/workflowWatcher/index.mjs.map +1 -0
- package/dist/src/services/workflowsHubChannel/index.cjs +58 -0
- package/dist/src/services/workflowsHubChannel/index.cjs.map +1 -0
- package/dist/src/services/workflowsHubChannel/index.mjs +58 -0
- package/dist/src/services/workflowsHubChannel/index.mjs.map +1 -0
- package/dist/src/tui/leader.cjs +84 -0
- package/dist/src/tui/leader.cjs.map +1 -0
- package/dist/src/tui/leader.mjs +83 -0
- package/dist/src/tui/leader.mjs.map +1 -0
- package/dist/src/tui/workflow/doomOverlay.cjs +159 -0
- package/dist/src/tui/workflow/doomOverlay.cjs.map +1 -0
- package/dist/src/tui/workflow/doomOverlay.mjs +157 -0
- package/dist/src/tui/workflow/doomOverlay.mjs.map +1 -0
- package/dist/src/tui/workflow/overlayText.cjs +83 -0
- package/dist/src/tui/workflow/overlayText.cjs.map +1 -0
- package/dist/src/tui/workflow/overlayText.mjs +78 -0
- package/dist/src/tui/workflow/overlayText.mjs.map +1 -0
- package/dist/src/tui/workflow/rendering.cjs +22 -0
- package/dist/src/tui/workflow/rendering.cjs.map +1 -0
- package/dist/src/tui/workflow/rendering.mjs +20 -0
- package/dist/src/tui/workflow/rendering.mjs.map +1 -0
- package/dist/src/tui/workflow/workflowCatalog.cjs +336 -0
- package/dist/src/tui/workflow/workflowCatalog.cjs.map +1 -0
- package/dist/src/tui/workflow/workflowCatalog.mjs +333 -0
- package/dist/src/tui/workflow/workflowCatalog.mjs.map +1 -0
- package/dist/src/tui/workflow/workflowFinishedMessage.cjs +50 -0
- package/dist/src/tui/workflow/workflowFinishedMessage.cjs.map +1 -0
- package/dist/src/tui/workflow/workflowFinishedMessage.mjs +47 -0
- package/dist/src/tui/workflow/workflowFinishedMessage.mjs.map +1 -0
- package/dist/src/tui/workflow/workflowInspector.cjs +276 -0
- package/dist/src/tui/workflow/workflowInspector.cjs.map +1 -0
- package/dist/src/tui/workflow/workflowInspector.mjs +276 -0
- package/dist/src/tui/workflow/workflowInspector.mjs.map +1 -0
- package/dist/src/tui/workflow/workflowOverlay.cjs +126 -0
- package/dist/src/tui/workflow/workflowOverlay.cjs.map +1 -0
- package/dist/src/tui/workflow/workflowOverlay.mjs +125 -0
- package/dist/src/tui/workflow/workflowOverlay.mjs.map +1 -0
- package/dist/src/tui/workflow/workflowPicker.cjs +138 -0
- package/dist/src/tui/workflow/workflowPicker.cjs.map +1 -0
- package/dist/src/tui/workflow/workflowPicker.mjs +136 -0
- package/dist/src/tui/workflow/workflowPicker.mjs.map +1 -0
- package/dist/src/tui/workflow/workflowProgressOverlay.cjs +197 -0
- package/dist/src/tui/workflow/workflowProgressOverlay.cjs.map +1 -0
- package/dist/src/tui/workflow/workflowProgressOverlay.mjs +192 -0
- package/dist/src/tui/workflow/workflowProgressOverlay.mjs.map +1 -0
- package/dist/src/tui/workflow/workflowPrompt.cjs +142 -0
- package/dist/src/tui/workflow/workflowPrompt.cjs.map +1 -0
- package/dist/src/tui/workflow/workflowPrompt.mjs +139 -0
- package/dist/src/tui/workflow/workflowPrompt.mjs.map +1 -0
- package/dist/src/tui/workflow/workflowRunPanel.cjs +95 -0
- package/dist/src/tui/workflow/workflowRunPanel.cjs.map +1 -0
- package/dist/src/tui/workflow/workflowRunPanel.mjs +94 -0
- package/dist/src/tui/workflow/workflowRunPanel.mjs.map +1 -0
- package/dist/src/tui/workflow/workflowStatusRow.cjs +20 -0
- package/dist/src/tui/workflow/workflowStatusRow.cjs.map +1 -0
- package/dist/src/tui/workflow/workflowStatusRow.mjs +20 -0
- package/dist/src/tui/workflow/workflowStatusRow.mjs.map +1 -0
- package/dist/src/tui/workflow/workflowStepMessage.cjs +32 -0
- package/dist/src/tui/workflow/workflowStepMessage.cjs.map +1 -0
- package/dist/src/tui/workflow/workflowStepMessage.mjs +31 -0
- package/dist/src/tui/workflow/workflowStepMessage.mjs.map +1 -0
- package/dist/src/tui/workflow/workflowToolRender.cjs +491 -0
- package/dist/src/tui/workflow/workflowToolRender.cjs.map +1 -0
- package/dist/src/tui/workflow/workflowToolRender.mjs +490 -0
- package/dist/src/tui/workflow/workflowToolRender.mjs.map +1 -0
- package/dist/src/tui/workflowRuntime.cjs +1934 -0
- package/dist/src/tui/workflowRuntime.cjs.map +1 -0
- package/dist/src/tui/workflowRuntime.d.cts +33 -0
- package/dist/src/tui/workflowRuntime.d.cts.map +1 -0
- package/dist/src/tui/workflowRuntime.d.mts +33 -0
- package/dist/src/tui/workflowRuntime.d.mts.map +1 -0
- package/dist/src/tui/workflowRuntime.mjs +1930 -0
- package/dist/src/tui/workflowRuntime.mjs.map +1 -0
- package/dist/src/types/apiRoutes.cjs +82 -0
- package/dist/src/types/apiRoutes.cjs.map +1 -0
- package/dist/src/types/apiRoutes.mjs +80 -0
- package/dist/src/types/apiRoutes.mjs.map +1 -0
- package/dist/src/types/webWorkflowTerminal.cjs +23 -0
- package/dist/src/types/webWorkflowTerminal.cjs.map +1 -0
- package/dist/src/types/webWorkflowTerminal.mjs +22 -0
- package/dist/src/types/webWorkflowTerminal.mjs.map +1 -0
- package/dist/src/types/webWorkflows.cjs +8 -0
- package/dist/src/types/webWorkflows.cjs.map +1 -0
- package/dist/src/types/webWorkflows.mjs +7 -0
- package/dist/src/types/webWorkflows.mjs.map +1 -0
- package/package.json +8 -8
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
const require_doomOverlay = require("./doomOverlay.cjs");
|
|
2
|
+
const require_overlayText = require("./overlayText.cjs");
|
|
3
|
+
const require_workflowStatusRow = require("./workflowStatusRow.cjs");
|
|
4
|
+
let _agimon_ai_workflow_mcp = require("@agimon-ai/workflow-mcp");
|
|
5
|
+
let _earendil_works_pi_tui = require("@earendil-works/pi-tui");
|
|
6
|
+
require_doomOverlay.DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;
|
|
7
|
+
const TITLE = "WORKFLOW SPACE";
|
|
8
|
+
const BREADCRUMB = "SPC › w / workflows › l / manage";
|
|
9
|
+
const ROSTER_HEADING = "RUNNING · this session";
|
|
10
|
+
const EMPTY_ROSTER = "No active workflows";
|
|
11
|
+
const PROGRESS_HEADING = "PROGRESS";
|
|
12
|
+
const OUTPUT_HEADING = "OUTPUT · newest last";
|
|
13
|
+
const NO_SELECTION = "Select a workflow to inspect its jobs and output.";
|
|
14
|
+
const RUNNING_STATE = "running";
|
|
15
|
+
const PAUSED_STATE = "paused";
|
|
16
|
+
/** Mirrors DoomPi's list-navigation legend without adding a runtime doompi-ui dependency. */
|
|
17
|
+
const LIST_NAVIGATION_KEYS = "↑↓";
|
|
18
|
+
const FOOTER = `${LIST_NAVIGATION_KEYS} select · PgUp/PgDn page · enter open · esc close`;
|
|
19
|
+
const FOOTER_HINTS = [
|
|
20
|
+
[LIST_NAVIGATION_KEYS, "select"],
|
|
21
|
+
["PgUp/PgDn", "page"],
|
|
22
|
+
["enter", "open"],
|
|
23
|
+
["esc", "close"]
|
|
24
|
+
];
|
|
25
|
+
const DEFAULT_REFRESH_MS = 750;
|
|
26
|
+
const SELECTION_MARKER = "›";
|
|
27
|
+
const ELLIPSIS = "…";
|
|
28
|
+
/** pi-tui brackets a truncation ellipsis with this; see `fit`. */
|
|
29
|
+
const HARD_RESET = "\x1B[0m";
|
|
30
|
+
/** A third for the roster, two thirds for the run being read. */
|
|
31
|
+
const LIST_PANE_RATIO = 1 / 3;
|
|
32
|
+
/** One blank column each side of the divider so neither pane touches it. */
|
|
33
|
+
const PANE_GUTTER = 1;
|
|
34
|
+
const MIN_PANE_WIDTH = 18;
|
|
35
|
+
/** Below this the roster cannot hold a readable name, so the detail takes the body. */
|
|
36
|
+
const MIN_TWO_PANE_WIDTH = 48;
|
|
37
|
+
/** Each run occupies a name row and a state row beneath it. */
|
|
38
|
+
const ROWS_PER_ITEM = 2;
|
|
39
|
+
/** Hanging indent for the state row: clears the marker and the glyph. */
|
|
40
|
+
const META_INDENT = 4;
|
|
41
|
+
/** Rows the roster heading and its spacer take. */
|
|
42
|
+
const ROSTER_CHROME_ROWS = 2;
|
|
43
|
+
/** The output tail keeps at least this many rows, or it is not worth a divider. */
|
|
44
|
+
const MIN_OUTPUT_ROWS = 2;
|
|
45
|
+
/**
|
|
46
|
+
* Truncates and pads to an exact column count.
|
|
47
|
+
*
|
|
48
|
+
* `truncateToWidth` wraps its ellipsis in a hard `\x1b[0m`, which would strip a
|
|
49
|
+
* row's background for everything after a clipped name. The theme closes its own
|
|
50
|
+
* colours with `\x1b[39m` and `\x1b[49m`, so the injected resets are dropped.
|
|
51
|
+
*/
|
|
52
|
+
function fit(text, width) {
|
|
53
|
+
const clipped = (0, _earendil_works_pi_tui.truncateToWidth)(text, Math.max(0, width), ELLIPSIS).replaceAll(HARD_RESET, "");
|
|
54
|
+
return clipped + " ".repeat(Math.max(0, width - (0, _earendil_works_pi_tui.visibleWidth)(clipped)));
|
|
55
|
+
}
|
|
56
|
+
function rightAligned(left, right, width) {
|
|
57
|
+
const rightWidth = (0, _earendil_works_pi_tui.visibleWidth)(right);
|
|
58
|
+
const leftWidth = Math.max(0, width - rightWidth - 1);
|
|
59
|
+
return fit(left, leftWidth) + " ".repeat(Math.max(1, width - leftWidth - rightWidth)) + fit(right, rightWidth);
|
|
60
|
+
}
|
|
61
|
+
function jobGlyph(status) {
|
|
62
|
+
return status === "completed" ? "✓" : status === "failed" ? "✗" : "●";
|
|
63
|
+
}
|
|
64
|
+
function jobColour(status) {
|
|
65
|
+
return status === "completed" ? "success" : status === "failed" ? "error" : "accent";
|
|
66
|
+
}
|
|
67
|
+
function progressLines(item, width, theme) {
|
|
68
|
+
const lines = [];
|
|
69
|
+
for (const job of item.jobs) {
|
|
70
|
+
const position = job.total ? ` · ${(job.index ?? 0) + 1}/${job.total}` : "";
|
|
71
|
+
lines.push((0, _earendil_works_pi_tui.truncateToWidth)(theme.fg(jobColour(job.status), `${jobGlyph(job.status)} ${job.name}`) + theme.fg("dim", position), width, ELLIPSIS));
|
|
72
|
+
if (job.status !== RUNNING_STATE) continue;
|
|
73
|
+
for (const step of job.steps) {
|
|
74
|
+
const stepColour = step.status === "failed" ? "error" : step.status === RUNNING_STATE ? "muted" : "dim";
|
|
75
|
+
lines.push((0, _earendil_works_pi_tui.truncateToWidth)(theme.fg(stepColour, ` ${jobGlyph(step.status)} ${step.name}`), width, ELLIPSIS));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return lines;
|
|
79
|
+
}
|
|
80
|
+
function detailLines(item, width, height, theme) {
|
|
81
|
+
if (!item) return [theme.fg("dim", NO_SELECTION)];
|
|
82
|
+
const position = (0, _agimon_ai_workflow_mcp.currentWorkflowPosition)(item.jobs);
|
|
83
|
+
const elapsed = require_workflowStatusRow.humanizeDuration(Math.max(0, Date.now() - Date.parse(item.startedAt)));
|
|
84
|
+
const executionState = item.executionState ?? RUNNING_STATE;
|
|
85
|
+
const stateColour = executionState === PAUSED_STATE ? "warning" : "dim";
|
|
86
|
+
const lines = [
|
|
87
|
+
rightAligned(`${theme.fg("accent", "●")} ${theme.bold(item.displayName)}`, theme.fg(stateColour, executionState), width),
|
|
88
|
+
(0, _earendil_works_pi_tui.truncateToWidth)(theme.fg("dim", `${item.runKey} · ${item.workspace} · ${elapsed}`), width, ELLIPSIS),
|
|
89
|
+
position ? (0, _earendil_works_pi_tui.truncateToWidth)(`${theme.fg("accent", position.job)}${position.step ? theme.fg("muted", ` · ${position.step}`) : ""}`, width, ELLIPSIS) : theme.fg("dim", "starting"),
|
|
90
|
+
"",
|
|
91
|
+
theme.fg("dim", PROGRESS_HEADING)
|
|
92
|
+
];
|
|
93
|
+
const reservedOutput = item.output.length > 0 ? 3 : 0;
|
|
94
|
+
const progressRoom = Math.max(0, height - lines.length - reservedOutput);
|
|
95
|
+
lines.push(...progressLines(item, width, theme).slice(0, progressRoom));
|
|
96
|
+
if (item.output.length > 0 && height - lines.length >= MIN_OUTPUT_ROWS) {
|
|
97
|
+
lines.push("", theme.fg("dim", OUTPUT_HEADING));
|
|
98
|
+
const outputRoom = Math.max(0, height - lines.length);
|
|
99
|
+
lines.push(...item.output.slice(-outputRoom).map((line) => require_overlayText.fitTerminalLine(line, width)));
|
|
100
|
+
}
|
|
101
|
+
return lines.slice(0, height);
|
|
102
|
+
}
|
|
103
|
+
var WorkflowInspectorComponent = class extends require_doomOverlay.DoomOverlay {
|
|
104
|
+
source;
|
|
105
|
+
done;
|
|
106
|
+
bodyHeight = 8;
|
|
107
|
+
completed = false;
|
|
108
|
+
error;
|
|
109
|
+
items = [];
|
|
110
|
+
refreshActive = false;
|
|
111
|
+
selected = 0;
|
|
112
|
+
selectedKey;
|
|
113
|
+
timer;
|
|
114
|
+
constructor(tui, theme, source, done, options = {}) {
|
|
115
|
+
super(tui, theme);
|
|
116
|
+
this.source = source;
|
|
117
|
+
this.done = done;
|
|
118
|
+
this.selectedKey = options.initialKey;
|
|
119
|
+
this.refresh();
|
|
120
|
+
this.timer = setInterval(() => void this.refresh(), options.refreshMs ?? DEFAULT_REFRESH_MS);
|
|
121
|
+
this.timer.unref?.();
|
|
122
|
+
}
|
|
123
|
+
async refresh() {
|
|
124
|
+
if (this.refreshActive || this.completed) return;
|
|
125
|
+
this.refreshActive = true;
|
|
126
|
+
try {
|
|
127
|
+
const previousKey = this.items[this.selected]?.key ?? this.selectedKey;
|
|
128
|
+
const items = await this.source.list();
|
|
129
|
+
if (this.completed) return;
|
|
130
|
+
const preserved = previousKey ? items.findIndex((item) => item.key === previousKey) : -1;
|
|
131
|
+
this.items = items;
|
|
132
|
+
this.selected = preserved >= 0 ? preserved : Math.min(this.selected, Math.max(0, items.length - 1));
|
|
133
|
+
this.selectedKey = items[this.selected]?.key;
|
|
134
|
+
const selected = items[this.selected];
|
|
135
|
+
if (selected) selected.output = await this.source.output(selected);
|
|
136
|
+
this.error = void 0;
|
|
137
|
+
if (!this.completed) this.tui.requestRender();
|
|
138
|
+
} catch (cause) {
|
|
139
|
+
this.error = cause instanceof Error ? cause.message : String(cause);
|
|
140
|
+
if (!this.completed) this.tui.requestRender();
|
|
141
|
+
} finally {
|
|
142
|
+
this.refreshActive = false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
move(delta) {
|
|
146
|
+
if (this.items.length === 0) return;
|
|
147
|
+
this.selected = Math.max(0, Math.min(this.items.length - 1, this.selected + delta));
|
|
148
|
+
this.selectedKey = this.items[this.selected]?.key;
|
|
149
|
+
this.refreshSelectedOutput();
|
|
150
|
+
this.tui.requestRender();
|
|
151
|
+
}
|
|
152
|
+
async refreshSelectedOutput() {
|
|
153
|
+
const selected = this.items[this.selected];
|
|
154
|
+
if (!selected || this.completed) return;
|
|
155
|
+
try {
|
|
156
|
+
selected.output = await this.source.output(selected);
|
|
157
|
+
this.error = void 0;
|
|
158
|
+
} catch (cause) {
|
|
159
|
+
this.error = cause instanceof Error ? cause.message : String(cause);
|
|
160
|
+
}
|
|
161
|
+
if (!this.completed && selected.key === this.selectedKey) this.tui.requestRender();
|
|
162
|
+
}
|
|
163
|
+
finish(selection) {
|
|
164
|
+
if (this.completed) return;
|
|
165
|
+
this.completed = true;
|
|
166
|
+
clearInterval(this.timer);
|
|
167
|
+
this.done(selection);
|
|
168
|
+
}
|
|
169
|
+
close() {
|
|
170
|
+
this.finish(void 0);
|
|
171
|
+
}
|
|
172
|
+
handleInput(data) {
|
|
173
|
+
if ((0, _earendil_works_pi_tui.matchesKey)(data, "escape") || (0, _earendil_works_pi_tui.matchesKey)(data, "ctrl+c")) {
|
|
174
|
+
this.finish(void 0);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if ((0, _earendil_works_pi_tui.matchesKey)(data, "up") || data === "k") {
|
|
178
|
+
this.move(-1);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if ((0, _earendil_works_pi_tui.matchesKey)(data, "down") || data === "j") {
|
|
182
|
+
this.move(1);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if ((0, _earendil_works_pi_tui.matchesKey)(data, "pageUp")) {
|
|
186
|
+
this.move(-this.rosterCapacity());
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if ((0, _earendil_works_pi_tui.matchesKey)(data, "pageDown")) {
|
|
190
|
+
this.move(this.rosterCapacity());
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if ((0, _earendil_works_pi_tui.matchesKey)(data, "enter")) {
|
|
194
|
+
const selected = this.items[this.selected];
|
|
195
|
+
if (selected) this.finish({
|
|
196
|
+
...selected.runId ? { runId: selected.runId } : {},
|
|
197
|
+
runKey: selected.runKey,
|
|
198
|
+
startedAt: selected.startedAt,
|
|
199
|
+
workspace: selected.workspace
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/** Runs the roster can draw, which is also what a page key moves by. */
|
|
204
|
+
rosterCapacity() {
|
|
205
|
+
return Math.max(1, Math.floor((this.bodyHeight - ROSTER_CHROME_ROWS) / ROWS_PER_ITEM));
|
|
206
|
+
}
|
|
207
|
+
current() {
|
|
208
|
+
if (this.items.length === 0) return void 0;
|
|
209
|
+
return this.items[Math.min(this.selected, this.items.length - 1)];
|
|
210
|
+
}
|
|
211
|
+
getChrome() {
|
|
212
|
+
const selected = this.current();
|
|
213
|
+
const summary = selected ? `${selected.displayName} · ${selected.executionState ?? RUNNING_STATE}` : "no active workflows";
|
|
214
|
+
return {
|
|
215
|
+
title: TITLE,
|
|
216
|
+
accent: require_doomOverlay.DOOM_OVERLAY_ACCENT,
|
|
217
|
+
breadcrumb: BREADCRUMB,
|
|
218
|
+
headerRight: `${this.items.length} running · ${summary}`,
|
|
219
|
+
footer: FOOTER,
|
|
220
|
+
footerHints: FOOTER_HINTS,
|
|
221
|
+
footerRight: this.items.length > 0 ? `${Math.min(this.selected, this.items.length - 1) + 1}/${this.items.length}` : "empty"
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
renderBody(width, height) {
|
|
225
|
+
this.bodyHeight = height;
|
|
226
|
+
const detailFor = (paneWidth) => this.error ? [this.theme.fg("warning", `Unable to refresh workflows: ${this.error}`)] : detailLines(this.current(), paneWidth, height, this.theme);
|
|
227
|
+
if (width < MIN_TWO_PANE_WIDTH) return detailFor(width).slice(0, height);
|
|
228
|
+
const leftWidth = Math.max(MIN_PANE_WIDTH, Math.floor((width - 1) * LIST_PANE_RATIO));
|
|
229
|
+
const rightWidth = Math.max(1, width - leftWidth - 1);
|
|
230
|
+
const leftContent = Math.max(1, leftWidth - PANE_GUTTER);
|
|
231
|
+
const rightContent = Math.max(1, rightWidth - PANE_GUTTER);
|
|
232
|
+
const left = this.rosterLines(leftContent);
|
|
233
|
+
const right = detailFor(rightContent);
|
|
234
|
+
const divider = this.theme.fg("borderMuted", "│");
|
|
235
|
+
return Array.from({ length: height }, (_, index) => {
|
|
236
|
+
const row = `${fit(left[index] ?? "", leftContent)} ${divider} ${require_overlayText.fitTerminalLine(right[index] ?? "", rightContent)}`;
|
|
237
|
+
return (0, _earendil_works_pi_tui.truncateToWidth)(row, width, ELLIPSIS);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
rosterLines(width) {
|
|
241
|
+
const lines = [this.theme.bold(ROSTER_HEADING), ""];
|
|
242
|
+
if (this.items.length === 0) {
|
|
243
|
+
lines.push(this.theme.fg("dim", EMPTY_ROSTER));
|
|
244
|
+
return lines;
|
|
245
|
+
}
|
|
246
|
+
const budget = this.rosterCapacity();
|
|
247
|
+
const active = Math.min(this.selected, this.items.length - 1);
|
|
248
|
+
const start = Math.max(0, Math.min(active - budget + 1, Math.max(0, this.items.length - budget)));
|
|
249
|
+
for (const [offset, item] of this.items.slice(start, start + budget).entries()) {
|
|
250
|
+
const current = start + offset === active;
|
|
251
|
+
const paused = item.executionState === PAUSED_STATE;
|
|
252
|
+
const marker = current ? this.theme.fg("accent", SELECTION_MARKER) : " ";
|
|
253
|
+
const glyph = this.theme.fg(paused ? "warning" : "accent", "●");
|
|
254
|
+
const name = current ? this.theme.bold(item.displayName) : item.displayName;
|
|
255
|
+
const stage = paused ? PAUSED_STATE : (0, _agimon_ai_workflow_mcp.currentWorkflowPosition)(item.jobs)?.job ?? "starting";
|
|
256
|
+
const heading = `${marker} ${glyph} ${name}`;
|
|
257
|
+
const meta = `${" ".repeat(META_INDENT)}${this.theme.fg(paused ? "warning" : "dim", stage)}`;
|
|
258
|
+
const background = current ? "selectedBg" : "userMessageBg";
|
|
259
|
+
for (const row of [heading, meta]) lines.push(this.theme.bg(background, fit(row, width)));
|
|
260
|
+
}
|
|
261
|
+
return lines;
|
|
262
|
+
}
|
|
263
|
+
/** Overridden: a repaint request is also the cue to re-read the registry. */
|
|
264
|
+
invalidate() {
|
|
265
|
+
this.refresh();
|
|
266
|
+
}
|
|
267
|
+
dispose() {
|
|
268
|
+
if (this.completed) return;
|
|
269
|
+
this.completed = true;
|
|
270
|
+
clearInterval(this.timer);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
//#endregion
|
|
274
|
+
exports.WorkflowInspectorComponent = WorkflowInspectorComponent;
|
|
275
|
+
|
|
276
|
+
//# sourceMappingURL=workflowInspector.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowInspector.cjs","names":["DOOM_FULLSCREEN_UI_OPTIONS","truncateToWidth","visibleWidth","currentWorkflowPosition","humanizeDuration","fitTerminalLine","DoomOverlay","matchesKey","DOOM_OVERLAY_ACCENT"],"sources":["../../../../src/tui/workflow/workflowInspector.ts"],"sourcesContent":["/**\n * Workflow Space: the `SPC w l` overlay.\n *\n * A doom overlay rather than a hand-rolled frame: the roster owns the left\n * third, the run under the cursor owns the right two thirds, and the chrome is\n * the same frame, breadcrumb and key legend every other doom surface draws.\n *\n * Rendering is pure -- `renderBody(width, height)` reads the last polled\n * snapshot and returns lines with no side effects -- so the surface can be\n * asserted as text without a live terminal.\n */\n\nimport { currentWorkflowPosition, type WorkflowProgressJob } from '@agimon-ai/workflow-mcp';\nimport type { Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\n\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from './doomOverlay';\nimport { fitTerminalLine } from './overlayText';\nimport { humanizeDuration } from './workflowStatusRow';\n\nexport const WORKFLOW_INSPECTOR_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nexport interface WorkflowInspectorSelection {\n runId?: string;\n runKey: string;\n startedAt: string;\n workspace: string;\n}\n\nexport interface WorkflowInspectorItem extends WorkflowInspectorSelection {\n displayName: string;\n executionState?: string;\n jobs: WorkflowProgressJob[];\n key: string;\n output: string[];\n}\n\nexport interface WorkflowInspectorSource {\n list(): Promise<WorkflowInspectorItem[]>;\n output(item: WorkflowInspectorItem): Promise<string[]>;\n}\n\nexport interface WorkflowInspectorOptions {\n initialKey?: string;\n refreshMs?: number;\n}\n\nconst TITLE = 'WORKFLOW SPACE';\nconst BREADCRUMB = 'SPC › w / workflows › l / manage';\nconst ROSTER_HEADING = 'RUNNING · this session';\nconst EMPTY_ROSTER = 'No active workflows';\nconst PROGRESS_HEADING = 'PROGRESS';\nconst OUTPUT_HEADING = 'OUTPUT · newest last';\nconst NO_SELECTION = 'Select a workflow to inspect its jobs and output.';\nconst RUNNING_STATE = 'running';\nconst PAUSED_STATE = 'paused';\n/** Mirrors DoomPi's list-navigation legend without adding a runtime doompi-ui dependency. */\nconst LIST_NAVIGATION_KEYS = '↑↓';\nconst FOOTER = `${LIST_NAVIGATION_KEYS} select · PgUp/PgDn page · enter open · esc close`;\nconst FOOTER_HINTS: readonly (readonly [string, string])[] = [\n [LIST_NAVIGATION_KEYS, 'select'],\n ['PgUp/PgDn', 'page'],\n ['enter', 'open'],\n ['esc', 'close'],\n];\n\nconst DEFAULT_REFRESH_MS = 750;\nconst SELECTION_MARKER = '›';\nconst ELLIPSIS = '…';\n/** pi-tui brackets a truncation ellipsis with this; see `fit`. */\nconst HARD_RESET = '\\x1b[0m';\n/** A third for the roster, two thirds for the run being read. */\nconst LIST_PANE_RATIO = 1 / 3;\n/** One blank column each side of the divider so neither pane touches it. */\nconst PANE_GUTTER = 1;\nconst MIN_PANE_WIDTH = 18;\n/** Below this the roster cannot hold a readable name, so the detail takes the body. */\nconst MIN_TWO_PANE_WIDTH = 48;\n/** Each run occupies a name row and a state row beneath it. */\nconst ROWS_PER_ITEM = 2;\n/** Hanging indent for the state row: clears the marker and the glyph. */\nconst META_INDENT = 4;\n/** Rows the roster heading and its spacer take. */\nconst ROSTER_CHROME_ROWS = 2;\n/** The output tail keeps at least this many rows, or it is not worth a divider. */\nconst MIN_OUTPUT_ROWS = 2;\n\n/** Neither palette name is exported, so take both from the theme's methods. */\ntype ThemeBg = Parameters<Theme['bg']>[0];\ntype ThemeColor = Parameters<Theme['fg']>[0];\n\n/**\n * Truncates and pads to an exact column count.\n *\n * `truncateToWidth` wraps its ellipsis in a hard `\\x1b[0m`, which would strip a\n * row's background for everything after a clipped name. The theme closes its own\n * colours with `\\x1b[39m` and `\\x1b[49m`, so the injected resets are dropped.\n */\nfunction fit(text: string, width: number): string {\n const clipped = truncateToWidth(text, Math.max(0, width), ELLIPSIS).replaceAll(HARD_RESET, '');\n return clipped + ' '.repeat(Math.max(0, width - visibleWidth(clipped)));\n}\n\nfunction rightAligned(left: string, right: string, width: number): string {\n const rightWidth = visibleWidth(right);\n const leftWidth = Math.max(0, width - rightWidth - 1);\n return fit(left, leftWidth) + ' '.repeat(Math.max(1, width - leftWidth - rightWidth)) + fit(right, rightWidth);\n}\n\nfunction jobGlyph(status: string): string {\n return status === 'completed' ? '✓' : status === 'failed' ? '✗' : '●';\n}\n\nfunction jobColour(status: string): ThemeColor {\n return status === 'completed' ? 'success' : status === 'failed' ? 'error' : 'accent';\n}\n\nfunction progressLines(item: WorkflowInspectorItem, width: number, theme: Theme): string[] {\n const lines: string[] = [];\n for (const job of item.jobs) {\n const position = job.total ? ` · ${(job.index ?? 0) + 1}/${job.total}` : '';\n lines.push(\n truncateToWidth(\n theme.fg(jobColour(job.status), `${jobGlyph(job.status)} ${job.name}`) + theme.fg('dim', position),\n width,\n ELLIPSIS,\n ),\n );\n // Only the running job's steps: every finished job's steps would push the\n // live one off the pane, which is the one thing worth watching.\n if (job.status !== RUNNING_STATE) continue;\n for (const step of job.steps) {\n const stepColour: ThemeColor =\n step.status === 'failed' ? 'error' : step.status === RUNNING_STATE ? 'muted' : 'dim';\n lines.push(truncateToWidth(theme.fg(stepColour, ` ${jobGlyph(step.status)} ${step.name}`), width, ELLIPSIS));\n }\n }\n return lines;\n}\n\nfunction detailLines(item: WorkflowInspectorItem | undefined, width: number, height: number, theme: Theme): string[] {\n if (!item) return [theme.fg('dim', NO_SELECTION)];\n\n const position = currentWorkflowPosition(item.jobs);\n const elapsed = humanizeDuration(Math.max(0, Date.now() - Date.parse(item.startedAt)));\n const executionState = item.executionState ?? RUNNING_STATE;\n const stateColour: ThemeColor = executionState === PAUSED_STATE ? 'warning' : 'dim';\n const lines = [\n rightAligned(\n `${theme.fg('accent', '●')} ${theme.bold(item.displayName)}`,\n theme.fg(stateColour, executionState),\n width,\n ),\n truncateToWidth(theme.fg('dim', `${item.runKey} · ${item.workspace} · ${elapsed}`), width, ELLIPSIS),\n position\n ? truncateToWidth(\n `${theme.fg('accent', position.job)}${position.step ? theme.fg('muted', ` · ${position.step}`) : ''}`,\n width,\n ELLIPSIS,\n )\n : theme.fg('dim', 'starting'),\n '',\n theme.fg('dim', PROGRESS_HEADING),\n ];\n\n // The output tail claims its rows first: progress is a list that can be paged\n // to by opening the run, while the tail is the only live signal on screen.\n const reservedOutput = item.output.length > 0 ? MIN_OUTPUT_ROWS + 1 : 0;\n const progressRoom = Math.max(0, height - lines.length - reservedOutput);\n lines.push(...progressLines(item, width, theme).slice(0, progressRoom));\n\n if (item.output.length > 0 && height - lines.length >= MIN_OUTPUT_ROWS) {\n lines.push('', theme.fg('dim', OUTPUT_HEADING));\n const outputRoom = Math.max(0, height - lines.length);\n // Captured output carries the run's own colour, so it is fitted by the\n // escape-preserving path: `fit` below drops hard resets, and a capture that\n // lost its reset bleeds its colour through the divider and down the pane.\n lines.push(...item.output.slice(-outputRoom).map((line) => fitTerminalLine(line, width)));\n }\n\n return lines.slice(0, height);\n}\n\nexport class WorkflowInspectorComponent extends DoomOverlay {\n private bodyHeight = 8;\n private completed = false;\n private error: string | undefined;\n private items: WorkflowInspectorItem[] = [];\n private refreshActive = false;\n private selected = 0;\n private selectedKey: string | undefined;\n private readonly timer: ReturnType<typeof setInterval>;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly source: WorkflowInspectorSource,\n private readonly done: (selection: WorkflowInspectorSelection | undefined) => void,\n options: WorkflowInspectorOptions = {},\n ) {\n super(tui, theme);\n this.selectedKey = options.initialKey;\n void this.refresh();\n this.timer = setInterval(() => void this.refresh(), options.refreshMs ?? DEFAULT_REFRESH_MS);\n this.timer.unref?.();\n }\n\n private async refresh(): Promise<void> {\n if (this.refreshActive || this.completed) return;\n this.refreshActive = true;\n try {\n const previousKey = this.items[this.selected]?.key ?? this.selectedKey;\n const items = await this.source.list();\n if (this.completed) return;\n const preserved = previousKey ? items.findIndex((item) => item.key === previousKey) : -1;\n this.items = items;\n this.selected = preserved >= 0 ? preserved : Math.min(this.selected, Math.max(0, items.length - 1));\n this.selectedKey = items[this.selected]?.key;\n const selected = items[this.selected];\n if (selected) selected.output = await this.source.output(selected);\n this.error = undefined;\n if (!this.completed) this.tui.requestRender();\n } catch (cause) {\n this.error = cause instanceof Error ? cause.message : String(cause);\n if (!this.completed) this.tui.requestRender();\n } finally {\n this.refreshActive = false;\n }\n }\n\n private move(delta: number): void {\n if (this.items.length === 0) return;\n this.selected = Math.max(0, Math.min(this.items.length - 1, this.selected + delta));\n this.selectedKey = this.items[this.selected]?.key;\n void this.refreshSelectedOutput();\n this.tui.requestRender();\n }\n\n private async refreshSelectedOutput(): Promise<void> {\n const selected = this.items[this.selected];\n if (!selected || this.completed) return;\n try {\n selected.output = await this.source.output(selected);\n this.error = undefined;\n } catch (cause) {\n this.error = cause instanceof Error ? cause.message : String(cause);\n }\n if (!this.completed && selected.key === this.selectedKey) this.tui.requestRender();\n }\n\n private finish(selection: WorkflowInspectorSelection | undefined): void {\n if (this.completed) return;\n this.completed = true;\n clearInterval(this.timer);\n this.done(selection);\n }\n\n close(): void {\n this.finish(undefined);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.finish(undefined);\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') {\n this.move(-1);\n return;\n }\n if (matchesKey(data, 'down') || data === 'j') {\n this.move(1);\n return;\n }\n if (matchesKey(data, 'pageUp')) {\n this.move(-this.rosterCapacity());\n return;\n }\n if (matchesKey(data, 'pageDown')) {\n this.move(this.rosterCapacity());\n return;\n }\n if (matchesKey(data, 'enter')) {\n const selected = this.items[this.selected];\n if (selected) {\n this.finish({\n ...(selected.runId ? { runId: selected.runId } : {}),\n runKey: selected.runKey,\n startedAt: selected.startedAt,\n workspace: selected.workspace,\n });\n }\n }\n }\n\n /** Runs the roster can draw, which is also what a page key moves by. */\n private rosterCapacity(): number {\n return Math.max(1, Math.floor((this.bodyHeight - ROSTER_CHROME_ROWS) / ROWS_PER_ITEM));\n }\n\n private current(): WorkflowInspectorItem | undefined {\n if (this.items.length === 0) return undefined;\n return this.items[Math.min(this.selected, this.items.length - 1)];\n }\n\n protected getChrome(): DoomOverlayChrome {\n const selected = this.current();\n const summary = selected\n ? `${selected.displayName} · ${selected.executionState ?? RUNNING_STATE}`\n : 'no active workflows';\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: BREADCRUMB,\n headerRight: `${this.items.length} running · ${summary}`,\n footer: FOOTER,\n footerHints: FOOTER_HINTS,\n footerRight:\n this.items.length > 0 ? `${Math.min(this.selected, this.items.length - 1) + 1}/${this.items.length}` : 'empty',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n this.bodyHeight = height;\n const detailFor = (paneWidth: number): string[] =>\n this.error\n ? [this.theme.fg('warning', `Unable to refresh workflows: ${this.error}`)]\n : detailLines(this.current(), paneWidth, height, this.theme);\n\n // Too narrow for two useful columns: the detail takes the whole body rather\n // than shredding both panes into stubs.\n if (width < MIN_TWO_PANE_WIDTH) return detailFor(width).slice(0, height);\n\n const leftWidth = Math.max(MIN_PANE_WIDTH, Math.floor((width - 1) * LIST_PANE_RATIO));\n const rightWidth = Math.max(1, width - leftWidth - 1);\n const leftContent = Math.max(1, leftWidth - PANE_GUTTER);\n const rightContent = Math.max(1, rightWidth - PANE_GUTTER);\n\n const left = this.rosterLines(leftContent);\n const right = detailFor(rightContent);\n const divider = this.theme.fg('borderMuted', '│');\n return Array.from({ length: height }, (_, index) => {\n // The roster keeps `fit`, whose reset stripping protects its row\n // backgrounds; the detail pane has none and may carry captured colour.\n const row = `${fit(left[index] ?? '', leftContent)} ${divider} ${fitTerminalLine(\n right[index] ?? '',\n rightContent,\n )}`;\n return truncateToWidth(row, width, ELLIPSIS);\n });\n }\n\n private rosterLines(width: number): string[] {\n const lines = [this.theme.bold(ROSTER_HEADING), ''];\n if (this.items.length === 0) {\n lines.push(this.theme.fg('dim', EMPTY_ROSTER));\n return lines;\n }\n\n // Two rows per run: at a third of the width a single row left the name with\n // a handful of columns once the stage took its share.\n const budget = this.rosterCapacity();\n const active = Math.min(this.selected, this.items.length - 1);\n const start = Math.max(0, Math.min(active - budget + 1, Math.max(0, this.items.length - budget)));\n for (const [offset, item] of this.items.slice(start, start + budget).entries()) {\n const current = start + offset === active;\n const paused = item.executionState === PAUSED_STATE;\n const marker = current ? this.theme.fg('accent', SELECTION_MARKER) : ' ';\n const glyph = this.theme.fg(paused ? 'warning' : 'accent', '●');\n const name = current ? this.theme.bold(item.displayName) : item.displayName;\n const stage = paused ? PAUSED_STATE : (currentWorkflowPosition(item.jobs)?.job ?? 'starting');\n const heading = `${marker} ${glyph} ${name}`;\n const meta = `${' '.repeat(META_INDENT)}${this.theme.fg(paused ? 'warning' : 'dim', stage)}`;\n const background: ThemeBg = current ? 'selectedBg' : 'userMessageBg';\n for (const row of [heading, meta]) lines.push(this.theme.bg(background, fit(row, width)));\n }\n return lines;\n }\n\n /** Overridden: a repaint request is also the cue to re-read the registry. */\n invalidate(): void {\n void this.refresh();\n }\n\n dispose(): void {\n if (this.completed) return;\n this.completed = true;\n clearInterval(this.timer);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { DOOM_FULLSCREEN_UI_OPTIONS, DOOM_OVERLAY_ACCENT, DoomOverlay } from "./doomOverlay.mjs";
|
|
2
|
+
import { fitTerminalLine } from "./overlayText.mjs";
|
|
3
|
+
import { humanizeDuration } from "./workflowStatusRow.mjs";
|
|
4
|
+
import { currentWorkflowPosition } from "@agimon-ai/workflow-mcp";
|
|
5
|
+
import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
6
|
+
DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;
|
|
7
|
+
const TITLE = "WORKFLOW SPACE";
|
|
8
|
+
const BREADCRUMB = "SPC › w / workflows › l / manage";
|
|
9
|
+
const ROSTER_HEADING = "RUNNING · this session";
|
|
10
|
+
const EMPTY_ROSTER = "No active workflows";
|
|
11
|
+
const PROGRESS_HEADING = "PROGRESS";
|
|
12
|
+
const OUTPUT_HEADING = "OUTPUT · newest last";
|
|
13
|
+
const NO_SELECTION = "Select a workflow to inspect its jobs and output.";
|
|
14
|
+
const RUNNING_STATE = "running";
|
|
15
|
+
const PAUSED_STATE = "paused";
|
|
16
|
+
/** Mirrors DoomPi's list-navigation legend without adding a runtime doompi-ui dependency. */
|
|
17
|
+
const LIST_NAVIGATION_KEYS = "↑↓";
|
|
18
|
+
const FOOTER = `${LIST_NAVIGATION_KEYS} select · PgUp/PgDn page · enter open · esc close`;
|
|
19
|
+
const FOOTER_HINTS = [
|
|
20
|
+
[LIST_NAVIGATION_KEYS, "select"],
|
|
21
|
+
["PgUp/PgDn", "page"],
|
|
22
|
+
["enter", "open"],
|
|
23
|
+
["esc", "close"]
|
|
24
|
+
];
|
|
25
|
+
const DEFAULT_REFRESH_MS = 750;
|
|
26
|
+
const SELECTION_MARKER = "›";
|
|
27
|
+
const ELLIPSIS = "…";
|
|
28
|
+
/** pi-tui brackets a truncation ellipsis with this; see `fit`. */
|
|
29
|
+
const HARD_RESET = "\x1B[0m";
|
|
30
|
+
/** A third for the roster, two thirds for the run being read. */
|
|
31
|
+
const LIST_PANE_RATIO = 1 / 3;
|
|
32
|
+
/** One blank column each side of the divider so neither pane touches it. */
|
|
33
|
+
const PANE_GUTTER = 1;
|
|
34
|
+
const MIN_PANE_WIDTH = 18;
|
|
35
|
+
/** Below this the roster cannot hold a readable name, so the detail takes the body. */
|
|
36
|
+
const MIN_TWO_PANE_WIDTH = 48;
|
|
37
|
+
/** Each run occupies a name row and a state row beneath it. */
|
|
38
|
+
const ROWS_PER_ITEM = 2;
|
|
39
|
+
/** Hanging indent for the state row: clears the marker and the glyph. */
|
|
40
|
+
const META_INDENT = 4;
|
|
41
|
+
/** Rows the roster heading and its spacer take. */
|
|
42
|
+
const ROSTER_CHROME_ROWS = 2;
|
|
43
|
+
/** The output tail keeps at least this many rows, or it is not worth a divider. */
|
|
44
|
+
const MIN_OUTPUT_ROWS = 2;
|
|
45
|
+
/**
|
|
46
|
+
* Truncates and pads to an exact column count.
|
|
47
|
+
*
|
|
48
|
+
* `truncateToWidth` wraps its ellipsis in a hard `\x1b[0m`, which would strip a
|
|
49
|
+
* row's background for everything after a clipped name. The theme closes its own
|
|
50
|
+
* colours with `\x1b[39m` and `\x1b[49m`, so the injected resets are dropped.
|
|
51
|
+
*/
|
|
52
|
+
function fit(text, width) {
|
|
53
|
+
const clipped = truncateToWidth(text, Math.max(0, width), ELLIPSIS).replaceAll(HARD_RESET, "");
|
|
54
|
+
return clipped + " ".repeat(Math.max(0, width - visibleWidth(clipped)));
|
|
55
|
+
}
|
|
56
|
+
function rightAligned(left, right, width) {
|
|
57
|
+
const rightWidth = visibleWidth(right);
|
|
58
|
+
const leftWidth = Math.max(0, width - rightWidth - 1);
|
|
59
|
+
return fit(left, leftWidth) + " ".repeat(Math.max(1, width - leftWidth - rightWidth)) + fit(right, rightWidth);
|
|
60
|
+
}
|
|
61
|
+
function jobGlyph(status) {
|
|
62
|
+
return status === "completed" ? "✓" : status === "failed" ? "✗" : "●";
|
|
63
|
+
}
|
|
64
|
+
function jobColour(status) {
|
|
65
|
+
return status === "completed" ? "success" : status === "failed" ? "error" : "accent";
|
|
66
|
+
}
|
|
67
|
+
function progressLines(item, width, theme) {
|
|
68
|
+
const lines = [];
|
|
69
|
+
for (const job of item.jobs) {
|
|
70
|
+
const position = job.total ? ` · ${(job.index ?? 0) + 1}/${job.total}` : "";
|
|
71
|
+
lines.push(truncateToWidth(theme.fg(jobColour(job.status), `${jobGlyph(job.status)} ${job.name}`) + theme.fg("dim", position), width, ELLIPSIS));
|
|
72
|
+
if (job.status !== RUNNING_STATE) continue;
|
|
73
|
+
for (const step of job.steps) {
|
|
74
|
+
const stepColour = step.status === "failed" ? "error" : step.status === RUNNING_STATE ? "muted" : "dim";
|
|
75
|
+
lines.push(truncateToWidth(theme.fg(stepColour, ` ${jobGlyph(step.status)} ${step.name}`), width, ELLIPSIS));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return lines;
|
|
79
|
+
}
|
|
80
|
+
function detailLines(item, width, height, theme) {
|
|
81
|
+
if (!item) return [theme.fg("dim", NO_SELECTION)];
|
|
82
|
+
const position = currentWorkflowPosition(item.jobs);
|
|
83
|
+
const elapsed = humanizeDuration(Math.max(0, Date.now() - Date.parse(item.startedAt)));
|
|
84
|
+
const executionState = item.executionState ?? RUNNING_STATE;
|
|
85
|
+
const stateColour = executionState === PAUSED_STATE ? "warning" : "dim";
|
|
86
|
+
const lines = [
|
|
87
|
+
rightAligned(`${theme.fg("accent", "●")} ${theme.bold(item.displayName)}`, theme.fg(stateColour, executionState), width),
|
|
88
|
+
truncateToWidth(theme.fg("dim", `${item.runKey} · ${item.workspace} · ${elapsed}`), width, ELLIPSIS),
|
|
89
|
+
position ? truncateToWidth(`${theme.fg("accent", position.job)}${position.step ? theme.fg("muted", ` · ${position.step}`) : ""}`, width, ELLIPSIS) : theme.fg("dim", "starting"),
|
|
90
|
+
"",
|
|
91
|
+
theme.fg("dim", PROGRESS_HEADING)
|
|
92
|
+
];
|
|
93
|
+
const reservedOutput = item.output.length > 0 ? 3 : 0;
|
|
94
|
+
const progressRoom = Math.max(0, height - lines.length - reservedOutput);
|
|
95
|
+
lines.push(...progressLines(item, width, theme).slice(0, progressRoom));
|
|
96
|
+
if (item.output.length > 0 && height - lines.length >= MIN_OUTPUT_ROWS) {
|
|
97
|
+
lines.push("", theme.fg("dim", OUTPUT_HEADING));
|
|
98
|
+
const outputRoom = Math.max(0, height - lines.length);
|
|
99
|
+
lines.push(...item.output.slice(-outputRoom).map((line) => fitTerminalLine(line, width)));
|
|
100
|
+
}
|
|
101
|
+
return lines.slice(0, height);
|
|
102
|
+
}
|
|
103
|
+
var WorkflowInspectorComponent = class extends DoomOverlay {
|
|
104
|
+
source;
|
|
105
|
+
done;
|
|
106
|
+
bodyHeight = 8;
|
|
107
|
+
completed = false;
|
|
108
|
+
error;
|
|
109
|
+
items = [];
|
|
110
|
+
refreshActive = false;
|
|
111
|
+
selected = 0;
|
|
112
|
+
selectedKey;
|
|
113
|
+
timer;
|
|
114
|
+
constructor(tui, theme, source, done, options = {}) {
|
|
115
|
+
super(tui, theme);
|
|
116
|
+
this.source = source;
|
|
117
|
+
this.done = done;
|
|
118
|
+
this.selectedKey = options.initialKey;
|
|
119
|
+
this.refresh();
|
|
120
|
+
this.timer = setInterval(() => void this.refresh(), options.refreshMs ?? DEFAULT_REFRESH_MS);
|
|
121
|
+
this.timer.unref?.();
|
|
122
|
+
}
|
|
123
|
+
async refresh() {
|
|
124
|
+
if (this.refreshActive || this.completed) return;
|
|
125
|
+
this.refreshActive = true;
|
|
126
|
+
try {
|
|
127
|
+
const previousKey = this.items[this.selected]?.key ?? this.selectedKey;
|
|
128
|
+
const items = await this.source.list();
|
|
129
|
+
if (this.completed) return;
|
|
130
|
+
const preserved = previousKey ? items.findIndex((item) => item.key === previousKey) : -1;
|
|
131
|
+
this.items = items;
|
|
132
|
+
this.selected = preserved >= 0 ? preserved : Math.min(this.selected, Math.max(0, items.length - 1));
|
|
133
|
+
this.selectedKey = items[this.selected]?.key;
|
|
134
|
+
const selected = items[this.selected];
|
|
135
|
+
if (selected) selected.output = await this.source.output(selected);
|
|
136
|
+
this.error = void 0;
|
|
137
|
+
if (!this.completed) this.tui.requestRender();
|
|
138
|
+
} catch (cause) {
|
|
139
|
+
this.error = cause instanceof Error ? cause.message : String(cause);
|
|
140
|
+
if (!this.completed) this.tui.requestRender();
|
|
141
|
+
} finally {
|
|
142
|
+
this.refreshActive = false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
move(delta) {
|
|
146
|
+
if (this.items.length === 0) return;
|
|
147
|
+
this.selected = Math.max(0, Math.min(this.items.length - 1, this.selected + delta));
|
|
148
|
+
this.selectedKey = this.items[this.selected]?.key;
|
|
149
|
+
this.refreshSelectedOutput();
|
|
150
|
+
this.tui.requestRender();
|
|
151
|
+
}
|
|
152
|
+
async refreshSelectedOutput() {
|
|
153
|
+
const selected = this.items[this.selected];
|
|
154
|
+
if (!selected || this.completed) return;
|
|
155
|
+
try {
|
|
156
|
+
selected.output = await this.source.output(selected);
|
|
157
|
+
this.error = void 0;
|
|
158
|
+
} catch (cause) {
|
|
159
|
+
this.error = cause instanceof Error ? cause.message : String(cause);
|
|
160
|
+
}
|
|
161
|
+
if (!this.completed && selected.key === this.selectedKey) this.tui.requestRender();
|
|
162
|
+
}
|
|
163
|
+
finish(selection) {
|
|
164
|
+
if (this.completed) return;
|
|
165
|
+
this.completed = true;
|
|
166
|
+
clearInterval(this.timer);
|
|
167
|
+
this.done(selection);
|
|
168
|
+
}
|
|
169
|
+
close() {
|
|
170
|
+
this.finish(void 0);
|
|
171
|
+
}
|
|
172
|
+
handleInput(data) {
|
|
173
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
174
|
+
this.finish(void 0);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (matchesKey(data, "up") || data === "k") {
|
|
178
|
+
this.move(-1);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (matchesKey(data, "down") || data === "j") {
|
|
182
|
+
this.move(1);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (matchesKey(data, "pageUp")) {
|
|
186
|
+
this.move(-this.rosterCapacity());
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (matchesKey(data, "pageDown")) {
|
|
190
|
+
this.move(this.rosterCapacity());
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (matchesKey(data, "enter")) {
|
|
194
|
+
const selected = this.items[this.selected];
|
|
195
|
+
if (selected) this.finish({
|
|
196
|
+
...selected.runId ? { runId: selected.runId } : {},
|
|
197
|
+
runKey: selected.runKey,
|
|
198
|
+
startedAt: selected.startedAt,
|
|
199
|
+
workspace: selected.workspace
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/** Runs the roster can draw, which is also what a page key moves by. */
|
|
204
|
+
rosterCapacity() {
|
|
205
|
+
return Math.max(1, Math.floor((this.bodyHeight - ROSTER_CHROME_ROWS) / ROWS_PER_ITEM));
|
|
206
|
+
}
|
|
207
|
+
current() {
|
|
208
|
+
if (this.items.length === 0) return void 0;
|
|
209
|
+
return this.items[Math.min(this.selected, this.items.length - 1)];
|
|
210
|
+
}
|
|
211
|
+
getChrome() {
|
|
212
|
+
const selected = this.current();
|
|
213
|
+
const summary = selected ? `${selected.displayName} · ${selected.executionState ?? RUNNING_STATE}` : "no active workflows";
|
|
214
|
+
return {
|
|
215
|
+
title: TITLE,
|
|
216
|
+
accent: DOOM_OVERLAY_ACCENT,
|
|
217
|
+
breadcrumb: BREADCRUMB,
|
|
218
|
+
headerRight: `${this.items.length} running · ${summary}`,
|
|
219
|
+
footer: FOOTER,
|
|
220
|
+
footerHints: FOOTER_HINTS,
|
|
221
|
+
footerRight: this.items.length > 0 ? `${Math.min(this.selected, this.items.length - 1) + 1}/${this.items.length}` : "empty"
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
renderBody(width, height) {
|
|
225
|
+
this.bodyHeight = height;
|
|
226
|
+
const detailFor = (paneWidth) => this.error ? [this.theme.fg("warning", `Unable to refresh workflows: ${this.error}`)] : detailLines(this.current(), paneWidth, height, this.theme);
|
|
227
|
+
if (width < MIN_TWO_PANE_WIDTH) return detailFor(width).slice(0, height);
|
|
228
|
+
const leftWidth = Math.max(MIN_PANE_WIDTH, Math.floor((width - 1) * LIST_PANE_RATIO));
|
|
229
|
+
const rightWidth = Math.max(1, width - leftWidth - 1);
|
|
230
|
+
const leftContent = Math.max(1, leftWidth - PANE_GUTTER);
|
|
231
|
+
const rightContent = Math.max(1, rightWidth - PANE_GUTTER);
|
|
232
|
+
const left = this.rosterLines(leftContent);
|
|
233
|
+
const right = detailFor(rightContent);
|
|
234
|
+
const divider = this.theme.fg("borderMuted", "│");
|
|
235
|
+
return Array.from({ length: height }, (_, index) => {
|
|
236
|
+
const row = `${fit(left[index] ?? "", leftContent)} ${divider} ${fitTerminalLine(right[index] ?? "", rightContent)}`;
|
|
237
|
+
return truncateToWidth(row, width, ELLIPSIS);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
rosterLines(width) {
|
|
241
|
+
const lines = [this.theme.bold(ROSTER_HEADING), ""];
|
|
242
|
+
if (this.items.length === 0) {
|
|
243
|
+
lines.push(this.theme.fg("dim", EMPTY_ROSTER));
|
|
244
|
+
return lines;
|
|
245
|
+
}
|
|
246
|
+
const budget = this.rosterCapacity();
|
|
247
|
+
const active = Math.min(this.selected, this.items.length - 1);
|
|
248
|
+
const start = Math.max(0, Math.min(active - budget + 1, Math.max(0, this.items.length - budget)));
|
|
249
|
+
for (const [offset, item] of this.items.slice(start, start + budget).entries()) {
|
|
250
|
+
const current = start + offset === active;
|
|
251
|
+
const paused = item.executionState === PAUSED_STATE;
|
|
252
|
+
const marker = current ? this.theme.fg("accent", SELECTION_MARKER) : " ";
|
|
253
|
+
const glyph = this.theme.fg(paused ? "warning" : "accent", "●");
|
|
254
|
+
const name = current ? this.theme.bold(item.displayName) : item.displayName;
|
|
255
|
+
const stage = paused ? PAUSED_STATE : currentWorkflowPosition(item.jobs)?.job ?? "starting";
|
|
256
|
+
const heading = `${marker} ${glyph} ${name}`;
|
|
257
|
+
const meta = `${" ".repeat(META_INDENT)}${this.theme.fg(paused ? "warning" : "dim", stage)}`;
|
|
258
|
+
const background = current ? "selectedBg" : "userMessageBg";
|
|
259
|
+
for (const row of [heading, meta]) lines.push(this.theme.bg(background, fit(row, width)));
|
|
260
|
+
}
|
|
261
|
+
return lines;
|
|
262
|
+
}
|
|
263
|
+
/** Overridden: a repaint request is also the cue to re-read the registry. */
|
|
264
|
+
invalidate() {
|
|
265
|
+
this.refresh();
|
|
266
|
+
}
|
|
267
|
+
dispose() {
|
|
268
|
+
if (this.completed) return;
|
|
269
|
+
this.completed = true;
|
|
270
|
+
clearInterval(this.timer);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
//#endregion
|
|
274
|
+
export { WorkflowInspectorComponent };
|
|
275
|
+
|
|
276
|
+
//# sourceMappingURL=workflowInspector.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowInspector.mjs","names":[],"sources":["../../../../src/tui/workflow/workflowInspector.ts"],"sourcesContent":["/**\n * Workflow Space: the `SPC w l` overlay.\n *\n * A doom overlay rather than a hand-rolled frame: the roster owns the left\n * third, the run under the cursor owns the right two thirds, and the chrome is\n * the same frame, breadcrumb and key legend every other doom surface draws.\n *\n * Rendering is pure -- `renderBody(width, height)` reads the last polled\n * snapshot and returns lines with no side effects -- so the surface can be\n * asserted as text without a live terminal.\n */\n\nimport { currentWorkflowPosition, type WorkflowProgressJob } from '@agimon-ai/workflow-mcp';\nimport type { Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\n\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from './doomOverlay';\nimport { fitTerminalLine } from './overlayText';\nimport { humanizeDuration } from './workflowStatusRow';\n\nexport const WORKFLOW_INSPECTOR_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nexport interface WorkflowInspectorSelection {\n runId?: string;\n runKey: string;\n startedAt: string;\n workspace: string;\n}\n\nexport interface WorkflowInspectorItem extends WorkflowInspectorSelection {\n displayName: string;\n executionState?: string;\n jobs: WorkflowProgressJob[];\n key: string;\n output: string[];\n}\n\nexport interface WorkflowInspectorSource {\n list(): Promise<WorkflowInspectorItem[]>;\n output(item: WorkflowInspectorItem): Promise<string[]>;\n}\n\nexport interface WorkflowInspectorOptions {\n initialKey?: string;\n refreshMs?: number;\n}\n\nconst TITLE = 'WORKFLOW SPACE';\nconst BREADCRUMB = 'SPC › w / workflows › l / manage';\nconst ROSTER_HEADING = 'RUNNING · this session';\nconst EMPTY_ROSTER = 'No active workflows';\nconst PROGRESS_HEADING = 'PROGRESS';\nconst OUTPUT_HEADING = 'OUTPUT · newest last';\nconst NO_SELECTION = 'Select a workflow to inspect its jobs and output.';\nconst RUNNING_STATE = 'running';\nconst PAUSED_STATE = 'paused';\n/** Mirrors DoomPi's list-navigation legend without adding a runtime doompi-ui dependency. */\nconst LIST_NAVIGATION_KEYS = '↑↓';\nconst FOOTER = `${LIST_NAVIGATION_KEYS} select · PgUp/PgDn page · enter open · esc close`;\nconst FOOTER_HINTS: readonly (readonly [string, string])[] = [\n [LIST_NAVIGATION_KEYS, 'select'],\n ['PgUp/PgDn', 'page'],\n ['enter', 'open'],\n ['esc', 'close'],\n];\n\nconst DEFAULT_REFRESH_MS = 750;\nconst SELECTION_MARKER = '›';\nconst ELLIPSIS = '…';\n/** pi-tui brackets a truncation ellipsis with this; see `fit`. */\nconst HARD_RESET = '\\x1b[0m';\n/** A third for the roster, two thirds for the run being read. */\nconst LIST_PANE_RATIO = 1 / 3;\n/** One blank column each side of the divider so neither pane touches it. */\nconst PANE_GUTTER = 1;\nconst MIN_PANE_WIDTH = 18;\n/** Below this the roster cannot hold a readable name, so the detail takes the body. */\nconst MIN_TWO_PANE_WIDTH = 48;\n/** Each run occupies a name row and a state row beneath it. */\nconst ROWS_PER_ITEM = 2;\n/** Hanging indent for the state row: clears the marker and the glyph. */\nconst META_INDENT = 4;\n/** Rows the roster heading and its spacer take. */\nconst ROSTER_CHROME_ROWS = 2;\n/** The output tail keeps at least this many rows, or it is not worth a divider. */\nconst MIN_OUTPUT_ROWS = 2;\n\n/** Neither palette name is exported, so take both from the theme's methods. */\ntype ThemeBg = Parameters<Theme['bg']>[0];\ntype ThemeColor = Parameters<Theme['fg']>[0];\n\n/**\n * Truncates and pads to an exact column count.\n *\n * `truncateToWidth` wraps its ellipsis in a hard `\\x1b[0m`, which would strip a\n * row's background for everything after a clipped name. The theme closes its own\n * colours with `\\x1b[39m` and `\\x1b[49m`, so the injected resets are dropped.\n */\nfunction fit(text: string, width: number): string {\n const clipped = truncateToWidth(text, Math.max(0, width), ELLIPSIS).replaceAll(HARD_RESET, '');\n return clipped + ' '.repeat(Math.max(0, width - visibleWidth(clipped)));\n}\n\nfunction rightAligned(left: string, right: string, width: number): string {\n const rightWidth = visibleWidth(right);\n const leftWidth = Math.max(0, width - rightWidth - 1);\n return fit(left, leftWidth) + ' '.repeat(Math.max(1, width - leftWidth - rightWidth)) + fit(right, rightWidth);\n}\n\nfunction jobGlyph(status: string): string {\n return status === 'completed' ? '✓' : status === 'failed' ? '✗' : '●';\n}\n\nfunction jobColour(status: string): ThemeColor {\n return status === 'completed' ? 'success' : status === 'failed' ? 'error' : 'accent';\n}\n\nfunction progressLines(item: WorkflowInspectorItem, width: number, theme: Theme): string[] {\n const lines: string[] = [];\n for (const job of item.jobs) {\n const position = job.total ? ` · ${(job.index ?? 0) + 1}/${job.total}` : '';\n lines.push(\n truncateToWidth(\n theme.fg(jobColour(job.status), `${jobGlyph(job.status)} ${job.name}`) + theme.fg('dim', position),\n width,\n ELLIPSIS,\n ),\n );\n // Only the running job's steps: every finished job's steps would push the\n // live one off the pane, which is the one thing worth watching.\n if (job.status !== RUNNING_STATE) continue;\n for (const step of job.steps) {\n const stepColour: ThemeColor =\n step.status === 'failed' ? 'error' : step.status === RUNNING_STATE ? 'muted' : 'dim';\n lines.push(truncateToWidth(theme.fg(stepColour, ` ${jobGlyph(step.status)} ${step.name}`), width, ELLIPSIS));\n }\n }\n return lines;\n}\n\nfunction detailLines(item: WorkflowInspectorItem | undefined, width: number, height: number, theme: Theme): string[] {\n if (!item) return [theme.fg('dim', NO_SELECTION)];\n\n const position = currentWorkflowPosition(item.jobs);\n const elapsed = humanizeDuration(Math.max(0, Date.now() - Date.parse(item.startedAt)));\n const executionState = item.executionState ?? RUNNING_STATE;\n const stateColour: ThemeColor = executionState === PAUSED_STATE ? 'warning' : 'dim';\n const lines = [\n rightAligned(\n `${theme.fg('accent', '●')} ${theme.bold(item.displayName)}`,\n theme.fg(stateColour, executionState),\n width,\n ),\n truncateToWidth(theme.fg('dim', `${item.runKey} · ${item.workspace} · ${elapsed}`), width, ELLIPSIS),\n position\n ? truncateToWidth(\n `${theme.fg('accent', position.job)}${position.step ? theme.fg('muted', ` · ${position.step}`) : ''}`,\n width,\n ELLIPSIS,\n )\n : theme.fg('dim', 'starting'),\n '',\n theme.fg('dim', PROGRESS_HEADING),\n ];\n\n // The output tail claims its rows first: progress is a list that can be paged\n // to by opening the run, while the tail is the only live signal on screen.\n const reservedOutput = item.output.length > 0 ? MIN_OUTPUT_ROWS + 1 : 0;\n const progressRoom = Math.max(0, height - lines.length - reservedOutput);\n lines.push(...progressLines(item, width, theme).slice(0, progressRoom));\n\n if (item.output.length > 0 && height - lines.length >= MIN_OUTPUT_ROWS) {\n lines.push('', theme.fg('dim', OUTPUT_HEADING));\n const outputRoom = Math.max(0, height - lines.length);\n // Captured output carries the run's own colour, so it is fitted by the\n // escape-preserving path: `fit` below drops hard resets, and a capture that\n // lost its reset bleeds its colour through the divider and down the pane.\n lines.push(...item.output.slice(-outputRoom).map((line) => fitTerminalLine(line, width)));\n }\n\n return lines.slice(0, height);\n}\n\nexport class WorkflowInspectorComponent extends DoomOverlay {\n private bodyHeight = 8;\n private completed = false;\n private error: string | undefined;\n private items: WorkflowInspectorItem[] = [];\n private refreshActive = false;\n private selected = 0;\n private selectedKey: string | undefined;\n private readonly timer: ReturnType<typeof setInterval>;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly source: WorkflowInspectorSource,\n private readonly done: (selection: WorkflowInspectorSelection | undefined) => void,\n options: WorkflowInspectorOptions = {},\n ) {\n super(tui, theme);\n this.selectedKey = options.initialKey;\n void this.refresh();\n this.timer = setInterval(() => void this.refresh(), options.refreshMs ?? DEFAULT_REFRESH_MS);\n this.timer.unref?.();\n }\n\n private async refresh(): Promise<void> {\n if (this.refreshActive || this.completed) return;\n this.refreshActive = true;\n try {\n const previousKey = this.items[this.selected]?.key ?? this.selectedKey;\n const items = await this.source.list();\n if (this.completed) return;\n const preserved = previousKey ? items.findIndex((item) => item.key === previousKey) : -1;\n this.items = items;\n this.selected = preserved >= 0 ? preserved : Math.min(this.selected, Math.max(0, items.length - 1));\n this.selectedKey = items[this.selected]?.key;\n const selected = items[this.selected];\n if (selected) selected.output = await this.source.output(selected);\n this.error = undefined;\n if (!this.completed) this.tui.requestRender();\n } catch (cause) {\n this.error = cause instanceof Error ? cause.message : String(cause);\n if (!this.completed) this.tui.requestRender();\n } finally {\n this.refreshActive = false;\n }\n }\n\n private move(delta: number): void {\n if (this.items.length === 0) return;\n this.selected = Math.max(0, Math.min(this.items.length - 1, this.selected + delta));\n this.selectedKey = this.items[this.selected]?.key;\n void this.refreshSelectedOutput();\n this.tui.requestRender();\n }\n\n private async refreshSelectedOutput(): Promise<void> {\n const selected = this.items[this.selected];\n if (!selected || this.completed) return;\n try {\n selected.output = await this.source.output(selected);\n this.error = undefined;\n } catch (cause) {\n this.error = cause instanceof Error ? cause.message : String(cause);\n }\n if (!this.completed && selected.key === this.selectedKey) this.tui.requestRender();\n }\n\n private finish(selection: WorkflowInspectorSelection | undefined): void {\n if (this.completed) return;\n this.completed = true;\n clearInterval(this.timer);\n this.done(selection);\n }\n\n close(): void {\n this.finish(undefined);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.finish(undefined);\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') {\n this.move(-1);\n return;\n }\n if (matchesKey(data, 'down') || data === 'j') {\n this.move(1);\n return;\n }\n if (matchesKey(data, 'pageUp')) {\n this.move(-this.rosterCapacity());\n return;\n }\n if (matchesKey(data, 'pageDown')) {\n this.move(this.rosterCapacity());\n return;\n }\n if (matchesKey(data, 'enter')) {\n const selected = this.items[this.selected];\n if (selected) {\n this.finish({\n ...(selected.runId ? { runId: selected.runId } : {}),\n runKey: selected.runKey,\n startedAt: selected.startedAt,\n workspace: selected.workspace,\n });\n }\n }\n }\n\n /** Runs the roster can draw, which is also what a page key moves by. */\n private rosterCapacity(): number {\n return Math.max(1, Math.floor((this.bodyHeight - ROSTER_CHROME_ROWS) / ROWS_PER_ITEM));\n }\n\n private current(): WorkflowInspectorItem | undefined {\n if (this.items.length === 0) return undefined;\n return this.items[Math.min(this.selected, this.items.length - 1)];\n }\n\n protected getChrome(): DoomOverlayChrome {\n const selected = this.current();\n const summary = selected\n ? `${selected.displayName} · ${selected.executionState ?? RUNNING_STATE}`\n : 'no active workflows';\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: BREADCRUMB,\n headerRight: `${this.items.length} running · ${summary}`,\n footer: FOOTER,\n footerHints: FOOTER_HINTS,\n footerRight:\n this.items.length > 0 ? `${Math.min(this.selected, this.items.length - 1) + 1}/${this.items.length}` : 'empty',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n this.bodyHeight = height;\n const detailFor = (paneWidth: number): string[] =>\n this.error\n ? [this.theme.fg('warning', `Unable to refresh workflows: ${this.error}`)]\n : detailLines(this.current(), paneWidth, height, this.theme);\n\n // Too narrow for two useful columns: the detail takes the whole body rather\n // than shredding both panes into stubs.\n if (width < MIN_TWO_PANE_WIDTH) return detailFor(width).slice(0, height);\n\n const leftWidth = Math.max(MIN_PANE_WIDTH, Math.floor((width - 1) * LIST_PANE_RATIO));\n const rightWidth = Math.max(1, width - leftWidth - 1);\n const leftContent = Math.max(1, leftWidth - PANE_GUTTER);\n const rightContent = Math.max(1, rightWidth - PANE_GUTTER);\n\n const left = this.rosterLines(leftContent);\n const right = detailFor(rightContent);\n const divider = this.theme.fg('borderMuted', '│');\n return Array.from({ length: height }, (_, index) => {\n // The roster keeps `fit`, whose reset stripping protects its row\n // backgrounds; the detail pane has none and may carry captured colour.\n const row = `${fit(left[index] ?? '', leftContent)} ${divider} ${fitTerminalLine(\n right[index] ?? '',\n rightContent,\n )}`;\n return truncateToWidth(row, width, ELLIPSIS);\n });\n }\n\n private rosterLines(width: number): string[] {\n const lines = [this.theme.bold(ROSTER_HEADING), ''];\n if (this.items.length === 0) {\n lines.push(this.theme.fg('dim', EMPTY_ROSTER));\n return lines;\n }\n\n // Two rows per run: at a third of the width a single row left the name with\n // a handful of columns once the stage took its share.\n const budget = this.rosterCapacity();\n const active = Math.min(this.selected, this.items.length - 1);\n const start = Math.max(0, Math.min(active - budget + 1, Math.max(0, this.items.length - budget)));\n for (const [offset, item] of this.items.slice(start, start + budget).entries()) {\n const current = start + offset === active;\n const paused = item.executionState === PAUSED_STATE;\n const marker = current ? this.theme.fg('accent', SELECTION_MARKER) : ' ';\n const glyph = this.theme.fg(paused ? 'warning' : 'accent', '●');\n const name = current ? this.theme.bold(item.displayName) : item.displayName;\n const stage = paused ? PAUSED_STATE : (currentWorkflowPosition(item.jobs)?.job ?? 'starting');\n const heading = `${marker} ${glyph} ${name}`;\n const meta = `${' '.repeat(META_INDENT)}${this.theme.fg(paused ? 'warning' : 'dim', stage)}`;\n const background: ThemeBg = current ? 'selectedBg' : 'userMessageBg';\n for (const row of [heading, meta]) lines.push(this.theme.bg(background, fit(row, width)));\n }\n return lines;\n }\n\n /** Overridden: a repaint request is also the cue to re-read the registry. */\n invalidate(): void {\n void this.refresh();\n }\n\n dispose(): void {\n if (this.completed) return;\n this.completed = true;\n clearInterval(this.timer);\n }\n}\n"],"mappings":"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"}
|