@agimon-ai/doompi-workflow 0.0.1-alpha.73 → 0.0.1-alpha.76
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/LICENSE +4 -2
- package/README.md +8 -5
- package/package.json +19 -18
- package/src/types/webWorkflowTerminal.ts +7 -6
- package/dist/_virtual/_rolldown/runtime.cjs +0 -1
- package/dist/api-contracts.cjs +0 -1
- package/dist/api-contracts.d.cts +0 -2
- package/dist/api-contracts.d.mts +0 -2
- package/dist/api-contracts.mjs +0 -1
- package/dist/extensions/pi.cjs +0 -2
- package/dist/extensions/pi.cjs.map +0 -1
- package/dist/extensions/pi.d.cts +0 -6
- package/dist/extensions/pi.d.cts.map +0 -1
- package/dist/extensions/pi.d.mts +0 -6
- package/dist/extensions/pi.d.mts.map +0 -1
- package/dist/extensions/pi.mjs +0 -2
- package/dist/extensions/pi.mjs.map +0 -1
- package/dist/extensions/server.cjs +0 -2
- package/dist/extensions/server.cjs.map +0 -1
- package/dist/extensions/server.d.cts +0 -5
- package/dist/extensions/server.d.cts.map +0 -1
- package/dist/extensions/server.d.mts +0 -5
- package/dist/extensions/server.d.mts.map +0 -1
- package/dist/extensions/server.mjs +0 -2
- package/dist/extensions/server.mjs.map +0 -1
- package/dist/index.cjs +0 -1
- package/dist/index.d.cts +0 -2
- package/dist/index.d.mts +0 -2
- package/dist/index.mjs +0 -1
- package/dist/src/constants/workflow.cjs +0 -2
- package/dist/src/constants/workflow.cjs.map +0 -1
- package/dist/src/constants/workflow.mjs +0 -2
- package/dist/src/constants/workflow.mjs.map +0 -1
- package/dist/src/controllers/serverRuntime.cjs +0 -2
- package/dist/src/controllers/serverRuntime.cjs.map +0 -1
- package/dist/src/controllers/serverRuntime.mjs +0 -2
- package/dist/src/controllers/serverRuntime.mjs.map +0 -1
- package/dist/src/controllers/workflowCatalogChannel.cjs +0 -2
- package/dist/src/controllers/workflowCatalogChannel.cjs.map +0 -1
- package/dist/src/controllers/workflowCatalogChannel.mjs +0 -2
- package/dist/src/controllers/workflowCatalogChannel.mjs.map +0 -1
- package/dist/src/controllers/workflowHubApi.cjs +0 -2
- package/dist/src/controllers/workflowHubApi.cjs.map +0 -1
- package/dist/src/controllers/workflowHubApi.mjs +0 -2
- package/dist/src/controllers/workflowHubApi.mjs.map +0 -1
- package/dist/src/controllers/workflowsHubChannel.cjs +0 -2
- package/dist/src/controllers/workflowsHubChannel.cjs.map +0 -1
- package/dist/src/controllers/workflowsHubChannel.mjs +0 -2
- package/dist/src/controllers/workflowsHubChannel.mjs.map +0 -1
- package/dist/src/schemas/apiContracts.cjs +0 -2
- package/dist/src/schemas/apiContracts.cjs.map +0 -1
- package/dist/src/schemas/apiContracts.d.cts +0 -53
- package/dist/src/schemas/apiContracts.d.cts.map +0 -1
- package/dist/src/schemas/apiContracts.d.mts +0 -53
- package/dist/src/schemas/apiContracts.d.mts.map +0 -1
- package/dist/src/schemas/apiContracts.mjs +0 -2
- package/dist/src/schemas/apiContracts.mjs.map +0 -1
- package/dist/src/schemas/workflowPi.cjs +0 -2
- package/dist/src/schemas/workflowPi.cjs.map +0 -1
- package/dist/src/schemas/workflowPi.d.cts +0 -1
- package/dist/src/schemas/workflowPi.d.mts +0 -1
- package/dist/src/schemas/workflowPi.mjs +0 -2
- package/dist/src/schemas/workflowPi.mjs.map +0 -1
- package/dist/src/services/backgroundWork/index.cjs +0 -2
- package/dist/src/services/backgroundWork/index.cjs.map +0 -1
- package/dist/src/services/backgroundWork/index.mjs +0 -2
- package/dist/src/services/backgroundWork/index.mjs.map +0 -1
- package/dist/src/services/piToolBridge/index.cjs +0 -11
- package/dist/src/services/piToolBridge/index.cjs.map +0 -1
- package/dist/src/services/piToolBridge/index.mjs +0 -11
- package/dist/src/services/piToolBridge/index.mjs.map +0 -1
- package/dist/src/services/webWorkflowCatalog/index.cjs +0 -2
- package/dist/src/services/webWorkflowCatalog/index.cjs.map +0 -1
- package/dist/src/services/webWorkflowCatalog/index.mjs +0 -2
- package/dist/src/services/webWorkflowCatalog/index.mjs.map +0 -1
- package/dist/src/services/workflowCatalogDeps/index.cjs +0 -2
- package/dist/src/services/workflowCatalogDeps/index.cjs.map +0 -1
- package/dist/src/services/workflowCatalogDeps/index.mjs +0 -2
- package/dist/src/services/workflowCatalogDeps/index.mjs.map +0 -1
- package/dist/src/services/workflowExecution/index.cjs +0 -2
- package/dist/src/services/workflowExecution/index.cjs.map +0 -1
- package/dist/src/services/workflowExecution/index.d.cts +0 -6
- package/dist/src/services/workflowExecution/index.d.mts +0 -6
- package/dist/src/services/workflowExecution/index.mjs +0 -2
- package/dist/src/services/workflowExecution/index.mjs.map +0 -1
- package/dist/src/services/workflowFence/index.cjs +0 -2
- package/dist/src/services/workflowFence/index.cjs.map +0 -1
- package/dist/src/services/workflowFence/index.mjs +0 -2
- package/dist/src/services/workflowFence/index.mjs.map +0 -1
- package/dist/src/services/workflowLaunchCommand/index.cjs +0 -2
- package/dist/src/services/workflowLaunchCommand/index.cjs.map +0 -1
- package/dist/src/services/workflowLaunchCommand/index.d.cts +0 -52
- package/dist/src/services/workflowLaunchCommand/index.d.cts.map +0 -1
- package/dist/src/services/workflowLaunchCommand/index.d.mts +0 -52
- package/dist/src/services/workflowLaunchCommand/index.d.mts.map +0 -1
- package/dist/src/services/workflowLaunchCommand/index.mjs +0 -2
- package/dist/src/services/workflowLaunchCommand/index.mjs.map +0 -1
- package/dist/src/services/workflowLauncher/index.cjs +0 -2
- package/dist/src/services/workflowLauncher/index.cjs.map +0 -1
- package/dist/src/services/workflowLauncher/index.mjs +0 -2
- package/dist/src/services/workflowLauncher/index.mjs.map +0 -1
- package/dist/src/services/workflowNarration/index.cjs +0 -2
- package/dist/src/services/workflowNarration/index.cjs.map +0 -1
- package/dist/src/services/workflowNarration/index.mjs +0 -2
- package/dist/src/services/workflowNarration/index.mjs.map +0 -1
- package/dist/src/services/workflowResource/index.cjs +0 -2
- package/dist/src/services/workflowResource/index.cjs.map +0 -1
- package/dist/src/services/workflowResource/index.mjs +0 -2
- package/dist/src/services/workflowResource/index.mjs.map +0 -1
- package/dist/src/services/workflowRuns/index.cjs +0 -3
- package/dist/src/services/workflowRuns/index.cjs.map +0 -1
- package/dist/src/services/workflowRuns/index.mjs +0 -3
- package/dist/src/services/workflowRuns/index.mjs.map +0 -1
- package/dist/src/services/workflowTerminal/index.cjs +0 -2
- package/dist/src/services/workflowTerminal/index.cjs.map +0 -1
- package/dist/src/services/workflowTerminal/index.mjs +0 -2
- package/dist/src/services/workflowTerminal/index.mjs.map +0 -1
- package/dist/src/services/workflowWatcher/index.cjs +0 -2
- package/dist/src/services/workflowWatcher/index.cjs.map +0 -1
- package/dist/src/services/workflowWatcher/index.mjs +0 -2
- package/dist/src/services/workflowWatcher/index.mjs.map +0 -1
- package/dist/src/tools/workflowTools.cjs +0 -5
- package/dist/src/tools/workflowTools.cjs.map +0 -1
- package/dist/src/tools/workflowTools.mjs +0 -5
- package/dist/src/tools/workflowTools.mjs.map +0 -1
- package/dist/src/tui/leader.cjs +0 -2
- package/dist/src/tui/leader.cjs.map +0 -1
- package/dist/src/tui/leader.mjs +0 -2
- package/dist/src/tui/leader.mjs.map +0 -1
- package/dist/src/tui/workflow/doomOverlay.cjs +0 -2
- package/dist/src/tui/workflow/doomOverlay.cjs.map +0 -1
- package/dist/src/tui/workflow/doomOverlay.mjs +0 -2
- package/dist/src/tui/workflow/doomOverlay.mjs.map +0 -1
- package/dist/src/tui/workflow/overlayText.cjs +0 -2
- package/dist/src/tui/workflow/overlayText.cjs.map +0 -1
- package/dist/src/tui/workflow/overlayText.mjs +0 -2
- package/dist/src/tui/workflow/overlayText.mjs.map +0 -1
- package/dist/src/tui/workflow/rendering.cjs +0 -2
- package/dist/src/tui/workflow/rendering.cjs.map +0 -1
- package/dist/src/tui/workflow/rendering.mjs +0 -2
- package/dist/src/tui/workflow/rendering.mjs.map +0 -1
- package/dist/src/tui/workflow/workflowCatalog.cjs +0 -2
- package/dist/src/tui/workflow/workflowCatalog.cjs.map +0 -1
- package/dist/src/tui/workflow/workflowCatalog.mjs +0 -2
- package/dist/src/tui/workflow/workflowCatalog.mjs.map +0 -1
- package/dist/src/tui/workflow/workflowFinishedMessage.cjs +0 -3
- package/dist/src/tui/workflow/workflowFinishedMessage.cjs.map +0 -1
- package/dist/src/tui/workflow/workflowFinishedMessage.mjs +0 -3
- package/dist/src/tui/workflow/workflowFinishedMessage.mjs.map +0 -1
- package/dist/src/tui/workflow/workflowInspector.cjs +0 -2
- package/dist/src/tui/workflow/workflowInspector.cjs.map +0 -1
- package/dist/src/tui/workflow/workflowInspector.mjs +0 -2
- package/dist/src/tui/workflow/workflowInspector.mjs.map +0 -1
- package/dist/src/tui/workflow/workflowOverlay.cjs +0 -2
- package/dist/src/tui/workflow/workflowOverlay.cjs.map +0 -1
- package/dist/src/tui/workflow/workflowOverlay.mjs +0 -2
- package/dist/src/tui/workflow/workflowOverlay.mjs.map +0 -1
- package/dist/src/tui/workflow/workflowPicker.cjs +0 -2
- package/dist/src/tui/workflow/workflowPicker.cjs.map +0 -1
- package/dist/src/tui/workflow/workflowPicker.mjs +0 -2
- package/dist/src/tui/workflow/workflowPicker.mjs.map +0 -1
- package/dist/src/tui/workflow/workflowProgressOverlay.cjs +0 -2
- package/dist/src/tui/workflow/workflowProgressOverlay.cjs.map +0 -1
- package/dist/src/tui/workflow/workflowProgressOverlay.mjs +0 -2
- package/dist/src/tui/workflow/workflowProgressOverlay.mjs.map +0 -1
- package/dist/src/tui/workflow/workflowPrompt.cjs +0 -2
- package/dist/src/tui/workflow/workflowPrompt.cjs.map +0 -1
- package/dist/src/tui/workflow/workflowPrompt.mjs +0 -2
- package/dist/src/tui/workflow/workflowPrompt.mjs.map +0 -1
- package/dist/src/tui/workflow/workflowRunPanel.cjs +0 -2
- package/dist/src/tui/workflow/workflowRunPanel.cjs.map +0 -1
- package/dist/src/tui/workflow/workflowRunPanel.mjs +0 -2
- package/dist/src/tui/workflow/workflowRunPanel.mjs.map +0 -1
- package/dist/src/tui/workflow/workflowStatusRow.cjs +0 -2
- package/dist/src/tui/workflow/workflowStatusRow.cjs.map +0 -1
- package/dist/src/tui/workflow/workflowStatusRow.mjs +0 -2
- package/dist/src/tui/workflow/workflowStatusRow.mjs.map +0 -1
- package/dist/src/tui/workflow/workflowStepMessage.cjs +0 -2
- package/dist/src/tui/workflow/workflowStepMessage.cjs.map +0 -1
- package/dist/src/tui/workflow/workflowStepMessage.mjs +0 -2
- package/dist/src/tui/workflow/workflowStepMessage.mjs.map +0 -1
- package/dist/src/tui/workflow/workflowToolRender.cjs +0 -7
- package/dist/src/tui/workflow/workflowToolRender.cjs.map +0 -1
- package/dist/src/tui/workflow/workflowToolRender.mjs +0 -7
- package/dist/src/tui/workflow/workflowToolRender.mjs.map +0 -1
- package/dist/src/tui/workflowRuntime.cjs +0 -9
- package/dist/src/tui/workflowRuntime.cjs.map +0 -1
- package/dist/src/tui/workflowRuntime.d.cts +0 -33
- package/dist/src/tui/workflowRuntime.d.cts.map +0 -1
- package/dist/src/tui/workflowRuntime.d.mts +0 -33
- package/dist/src/tui/workflowRuntime.d.mts.map +0 -1
- package/dist/src/tui/workflowRuntime.mjs +0 -9
- package/dist/src/tui/workflowRuntime.mjs.map +0 -1
- package/dist/src/types/webWorkflowTerminal.cjs +0 -2
- package/dist/src/types/webWorkflowTerminal.cjs.map +0 -1
- package/dist/src/types/webWorkflowTerminal.mjs +0 -2
- package/dist/src/types/webWorkflowTerminal.mjs.map +0 -1
- package/dist/src/types/webWorkflows.cjs +0 -2
- package/dist/src/types/webWorkflows.cjs.map +0 -1
- package/dist/src/types/webWorkflows.mjs +0 -2
- package/dist/src/types/webWorkflows.mjs.map +0 -1
- package/src/extensions/web.ts +0 -80
- package/src/web/api/terminalApi.ts +0 -208
- package/src/web/components/ArtifactsPane.tsx +0 -451
- package/src/web/components/LaunchWorkflowDialog.tsx +0 -259
- package/src/web/components/StepTerminalPanel.tsx +0 -243
- package/src/web/components/WorkflowCatalogDrawer.tsx +0 -229
- package/src/web/components/WorkflowToolMessage.tsx +0 -55
- package/src/web/components/WorkflowsActivitySection.tsx +0 -155
- package/src/web/components/WorkflowsPanel.tsx +0 -750
- package/src/web/lib/launchLine.ts +0 -66
- package/src/web/lib/runDuration.ts +0 -15
- package/src/web/lib/workflowActivity.ts +0 -102
- package/src/web/lib/workflowToolRender.ts +0 -585
- package/src/web/stores/catalogStore.ts +0 -123
- package/src/web/stores/workflowsStore.ts +0 -91
- package/src/web/tsconfig.json +0 -22
- /package/{src/web/LICENSE → LICENSE.web} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowPicker.cjs","names":["DOOM_FULLSCREEN_UI_OPTIONS","matchesQuery","DoomOverlay","matchesKey","isControlInput","DOOM_OVERLAY_ACCENT","fit","pathTail"],"sources":["../../../../src/tui/workflow/workflowPicker.ts"],"sourcesContent":["/**\n * Scrollable, filterable list picker for every long list this extension shows.\n *\n * Replaces a bare `ui.select`, which wrapped each `name · detail` pair across\n * two or three terminal rows and left no way to reach a row other than scrolling\n * past all of them. This is a doom overlay instead: one row per item, a windowed\n * list that scrolls with the selection, and a filter field that narrows as you\n * type.\n *\n * Generic over the value it hands back. `SPC w c` picks a failed run to recover\n * through it; browsing and launching workflows moved to `workflowCatalog.ts`,\n * which needs the detail on screen beside the list rather than behind a pick.\n *\n * Rendering is pure, so the surface can be asserted as text without a terminal.\n */\n\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey } 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 { CURSOR_BLOCK, fit, isControlInput, matchesQuery, pathTail, SELECTION_MARKER } from './overlayText';\n\nexport const WORKFLOW_PICKER_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nconst FILTER_LABEL = 'FILTER';\nconst EMPTY_NAME = '(unnamed)';\nconst KEY_CLEAR = '\\x15';\n/** Rows the filter field and its trailing spacer take from the body. */\nconst FILTER_ROWS = 3;\n/** Columns the marker and its trailing space take from a row. */\nconst MARKER_COLUMNS = 2;\n/** Blank columns between the name column and the detail column. */\nconst COLUMN_GAP = 2;\nconst MIN_NAME_WIDTH = 16;\nconst NAME_RATIO = 0.45;\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\nconst DIM: ThemeColor = 'dim';\nconst ACCENT: ThemeColor = 'accent';\nconst SELECTED_BG: ThemeBg = 'selectedBg';\nconst ROW_BG: ThemeBg = 'userMessageBg';\n\nexport interface PickerRow<T> {\n /** Stable identity, used only to keep rows distinct. */\n key: string;\n /** Left column. */\n name: string;\n /** Right column, trimmed from the front so its tail survives. */\n detail: string;\n /** Extra text the filter should match but the row does not show. */\n search?: string;\n value: T;\n}\n\nexport interface WorkflowPickerConfig {\n title: string;\n breadcrumb: string;\n /** Plural noun for the header count, e.g. `workflows`. */\n unit: string;\n /** Verb on the enter key cap, e.g. `launch`. */\n action: string;\n filterPlaceholder: string;\n /** Shown when the source list itself is empty, as opposed to over-filtered. */\n emptyMessage: string;\n}\n\nexport function filterPickerRows<T>(rows: readonly PickerRow<T>[], query: string): PickerRow<T>[] {\n if (!query.trim()) return [...rows];\n return rows.filter((row) => matchesQuery([row.name, row.detail, row.search ?? ''].join(' '), query));\n}\n\nexport class WorkflowPickerComponent<T> extends DoomOverlay {\n private query = '';\n private selected = 0;\n private bodyHeight = 1;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly config: WorkflowPickerConfig,\n private readonly rows: readonly PickerRow<T>[],\n private readonly done: (value: T | undefined) => void,\n ) {\n super(tui, theme);\n }\n\n private matches(): PickerRow<T>[] {\n return filterPickerRows(this.rows, this.query);\n }\n\n /** Rows the list can draw, once the filter field has taken its share. */\n private capacity(): number {\n return Math.max(1, this.bodyHeight - FILTER_ROWS);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.done(undefined);\n return;\n }\n const matches = this.matches();\n if (matchesKey(data, 'enter')) {\n const row = matches[Math.min(this.selected, matches.length - 1)];\n if (row) this.done(row.value);\n return;\n }\n\n if (matchesKey(data, 'up') || matchesKey(data, 'ctrl+p')) this.move(-1, matches.length);\n else if (matchesKey(data, 'down') || matchesKey(data, 'ctrl+n')) this.move(1, matches.length);\n else if (matchesKey(data, 'pageUp')) this.move(-this.capacity(), matches.length);\n else if (matchesKey(data, 'pageDown')) this.move(this.capacity(), matches.length);\n else if (matchesKey(data, 'backspace')) this.setQuery(this.query.slice(0, -1));\n else if (data === KEY_CLEAR) this.setQuery('');\n else if (!isControlInput(data)) this.setQuery(this.query + data);\n else return;\n\n this.tui.requestRender();\n }\n\n /** Editing the filter resets the cursor: the previous index named another row. */\n private setQuery(query: string): void {\n if (query === this.query) return;\n this.query = query;\n this.selected = 0;\n }\n\n private move(delta: number, count: number): void {\n if (count === 0) return;\n this.selected = Math.max(0, Math.min(count - 1, this.selected + delta));\n }\n\n protected getChrome(): DoomOverlayChrome {\n const matches = this.matches();\n const scope = this.query ? `${matches.length} of ${this.rows.length}` : `${this.rows.length}`;\n return {\n title: this.config.title,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: this.config.breadcrumb,\n headerRight: `${scope} ${this.config.unit}`,\n footer: `↑↓ select · enter ${this.config.action} · type to filter · ctrl+u clear · esc cancel`,\n footerHints: [\n ['↑↓', 'select'],\n ['enter', this.config.action],\n ['type', 'filter'],\n ['ctrl+u', 'clear'],\n ['esc', 'cancel'],\n ],\n footerRight:\n matches.length > 0 ? `${Math.min(this.selected, matches.length - 1) + 1}/${matches.length}` : 'no match',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n this.bodyHeight = height;\n return [...this.filterField(width), ...this.listLines(width)].slice(0, height);\n }\n\n private filterField(width: number): string[] {\n const value = this.query\n ? `${this.query}${CURSOR_BLOCK}`\n : `${CURSOR_BLOCK}${this.theme.fg(DIM, this.config.filterPlaceholder)}`;\n return [this.theme.fg(DIM, FILTER_LABEL), this.theme.bg(SELECTED_BG, fit(` ${value}`, width)), ''];\n }\n\n private listLines(width: number): string[] {\n if (this.rows.length === 0) return [this.theme.fg(DIM, this.config.emptyMessage)];\n const matches = this.matches();\n if (matches.length === 0) return [this.theme.fg(DIM, `No ${this.config.unit} match \"${this.query}\".`)];\n\n const capacity = this.capacity();\n const active = Math.min(this.selected, matches.length - 1);\n // The window follows the cursor rather than the other way round, so a filter\n // that shrinks the list can never strand the selection off-screen.\n const start = Math.max(0, Math.min(active - capacity + 1, Math.max(0, matches.length - capacity)));\n\n const nameWidth = Math.max(MIN_NAME_WIDTH, Math.floor((width - MARKER_COLUMNS - COLUMN_GAP) * NAME_RATIO));\n const detailWidth = Math.max(0, width - MARKER_COLUMNS - COLUMN_GAP - nameWidth);\n return matches.slice(start, start + capacity).map((row, offset) => {\n const current = start + offset === active;\n const marker = current ? this.theme.fg(ACCENT, SELECTION_MARKER) : ' ';\n const name = row.name || EMPTY_NAME;\n const label = current ? this.theme.bold(fit(name, nameWidth)) : fit(name, nameWidth);\n const detail = this.theme.fg(DIM, fit(pathTail(row.detail, detailWidth), detailWidth));\n const background: ThemeBg = current ? SELECTED_BG : ROW_BG;\n return this.theme.bg(background, fit(`${marker} ${label}${' '.repeat(COLUMN_GAP)}${detail}`, width));\n });\n }\n}\n\nexport async function openWorkflowPickerOverlay<T>(\n ctx: ExtensionContext,\n config: WorkflowPickerConfig,\n rows: readonly PickerRow<T>[],\n): Promise<T | undefined> {\n return ctx.ui.custom<T | undefined>(\n (tui, theme, _keybindings, done) => new WorkflowPickerComponent(tui, theme, config, rows, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{DOOM_FULLSCREEN_UI_OPTIONS as e,DOOM_OVERLAY_ACCENT as t,DoomOverlay as n}from"./doomOverlay.mjs";import{fit as r,isControlInput as i,matchesQuery as a,pathTail as o}from"./overlayText.mjs";import{matchesKey as s}from"@earendil-works/pi-tui";e.overlayOptions;const c=`selectedBg`;function l(e,t){return t.trim()?e.filter(e=>a([e.name,e.detail,e.search??``].join(` `),t)):[...e]}var u=class extends n{config;rows;done;query=``;selected=0;bodyHeight=1;constructor(e,t,n,r,i){super(e,t),this.config=n,this.rows=r,this.done=i}matches(){return l(this.rows,this.query)}capacity(){return Math.max(1,this.bodyHeight-3)}handleInput(e){if(s(e,`escape`)||s(e,`ctrl+c`)){this.done(void 0);return}let t=this.matches();if(s(e,`enter`)){let e=t[Math.min(this.selected,t.length-1)];e&&this.done(e.value);return}if(s(e,`up`)||s(e,`ctrl+p`))this.move(-1,t.length);else if(s(e,`down`)||s(e,`ctrl+n`))this.move(1,t.length);else if(s(e,`pageUp`))this.move(-this.capacity(),t.length);else if(s(e,`pageDown`))this.move(this.capacity(),t.length);else if(s(e,`backspace`))this.setQuery(this.query.slice(0,-1));else if(e===``)this.setQuery(``);else if(!i(e))this.setQuery(this.query+e);else return;this.tui.requestRender()}setQuery(e){e!==this.query&&(this.query=e,this.selected=0)}move(e,t){t!==0&&(this.selected=Math.max(0,Math.min(t-1,this.selected+e)))}getChrome(){let e=this.matches(),n=this.query?`${e.length} of ${this.rows.length}`:`${this.rows.length}`;return{title:this.config.title,accent:t,breadcrumb:this.config.breadcrumb,headerRight:`${n} ${this.config.unit}`,footer:`↑↓ select · enter ${this.config.action} · type to filter · ctrl+u clear · esc cancel`,footerHints:[[`↑↓`,`select`],[`enter`,this.config.action],[`type`,`filter`],[`ctrl+u`,`clear`],[`esc`,`cancel`]],footerRight:e.length>0?`${Math.min(this.selected,e.length-1)+1}/${e.length}`:`no match`}}renderBody(e,t){return this.bodyHeight=t,[...this.filterField(e),...this.listLines(e)].slice(0,t)}filterField(e){let t=this.query?`${this.query}█`:`█${this.theme.fg(`dim`,this.config.filterPlaceholder)}`;return[this.theme.fg(`dim`,`FILTER`),this.theme.bg(c,r(` ${t}`,e)),``]}listLines(e){if(this.rows.length===0)return[this.theme.fg(`dim`,this.config.emptyMessage)];let t=this.matches();if(t.length===0)return[this.theme.fg(`dim`,`No ${this.config.unit} match "${this.query}".`)];let n=this.capacity(),i=Math.min(this.selected,t.length-1),a=Math.max(0,Math.min(i-n+1,Math.max(0,t.length-n))),s=Math.max(16,Math.floor((e-2-2)*.45)),l=Math.max(0,e-2-2-s);return t.slice(a,a+n).map((t,n)=>{let u=a+n===i,d=u?this.theme.fg(`accent`,`›`):` `,f=t.name||`(unnamed)`,p=u?this.theme.bold(r(f,s)):r(f,s),m=this.theme.fg(`dim`,r(o(t.detail,l),l)),h=u?c:`userMessageBg`;return this.theme.bg(h,r(`${d} ${p}${` `.repeat(2)}${m}`,e))})}};async function d(t,n,r){return t.ui.custom((e,t,i,a)=>new u(e,t,n,r,a),e)}export{u as WorkflowPickerComponent,l as filterPickerRows,d as openWorkflowPickerOverlay};
|
|
2
|
-
//# sourceMappingURL=workflowPicker.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowPicker.mjs","names":[],"sources":["../../../../src/tui/workflow/workflowPicker.ts"],"sourcesContent":["/**\n * Scrollable, filterable list picker for every long list this extension shows.\n *\n * Replaces a bare `ui.select`, which wrapped each `name · detail` pair across\n * two or three terminal rows and left no way to reach a row other than scrolling\n * past all of them. This is a doom overlay instead: one row per item, a windowed\n * list that scrolls with the selection, and a filter field that narrows as you\n * type.\n *\n * Generic over the value it hands back. `SPC w c` picks a failed run to recover\n * through it; browsing and launching workflows moved to `workflowCatalog.ts`,\n * which needs the detail on screen beside the list rather than behind a pick.\n *\n * Rendering is pure, so the surface can be asserted as text without a terminal.\n */\n\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey } 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 { CURSOR_BLOCK, fit, isControlInput, matchesQuery, pathTail, SELECTION_MARKER } from './overlayText';\n\nexport const WORKFLOW_PICKER_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nconst FILTER_LABEL = 'FILTER';\nconst EMPTY_NAME = '(unnamed)';\nconst KEY_CLEAR = '\\x15';\n/** Rows the filter field and its trailing spacer take from the body. */\nconst FILTER_ROWS = 3;\n/** Columns the marker and its trailing space take from a row. */\nconst MARKER_COLUMNS = 2;\n/** Blank columns between the name column and the detail column. */\nconst COLUMN_GAP = 2;\nconst MIN_NAME_WIDTH = 16;\nconst NAME_RATIO = 0.45;\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\nconst DIM: ThemeColor = 'dim';\nconst ACCENT: ThemeColor = 'accent';\nconst SELECTED_BG: ThemeBg = 'selectedBg';\nconst ROW_BG: ThemeBg = 'userMessageBg';\n\nexport interface PickerRow<T> {\n /** Stable identity, used only to keep rows distinct. */\n key: string;\n /** Left column. */\n name: string;\n /** Right column, trimmed from the front so its tail survives. */\n detail: string;\n /** Extra text the filter should match but the row does not show. */\n search?: string;\n value: T;\n}\n\nexport interface WorkflowPickerConfig {\n title: string;\n breadcrumb: string;\n /** Plural noun for the header count, e.g. `workflows`. */\n unit: string;\n /** Verb on the enter key cap, e.g. `launch`. */\n action: string;\n filterPlaceholder: string;\n /** Shown when the source list itself is empty, as opposed to over-filtered. */\n emptyMessage: string;\n}\n\nexport function filterPickerRows<T>(rows: readonly PickerRow<T>[], query: string): PickerRow<T>[] {\n if (!query.trim()) return [...rows];\n return rows.filter((row) => matchesQuery([row.name, row.detail, row.search ?? ''].join(' '), query));\n}\n\nexport class WorkflowPickerComponent<T> extends DoomOverlay {\n private query = '';\n private selected = 0;\n private bodyHeight = 1;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly config: WorkflowPickerConfig,\n private readonly rows: readonly PickerRow<T>[],\n private readonly done: (value: T | undefined) => void,\n ) {\n super(tui, theme);\n }\n\n private matches(): PickerRow<T>[] {\n return filterPickerRows(this.rows, this.query);\n }\n\n /** Rows the list can draw, once the filter field has taken its share. */\n private capacity(): number {\n return Math.max(1, this.bodyHeight - FILTER_ROWS);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.done(undefined);\n return;\n }\n const matches = this.matches();\n if (matchesKey(data, 'enter')) {\n const row = matches[Math.min(this.selected, matches.length - 1)];\n if (row) this.done(row.value);\n return;\n }\n\n if (matchesKey(data, 'up') || matchesKey(data, 'ctrl+p')) this.move(-1, matches.length);\n else if (matchesKey(data, 'down') || matchesKey(data, 'ctrl+n')) this.move(1, matches.length);\n else if (matchesKey(data, 'pageUp')) this.move(-this.capacity(), matches.length);\n else if (matchesKey(data, 'pageDown')) this.move(this.capacity(), matches.length);\n else if (matchesKey(data, 'backspace')) this.setQuery(this.query.slice(0, -1));\n else if (data === KEY_CLEAR) this.setQuery('');\n else if (!isControlInput(data)) this.setQuery(this.query + data);\n else return;\n\n this.tui.requestRender();\n }\n\n /** Editing the filter resets the cursor: the previous index named another row. */\n private setQuery(query: string): void {\n if (query === this.query) return;\n this.query = query;\n this.selected = 0;\n }\n\n private move(delta: number, count: number): void {\n if (count === 0) return;\n this.selected = Math.max(0, Math.min(count - 1, this.selected + delta));\n }\n\n protected getChrome(): DoomOverlayChrome {\n const matches = this.matches();\n const scope = this.query ? `${matches.length} of ${this.rows.length}` : `${this.rows.length}`;\n return {\n title: this.config.title,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: this.config.breadcrumb,\n headerRight: `${scope} ${this.config.unit}`,\n footer: `↑↓ select · enter ${this.config.action} · type to filter · ctrl+u clear · esc cancel`,\n footerHints: [\n ['↑↓', 'select'],\n ['enter', this.config.action],\n ['type', 'filter'],\n ['ctrl+u', 'clear'],\n ['esc', 'cancel'],\n ],\n footerRight:\n matches.length > 0 ? `${Math.min(this.selected, matches.length - 1) + 1}/${matches.length}` : 'no match',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n this.bodyHeight = height;\n return [...this.filterField(width), ...this.listLines(width)].slice(0, height);\n }\n\n private filterField(width: number): string[] {\n const value = this.query\n ? `${this.query}${CURSOR_BLOCK}`\n : `${CURSOR_BLOCK}${this.theme.fg(DIM, this.config.filterPlaceholder)}`;\n return [this.theme.fg(DIM, FILTER_LABEL), this.theme.bg(SELECTED_BG, fit(` ${value}`, width)), ''];\n }\n\n private listLines(width: number): string[] {\n if (this.rows.length === 0) return [this.theme.fg(DIM, this.config.emptyMessage)];\n const matches = this.matches();\n if (matches.length === 0) return [this.theme.fg(DIM, `No ${this.config.unit} match \"${this.query}\".`)];\n\n const capacity = this.capacity();\n const active = Math.min(this.selected, matches.length - 1);\n // The window follows the cursor rather than the other way round, so a filter\n // that shrinks the list can never strand the selection off-screen.\n const start = Math.max(0, Math.min(active - capacity + 1, Math.max(0, matches.length - capacity)));\n\n const nameWidth = Math.max(MIN_NAME_WIDTH, Math.floor((width - MARKER_COLUMNS - COLUMN_GAP) * NAME_RATIO));\n const detailWidth = Math.max(0, width - MARKER_COLUMNS - COLUMN_GAP - nameWidth);\n return matches.slice(start, start + capacity).map((row, offset) => {\n const current = start + offset === active;\n const marker = current ? this.theme.fg(ACCENT, SELECTION_MARKER) : ' ';\n const name = row.name || EMPTY_NAME;\n const label = current ? this.theme.bold(fit(name, nameWidth)) : fit(name, nameWidth);\n const detail = this.theme.fg(DIM, fit(pathTail(row.detail, detailWidth), detailWidth));\n const background: ThemeBg = current ? SELECTED_BG : ROW_BG;\n return this.theme.bg(background, fit(`${marker} ${label}${' '.repeat(COLUMN_GAP)}${detail}`, width));\n });\n }\n}\n\nexport async function openWorkflowPickerOverlay<T>(\n ctx: ExtensionContext,\n config: WorkflowPickerConfig,\n rows: readonly PickerRow<T>[],\n): Promise<T | undefined> {\n return ctx.ui.custom<T | undefined>(\n (tui, theme, _keybindings, done) => new WorkflowPickerComponent(tui, theme, config, rows, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
let e=require("@earendil-works/pi-tui");const t=`workflow-mcp-progress`,n=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`],r={starting:`dim`,running:`accent`,completed:`success`,paused:`warning`,failed:`error`,skipped:`muted`};function i(e){return e.findLast(e=>e.type===`step`&&!!e.step)}function a(e){return e.at(-1)}function o(e,t){if(e.stage===`error`)return`failed`;if(e.stage===`completed`)return`completed`;if(e.executionState===`paused`||e.executionState===`pause_requested`)return`paused`;switch(t?.status){case`failed`:return`failed`;case`paused`:case`pause_requested`:return`paused`;case`completed`:return`completed`;case`skipped`:return`skipped`;case`resumed`:case`running`:return`running`;default:return e.executionCursor?.stepName?`running`:`starting`}}function s(e){switch(e.executionCursor?.phase){case`pre`:return`Preparing workflow`;case`post`:return`Finalizing workflow`;case`job`:case`step`:return`Starting job`;default:return`Starting workflow`}}function c(e,t){let n=i(t),r=a(t),c=e.executionCursor,l=c?.job??n?.job??r?.job??e.job??c?.phase??`workflow`,u=c?.stepName??(n?.job===l?n.step:void 0)??s(e);return{job:l,key:e.runId??`${e.workspace}/${e.runKey}`,name:e.displayName,startedAt:e.startedAt,state:o(e,r),step:u}}function l(e){if(!Number.isFinite(e)||e<0)return`?`;let t=Math.floor(e/1e3),n=t%60,r=Math.floor(t/60);if(r<60)return r===0?`${n}s`:`${r}m${String(n).padStart(2,`0`)}s`;let i=r%60;return`${Math.floor(r/60)}h${String(i).padStart(2,`0`)}m${String(n).padStart(2,`0`)}s`}function u(e,t){if(e!==`running`&&e!==`starting`)return`*`;let r=Number.isFinite(t)?Math.floor(Math.max(0,t)/200):0;return n[r%n.length]??n[0]}function d(e,t,n=Date.now()){let i=Date.parse(e.startedAt),a=l(Number.isFinite(i)?n-i:NaN),o=r[e.state];return[t.fg(o,e.name),t.fg(`muted`,`[${a}][${e.job}]`),t.fg(o,` ${u(e.state,n)} `),t.fg(`text`,e.step)].join(``)}var f=class{rows=[];uiCtx;widgetRegistered=!1;tui;progressTimer;setUICtx(e){e!==this.uiCtx&&(this.uiCtx&&this.widgetRegistered&&this.uiCtx.setWidget(t,void 0),this.stopProgressTimer(),this.uiCtx=e,this.widgetRegistered=!1,this.tui=void 0)}update(e){if(this.rows=e,!this.uiCtx){this.stopProgressTimer();return}if(e.length===0){this.widgetRegistered&&(this.uiCtx.setWidget(t,void 0),this.widgetRegistered=!1,this.tui=void 0),this.stopProgressTimer();return}if(this.widgetRegistered){this.syncProgressTimer(),this.tui?.requestRender();return}this.uiCtx.setWidget(t,(e,t)=>(this.tui=e,{render:e=>this.renderWidget(this.uiCtx?.theme??t,e),invalidate:()=>{}}),{placement:`aboveEditor`}),this.widgetRegistered=!0,this.syncProgressTimer()}syncProgressTimer(){if(!this.widgetRegistered||this.rows.length===0){this.stopProgressTimer();return}this.progressTimer||(this.progressTimer=setInterval(()=>this.tui?.requestRender(),200),this.progressTimer.unref?.())}stopProgressTimer(){this.progressTimer&&clearInterval(this.progressTimer),this.progressTimer=void 0}renderWidget(t,n){if(this.rows.length===0)return[];let r=t=>(0,e.truncateToWidth)(t,n,`…`),i=r(`${t.fg(`accent`,`●`)} ${t.fg(`accent`,`Workflows (${this.rows.length})`)}`),a=Date.now(),o=[i];for(let[e,n]of this.rows.entries()){let i=e===this.rows.length-1?`└─`:`├─`;o.push(r(`${t.fg(`dim`,i)} ${d(n,t,a)}`))}return o.push(``),o}dispose(){this.stopProgressTimer(),this.uiCtx&&this.uiCtx.setWidget(t,void 0),this.rows=[],this.widgetRegistered=!1,this.tui=void 0,this.uiCtx=void 0}};exports.WORKFLOW_PROGRESS_COLORS=r,exports.WORKFLOW_PROGRESS_WIDGET_KEY=t,exports.WorkflowProgressOverlay=f,exports.formatWorkflowDuration=l,exports.formatWorkflowProgressRow=d,exports.workflowProgressRow=c;
|
|
2
|
-
//# sourceMappingURL=workflowProgressOverlay.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowProgressOverlay.cjs","names":["truncateToWidth"],"sources":["../../../../src/tui/workflow/workflowProgressOverlay.ts"],"sourcesContent":["/**\n * Task-like live workflow list shown above Pi's editor.\n *\n * DESIGN PATTERNS:\n * - The extension owns run/event state; this component only projects and renders it.\n * - Register the widget once, then request redraws as progress, elapsed time, or the spinner changes.\n * - Terminal runs are absent from `rows`, so the widget disappears when the last run ends.\n *\n * CODING STANDARDS:\n * - Every rendered line is ANSI-aware and truncated to the available terminal width.\n * - Theme colors come from Pi's live UI context so theme changes repaint correctly.\n * - Named exports only and explicit return types on exported members.\n *\n * AVOID:\n * - Persisting progress in the chat transcript.\n * - Starting timers while no workflow is visible.\n */\n\nimport type { WorkflowProgressEvent, WorkflowRunRecord } from '@agimon-ai/workflow-mcp';\nimport type { ExtensionUIContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { type TUI, truncateToWidth } from '@earendil-works/pi-tui';\n\nexport const WORKFLOW_PROGRESS_WIDGET_KEY = 'workflow-mcp-progress';\n\nconst OVERLAY_HEADING = 'Workflows';\nconst PROGRESS_TICK_MS = 200;\nconst RUNNING_SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] as const;\nconst STATIC_PROGRESS_MARKER = '*';\nconst MILLISECONDS_PER_SECOND = 1000;\nconst SECONDS_PER_MINUTE = 60;\nconst MINUTES_PER_HOUR = 60;\nconst STARTING_JOB_STEP = 'Starting job';\nconst STARTING_WORKFLOW_STEP = 'Starting workflow';\nconst PREPARING_WORKFLOW_STEP = 'Preparing workflow';\nconst FINALIZING_WORKFLOW_STEP = 'Finalizing workflow';\n\ntype WorkflowProgressColor = Parameters<Theme['fg']>[0];\n\nexport type WorkflowProgressState = 'completed' | 'failed' | 'paused' | 'running' | 'skipped' | 'starting';\n\n/** One explicit palette makes every workflow state visually stable across the list. */\nexport const WORKFLOW_PROGRESS_COLORS: Readonly<Record<WorkflowProgressState, WorkflowProgressColor>> = {\n starting: 'dim',\n running: 'accent',\n completed: 'success',\n paused: 'warning',\n failed: 'error',\n skipped: 'muted',\n};\n\nexport interface WorkflowProgressRow {\n job: string;\n key: string;\n name: string;\n startedAt: string;\n state: WorkflowProgressState;\n step: string;\n}\n\nfunction latestStep(events: readonly WorkflowProgressEvent[]): WorkflowProgressEvent | undefined {\n return events.findLast((event) => event.type === 'step' && !!event.step);\n}\n\nfunction latestEvent(events: readonly WorkflowProgressEvent[]): WorkflowProgressEvent | undefined {\n return events.at(-1);\n}\n\nfunction progressState(record: WorkflowRunRecord, event: WorkflowProgressEvent | undefined): WorkflowProgressState {\n if (record.stage === 'error') return 'failed';\n if (record.stage === 'completed') return 'completed';\n if (record.executionState === 'paused' || record.executionState === 'pause_requested') return 'paused';\n\n switch (event?.status) {\n case 'failed':\n return 'failed';\n case 'paused':\n case 'pause_requested':\n return 'paused';\n case 'completed':\n return 'completed';\n case 'skipped':\n return 'skipped';\n case 'resumed':\n case 'running':\n return 'running';\n default:\n return record.executionCursor?.stepName ? 'running' : 'starting';\n }\n}\n\nfunction fallbackStep(record: WorkflowRunRecord): string {\n switch (record.executionCursor?.phase) {\n case 'pre':\n return PREPARING_WORKFLOW_STEP;\n case 'post':\n return FINALIZING_WORKFLOW_STEP;\n case 'job':\n case 'step':\n return STARTING_JOB_STEP;\n default:\n return STARTING_WORKFLOW_STEP;\n }\n}\n\n/** Derive the single current line for one run from its record and pushed progress events. */\nexport function workflowProgressRow(\n record: WorkflowRunRecord,\n events: readonly WorkflowProgressEvent[],\n): WorkflowProgressRow {\n const stepEvent = latestStep(events);\n const event = latestEvent(events);\n const cursor = record.executionCursor;\n const job = cursor?.job ?? stepEvent?.job ?? event?.job ?? record.job ?? cursor?.phase ?? 'workflow';\n const step = cursor?.stepName ?? (stepEvent?.job === job ? stepEvent.step : undefined) ?? fallbackStep(record);\n\n return {\n job,\n key: record.runId ?? `${record.workspace}/${record.runKey}`,\n name: record.displayName,\n startedAt: record.startedAt,\n state: progressState(record, event),\n step,\n };\n}\n\n/** Task-style elapsed time: whole seconds stay stable and compact between animation ticks. */\nexport function formatWorkflowDuration(durationMs: number): string {\n if (!Number.isFinite(durationMs) || durationMs < 0) return '?';\n const totalSeconds = Math.floor(durationMs / MILLISECONDS_PER_SECOND);\n const seconds = totalSeconds % SECONDS_PER_MINUTE;\n const totalMinutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE);\n if (totalMinutes < MINUTES_PER_HOUR) {\n return totalMinutes === 0 ? `${seconds}s` : `${totalMinutes}m${String(seconds).padStart(2, '0')}s`;\n }\n const minutes = totalMinutes % MINUTES_PER_HOUR;\n const hours = Math.floor(totalMinutes / MINUTES_PER_HOUR);\n return `${hours}h${String(minutes).padStart(2, '0')}m${String(seconds).padStart(2, '0')}s`;\n}\n\nfunction progressMarker(state: WorkflowProgressState, nowMs: number): string {\n if (state !== 'running' && state !== 'starting') return STATIC_PROGRESS_MARKER;\n const tick = Number.isFinite(nowMs) ? Math.floor(Math.max(0, nowMs) / PROGRESS_TICK_MS) : 0;\n return RUNNING_SPINNER_FRAMES[tick % RUNNING_SPINNER_FRAMES.length] ?? RUNNING_SPINNER_FRAMES[0];\n}\n\n/** Render exactly: `<workflow name>[<duration>][<job>] <activity> <step>`. */\nexport function formatWorkflowProgressRow(row: WorkflowProgressRow, theme: Theme, nowMs = Date.now()): string {\n const startedAt = Date.parse(row.startedAt);\n const duration = formatWorkflowDuration(Number.isFinite(startedAt) ? nowMs - startedAt : Number.NaN);\n const color = WORKFLOW_PROGRESS_COLORS[row.state];\n return [\n theme.fg(color, row.name),\n theme.fg('muted', `[${duration}][${row.job}]`),\n theme.fg(color, ` ${progressMarker(row.state, nowMs)} `),\n theme.fg('text', row.step),\n ].join('');\n}\n\n/** Persistent workflow widget with the same heading/tree shape as Doom's task list. */\nexport class WorkflowProgressOverlay {\n private rows: readonly WorkflowProgressRow[] = [];\n private uiCtx: ExtensionUIContext | undefined;\n private widgetRegistered = false;\n private tui: TUI | undefined;\n private progressTimer: NodeJS.Timeout | undefined;\n\n /** Identity-compare so repeated session starts do not register duplicate widgets. */\n setUICtx(ctx: ExtensionUIContext): void {\n if (ctx === this.uiCtx) return;\n if (this.uiCtx && this.widgetRegistered) this.uiCtx.setWidget(WORKFLOW_PROGRESS_WIDGET_KEY, undefined);\n this.stopProgressTimer();\n this.uiCtx = ctx;\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n\n update(rows: readonly WorkflowProgressRow[]): void {\n this.rows = rows;\n if (!this.uiCtx) {\n this.stopProgressTimer();\n return;\n }\n\n if (rows.length === 0) {\n if (this.widgetRegistered) {\n this.uiCtx.setWidget(WORKFLOW_PROGRESS_WIDGET_KEY, undefined);\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n this.stopProgressTimer();\n return;\n }\n\n if (this.widgetRegistered) {\n this.syncProgressTimer();\n this.tui?.requestRender();\n return;\n }\n\n this.uiCtx.setWidget(\n WORKFLOW_PROGRESS_WIDGET_KEY,\n (tui, factoryTheme) => {\n this.tui = tui;\n return {\n render: (width: number) => this.renderWidget(this.uiCtx?.theme ?? factoryTheme, width),\n invalidate: () => {\n // Nothing cached: the next render reads the current rows, clock, and theme.\n },\n };\n },\n { placement: 'aboveEditor' },\n );\n this.widgetRegistered = true;\n this.syncProgressTimer();\n }\n\n private syncProgressTimer(): void {\n if (!this.widgetRegistered || this.rows.length === 0) {\n this.stopProgressTimer();\n return;\n }\n if (this.progressTimer) return;\n this.progressTimer = setInterval(() => this.tui?.requestRender(), PROGRESS_TICK_MS);\n this.progressTimer.unref?.();\n }\n\n private stopProgressTimer(): void {\n if (this.progressTimer) clearInterval(this.progressTimer);\n this.progressTimer = undefined;\n }\n\n private renderWidget(theme: Theme, width: number): string[] {\n if (this.rows.length === 0) return [];\n const truncate = (line: string): string => truncateToWidth(line, width, '…');\n const heading = truncate(\n `${theme.fg('accent', '●')} ${theme.fg('accent', `${OVERLAY_HEADING} (${this.rows.length})`)}`,\n );\n const nowMs = Date.now();\n const lines = [heading];\n\n for (const [index, row] of this.rows.entries()) {\n const branch = index === this.rows.length - 1 ? '└─' : '├─';\n lines.push(truncate(`${theme.fg('dim', branch)} ${formatWorkflowProgressRow(row, theme, nowMs)}`));\n }\n\n // Pi adds space above widgets but not below; keep the list off the editor border.\n lines.push('');\n return lines;\n }\n\n dispose(): void {\n this.stopProgressTimer();\n if (this.uiCtx) this.uiCtx.setWidget(WORKFLOW_PROGRESS_WIDGET_KEY, undefined);\n this.rows = [];\n this.widgetRegistered = false;\n this.tui = undefined;\n this.uiCtx = undefined;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{truncateToWidth as e}from"@earendil-works/pi-tui";const t=`workflow-mcp-progress`,n=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`],r={starting:`dim`,running:`accent`,completed:`success`,paused:`warning`,failed:`error`,skipped:`muted`};function i(e){return e.findLast(e=>e.type===`step`&&!!e.step)}function a(e){return e.at(-1)}function o(e,t){if(e.stage===`error`)return`failed`;if(e.stage===`completed`)return`completed`;if(e.executionState===`paused`||e.executionState===`pause_requested`)return`paused`;switch(t?.status){case`failed`:return`failed`;case`paused`:case`pause_requested`:return`paused`;case`completed`:return`completed`;case`skipped`:return`skipped`;case`resumed`:case`running`:return`running`;default:return e.executionCursor?.stepName?`running`:`starting`}}function s(e){switch(e.executionCursor?.phase){case`pre`:return`Preparing workflow`;case`post`:return`Finalizing workflow`;case`job`:case`step`:return`Starting job`;default:return`Starting workflow`}}function c(e,t){let n=i(t),r=a(t),c=e.executionCursor,l=c?.job??n?.job??r?.job??e.job??c?.phase??`workflow`,u=c?.stepName??(n?.job===l?n.step:void 0)??s(e);return{job:l,key:e.runId??`${e.workspace}/${e.runKey}`,name:e.displayName,startedAt:e.startedAt,state:o(e,r),step:u}}function l(e){if(!Number.isFinite(e)||e<0)return`?`;let t=Math.floor(e/1e3),n=t%60,r=Math.floor(t/60);if(r<60)return r===0?`${n}s`:`${r}m${String(n).padStart(2,`0`)}s`;let i=r%60;return`${Math.floor(r/60)}h${String(i).padStart(2,`0`)}m${String(n).padStart(2,`0`)}s`}function u(e,t){if(e!==`running`&&e!==`starting`)return`*`;let r=Number.isFinite(t)?Math.floor(Math.max(0,t)/200):0;return n[r%n.length]??n[0]}function d(e,t,n=Date.now()){let i=Date.parse(e.startedAt),a=l(Number.isFinite(i)?n-i:NaN),o=r[e.state];return[t.fg(o,e.name),t.fg(`muted`,`[${a}][${e.job}]`),t.fg(o,` ${u(e.state,n)} `),t.fg(`text`,e.step)].join(``)}var f=class{rows=[];uiCtx;widgetRegistered=!1;tui;progressTimer;setUICtx(e){e!==this.uiCtx&&(this.uiCtx&&this.widgetRegistered&&this.uiCtx.setWidget(t,void 0),this.stopProgressTimer(),this.uiCtx=e,this.widgetRegistered=!1,this.tui=void 0)}update(e){if(this.rows=e,!this.uiCtx){this.stopProgressTimer();return}if(e.length===0){this.widgetRegistered&&(this.uiCtx.setWidget(t,void 0),this.widgetRegistered=!1,this.tui=void 0),this.stopProgressTimer();return}if(this.widgetRegistered){this.syncProgressTimer(),this.tui?.requestRender();return}this.uiCtx.setWidget(t,(e,t)=>(this.tui=e,{render:e=>this.renderWidget(this.uiCtx?.theme??t,e),invalidate:()=>{}}),{placement:`aboveEditor`}),this.widgetRegistered=!0,this.syncProgressTimer()}syncProgressTimer(){if(!this.widgetRegistered||this.rows.length===0){this.stopProgressTimer();return}this.progressTimer||(this.progressTimer=setInterval(()=>this.tui?.requestRender(),200),this.progressTimer.unref?.())}stopProgressTimer(){this.progressTimer&&clearInterval(this.progressTimer),this.progressTimer=void 0}renderWidget(t,n){if(this.rows.length===0)return[];let r=t=>e(t,n,`…`),i=r(`${t.fg(`accent`,`●`)} ${t.fg(`accent`,`Workflows (${this.rows.length})`)}`),a=Date.now(),o=[i];for(let[e,n]of this.rows.entries()){let i=e===this.rows.length-1?`└─`:`├─`;o.push(r(`${t.fg(`dim`,i)} ${d(n,t,a)}`))}return o.push(``),o}dispose(){this.stopProgressTimer(),this.uiCtx&&this.uiCtx.setWidget(t,void 0),this.rows=[],this.widgetRegistered=!1,this.tui=void 0,this.uiCtx=void 0}};export{r as WORKFLOW_PROGRESS_COLORS,t as WORKFLOW_PROGRESS_WIDGET_KEY,f as WorkflowProgressOverlay,l as formatWorkflowDuration,d as formatWorkflowProgressRow,c as workflowProgressRow};
|
|
2
|
-
//# sourceMappingURL=workflowProgressOverlay.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowProgressOverlay.mjs","names":[],"sources":["../../../../src/tui/workflow/workflowProgressOverlay.ts"],"sourcesContent":["/**\n * Task-like live workflow list shown above Pi's editor.\n *\n * DESIGN PATTERNS:\n * - The extension owns run/event state; this component only projects and renders it.\n * - Register the widget once, then request redraws as progress, elapsed time, or the spinner changes.\n * - Terminal runs are absent from `rows`, so the widget disappears when the last run ends.\n *\n * CODING STANDARDS:\n * - Every rendered line is ANSI-aware and truncated to the available terminal width.\n * - Theme colors come from Pi's live UI context so theme changes repaint correctly.\n * - Named exports only and explicit return types on exported members.\n *\n * AVOID:\n * - Persisting progress in the chat transcript.\n * - Starting timers while no workflow is visible.\n */\n\nimport type { WorkflowProgressEvent, WorkflowRunRecord } from '@agimon-ai/workflow-mcp';\nimport type { ExtensionUIContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { type TUI, truncateToWidth } from '@earendil-works/pi-tui';\n\nexport const WORKFLOW_PROGRESS_WIDGET_KEY = 'workflow-mcp-progress';\n\nconst OVERLAY_HEADING = 'Workflows';\nconst PROGRESS_TICK_MS = 200;\nconst RUNNING_SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] as const;\nconst STATIC_PROGRESS_MARKER = '*';\nconst MILLISECONDS_PER_SECOND = 1000;\nconst SECONDS_PER_MINUTE = 60;\nconst MINUTES_PER_HOUR = 60;\nconst STARTING_JOB_STEP = 'Starting job';\nconst STARTING_WORKFLOW_STEP = 'Starting workflow';\nconst PREPARING_WORKFLOW_STEP = 'Preparing workflow';\nconst FINALIZING_WORKFLOW_STEP = 'Finalizing workflow';\n\ntype WorkflowProgressColor = Parameters<Theme['fg']>[0];\n\nexport type WorkflowProgressState = 'completed' | 'failed' | 'paused' | 'running' | 'skipped' | 'starting';\n\n/** One explicit palette makes every workflow state visually stable across the list. */\nexport const WORKFLOW_PROGRESS_COLORS: Readonly<Record<WorkflowProgressState, WorkflowProgressColor>> = {\n starting: 'dim',\n running: 'accent',\n completed: 'success',\n paused: 'warning',\n failed: 'error',\n skipped: 'muted',\n};\n\nexport interface WorkflowProgressRow {\n job: string;\n key: string;\n name: string;\n startedAt: string;\n state: WorkflowProgressState;\n step: string;\n}\n\nfunction latestStep(events: readonly WorkflowProgressEvent[]): WorkflowProgressEvent | undefined {\n return events.findLast((event) => event.type === 'step' && !!event.step);\n}\n\nfunction latestEvent(events: readonly WorkflowProgressEvent[]): WorkflowProgressEvent | undefined {\n return events.at(-1);\n}\n\nfunction progressState(record: WorkflowRunRecord, event: WorkflowProgressEvent | undefined): WorkflowProgressState {\n if (record.stage === 'error') return 'failed';\n if (record.stage === 'completed') return 'completed';\n if (record.executionState === 'paused' || record.executionState === 'pause_requested') return 'paused';\n\n switch (event?.status) {\n case 'failed':\n return 'failed';\n case 'paused':\n case 'pause_requested':\n return 'paused';\n case 'completed':\n return 'completed';\n case 'skipped':\n return 'skipped';\n case 'resumed':\n case 'running':\n return 'running';\n default:\n return record.executionCursor?.stepName ? 'running' : 'starting';\n }\n}\n\nfunction fallbackStep(record: WorkflowRunRecord): string {\n switch (record.executionCursor?.phase) {\n case 'pre':\n return PREPARING_WORKFLOW_STEP;\n case 'post':\n return FINALIZING_WORKFLOW_STEP;\n case 'job':\n case 'step':\n return STARTING_JOB_STEP;\n default:\n return STARTING_WORKFLOW_STEP;\n }\n}\n\n/** Derive the single current line for one run from its record and pushed progress events. */\nexport function workflowProgressRow(\n record: WorkflowRunRecord,\n events: readonly WorkflowProgressEvent[],\n): WorkflowProgressRow {\n const stepEvent = latestStep(events);\n const event = latestEvent(events);\n const cursor = record.executionCursor;\n const job = cursor?.job ?? stepEvent?.job ?? event?.job ?? record.job ?? cursor?.phase ?? 'workflow';\n const step = cursor?.stepName ?? (stepEvent?.job === job ? stepEvent.step : undefined) ?? fallbackStep(record);\n\n return {\n job,\n key: record.runId ?? `${record.workspace}/${record.runKey}`,\n name: record.displayName,\n startedAt: record.startedAt,\n state: progressState(record, event),\n step,\n };\n}\n\n/** Task-style elapsed time: whole seconds stay stable and compact between animation ticks. */\nexport function formatWorkflowDuration(durationMs: number): string {\n if (!Number.isFinite(durationMs) || durationMs < 0) return '?';\n const totalSeconds = Math.floor(durationMs / MILLISECONDS_PER_SECOND);\n const seconds = totalSeconds % SECONDS_PER_MINUTE;\n const totalMinutes = Math.floor(totalSeconds / SECONDS_PER_MINUTE);\n if (totalMinutes < MINUTES_PER_HOUR) {\n return totalMinutes === 0 ? `${seconds}s` : `${totalMinutes}m${String(seconds).padStart(2, '0')}s`;\n }\n const minutes = totalMinutes % MINUTES_PER_HOUR;\n const hours = Math.floor(totalMinutes / MINUTES_PER_HOUR);\n return `${hours}h${String(minutes).padStart(2, '0')}m${String(seconds).padStart(2, '0')}s`;\n}\n\nfunction progressMarker(state: WorkflowProgressState, nowMs: number): string {\n if (state !== 'running' && state !== 'starting') return STATIC_PROGRESS_MARKER;\n const tick = Number.isFinite(nowMs) ? Math.floor(Math.max(0, nowMs) / PROGRESS_TICK_MS) : 0;\n return RUNNING_SPINNER_FRAMES[tick % RUNNING_SPINNER_FRAMES.length] ?? RUNNING_SPINNER_FRAMES[0];\n}\n\n/** Render exactly: `<workflow name>[<duration>][<job>] <activity> <step>`. */\nexport function formatWorkflowProgressRow(row: WorkflowProgressRow, theme: Theme, nowMs = Date.now()): string {\n const startedAt = Date.parse(row.startedAt);\n const duration = formatWorkflowDuration(Number.isFinite(startedAt) ? nowMs - startedAt : Number.NaN);\n const color = WORKFLOW_PROGRESS_COLORS[row.state];\n return [\n theme.fg(color, row.name),\n theme.fg('muted', `[${duration}][${row.job}]`),\n theme.fg(color, ` ${progressMarker(row.state, nowMs)} `),\n theme.fg('text', row.step),\n ].join('');\n}\n\n/** Persistent workflow widget with the same heading/tree shape as Doom's task list. */\nexport class WorkflowProgressOverlay {\n private rows: readonly WorkflowProgressRow[] = [];\n private uiCtx: ExtensionUIContext | undefined;\n private widgetRegistered = false;\n private tui: TUI | undefined;\n private progressTimer: NodeJS.Timeout | undefined;\n\n /** Identity-compare so repeated session starts do not register duplicate widgets. */\n setUICtx(ctx: ExtensionUIContext): void {\n if (ctx === this.uiCtx) return;\n if (this.uiCtx && this.widgetRegistered) this.uiCtx.setWidget(WORKFLOW_PROGRESS_WIDGET_KEY, undefined);\n this.stopProgressTimer();\n this.uiCtx = ctx;\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n\n update(rows: readonly WorkflowProgressRow[]): void {\n this.rows = rows;\n if (!this.uiCtx) {\n this.stopProgressTimer();\n return;\n }\n\n if (rows.length === 0) {\n if (this.widgetRegistered) {\n this.uiCtx.setWidget(WORKFLOW_PROGRESS_WIDGET_KEY, undefined);\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n this.stopProgressTimer();\n return;\n }\n\n if (this.widgetRegistered) {\n this.syncProgressTimer();\n this.tui?.requestRender();\n return;\n }\n\n this.uiCtx.setWidget(\n WORKFLOW_PROGRESS_WIDGET_KEY,\n (tui, factoryTheme) => {\n this.tui = tui;\n return {\n render: (width: number) => this.renderWidget(this.uiCtx?.theme ?? factoryTheme, width),\n invalidate: () => {\n // Nothing cached: the next render reads the current rows, clock, and theme.\n },\n };\n },\n { placement: 'aboveEditor' },\n );\n this.widgetRegistered = true;\n this.syncProgressTimer();\n }\n\n private syncProgressTimer(): void {\n if (!this.widgetRegistered || this.rows.length === 0) {\n this.stopProgressTimer();\n return;\n }\n if (this.progressTimer) return;\n this.progressTimer = setInterval(() => this.tui?.requestRender(), PROGRESS_TICK_MS);\n this.progressTimer.unref?.();\n }\n\n private stopProgressTimer(): void {\n if (this.progressTimer) clearInterval(this.progressTimer);\n this.progressTimer = undefined;\n }\n\n private renderWidget(theme: Theme, width: number): string[] {\n if (this.rows.length === 0) return [];\n const truncate = (line: string): string => truncateToWidth(line, width, '…');\n const heading = truncate(\n `${theme.fg('accent', '●')} ${theme.fg('accent', `${OVERLAY_HEADING} (${this.rows.length})`)}`,\n );\n const nowMs = Date.now();\n const lines = [heading];\n\n for (const [index, row] of this.rows.entries()) {\n const branch = index === this.rows.length - 1 ? '└─' : '├─';\n lines.push(truncate(`${theme.fg('dim', branch)} ${formatWorkflowProgressRow(row, theme, nowMs)}`));\n }\n\n // Pi adds space above widgets but not below; keep the list off the editor border.\n lines.push('');\n return lines;\n }\n\n dispose(): void {\n this.stopProgressTimer();\n if (this.uiCtx) this.uiCtx.setWidget(WORKFLOW_PROGRESS_WIDGET_KEY, undefined);\n this.rows = [];\n this.widgetRegistered = false;\n this.tui = undefined;\n this.uiCtx = undefined;\n }\n}\n"],"mappings":"yDAsBA,MAAa,EAA+B,wBAItC,EAAyB,CAAC,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,IAAK,GAAG,EAenE,EAA2F,CACtG,SAAU,MACV,QAAS,SACT,UAAW,UACX,OAAQ,UACR,OAAQ,QACR,QAAS,OACX,EAWA,SAAS,EAAW,EAA6E,CAC/F,OAAO,EAAO,SAAU,GAAU,EAAM,OAAS,QAAU,CAAC,CAAC,EAAM,IAAI,CACzE,CAEA,SAAS,EAAY,EAA6E,CAChG,OAAO,EAAO,GAAG,EAAE,CACrB,CAEA,SAAS,EAAc,EAA2B,EAAiE,CACjH,GAAI,EAAO,QAAU,QAAS,MAAO,SACrC,GAAI,EAAO,QAAU,YAAa,MAAO,YACzC,GAAI,EAAO,iBAAmB,UAAY,EAAO,iBAAmB,kBAAmB,MAAO,SAE9F,OAAQ,GAAO,OAAf,CACE,IAAK,SACH,MAAO,SACT,IAAK,SACL,IAAK,kBACH,MAAO,SACT,IAAK,YACH,MAAO,YACT,IAAK,UACH,MAAO,UACT,IAAK,UACL,IAAK,UACH,MAAO,UACT,QACE,OAAO,EAAO,iBAAiB,SAAW,UAAY,UAC1D,CACF,CAEA,SAAS,EAAa,EAAmC,CACvD,OAAQ,EAAO,iBAAiB,MAAhC,CACE,IAAK,MACH,MAAO,qBACT,IAAK,OACH,MAAO,sBACT,IAAK,MACL,IAAK,OACH,MAAO,eACT,QACE,MAAO,mBACX,CACF,CAGA,SAAgB,EACd,EACA,EACqB,CACrB,IAAM,EAAY,EAAW,CAAM,EAC7B,EAAQ,EAAY,CAAM,EAC1B,EAAS,EAAO,gBAChB,EAAM,GAAQ,KAAO,GAAW,KAAO,GAAO,KAAO,EAAO,KAAO,GAAQ,OAAS,WACpF,EAAO,GAAQ,WAAa,GAAW,MAAQ,EAAM,EAAU,KAAO,IAAA,KAAc,EAAa,CAAM,EAE7G,MAAO,CACL,MACA,IAAK,EAAO,OAAS,GAAG,EAAO,UAAU,GAAG,EAAO,SACnD,KAAM,EAAO,YACb,UAAW,EAAO,UAClB,MAAO,EAAc,EAAQ,CAAK,EAClC,MACF,CACF,CAGA,SAAgB,EAAuB,EAA4B,CACjE,GAAI,CAAC,OAAO,SAAS,CAAU,GAAK,EAAa,EAAG,MAAO,IAC3D,IAAM,EAAe,KAAK,MAAM,EAAa,GAAuB,EAC9D,EAAU,EAAe,GACzB,EAAe,KAAK,MAAM,EAAe,EAAkB,EACjE,GAAI,EAAe,GACjB,OAAO,IAAiB,EAAI,GAAG,EAAQ,GAAK,GAAG,EAAa,GAAG,OAAO,CAAO,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,GAElG,IAAM,EAAU,EAAe,GAE/B,MAAO,GADO,KAAK,MAAM,EAAe,EAC1B,EAAE,GAAG,OAAO,CAAO,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,GAAG,OAAO,CAAO,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,EAC1F,CAEA,SAAS,EAAe,EAA8B,EAAuB,CAC3E,GAAI,IAAU,WAAa,IAAU,WAAY,MAAO,IACxD,IAAM,EAAO,OAAO,SAAS,CAAK,EAAI,KAAK,MAAM,KAAK,IAAI,EAAG,CAAK,EAAI,GAAgB,EAAI,EAC1F,OAAO,EAAuB,EAAO,EAAuB,SAAW,EAAuB,EAChG,CAGA,SAAgB,EAA0B,EAA0B,EAAc,EAAQ,KAAK,IAAI,EAAW,CAC5G,IAAM,EAAY,KAAK,MAAM,EAAI,SAAS,EACpC,EAAW,EAAuB,OAAO,SAAS,CAAS,EAAI,EAAQ,EAAY,GAAU,EAC7F,EAAQ,EAAyB,EAAI,OAC3C,MAAO,CACL,EAAM,GAAG,EAAO,EAAI,IAAI,EACxB,EAAM,GAAG,QAAS,IAAI,EAAS,IAAI,EAAI,IAAI,EAAE,EAC7C,EAAM,GAAG,EAAO,IAAI,EAAe,EAAI,MAAO,CAAK,EAAE,EAAE,EACvD,EAAM,GAAG,OAAQ,EAAI,IAAI,CAC3B,CAAC,CAAC,KAAK,EAAE,CACX,CAGA,IAAa,EAAb,KAAqC,CACnC,KAA+C,CAAC,EAChD,MACA,iBAA2B,GAC3B,IACA,cAGA,SAAS,EAA+B,CAClC,IAAQ,KAAK,QACb,KAAK,OAAS,KAAK,kBAAkB,KAAK,MAAM,UAAU,EAA8B,IAAA,EAAS,EACrG,KAAK,kBAAkB,EACvB,KAAK,MAAQ,EACb,KAAK,iBAAmB,GACxB,KAAK,IAAM,IAAA,GACb,CAEA,OAAO,EAA4C,CAEjD,GADA,KAAK,KAAO,EACR,CAAC,KAAK,MAAO,CACf,KAAK,kBAAkB,EACvB,MACF,CAEA,GAAI,EAAK,SAAW,EAAG,CACjB,KAAK,mBACP,KAAK,MAAM,UAAU,EAA8B,IAAA,EAAS,EAC5D,KAAK,iBAAmB,GACxB,KAAK,IAAM,IAAA,IAEb,KAAK,kBAAkB,EACvB,MACF,CAEA,GAAI,KAAK,iBAAkB,CACzB,KAAK,kBAAkB,EACvB,KAAK,KAAK,cAAc,EACxB,MACF,CAEA,KAAK,MAAM,UACT,GACC,EAAK,KACJ,KAAK,IAAM,EACJ,CACL,OAAS,GAAkB,KAAK,aAAa,KAAK,OAAO,OAAS,EAAc,CAAK,EACrF,eAAkB,CAElB,CACF,GAEF,CAAE,UAAW,aAAc,CAC7B,EACA,KAAK,iBAAmB,GACxB,KAAK,kBAAkB,CACzB,CAEA,mBAAkC,CAChC,GAAI,CAAC,KAAK,kBAAoB,KAAK,KAAK,SAAW,EAAG,CACpD,KAAK,kBAAkB,EACvB,MACF,CACI,KAAK,gBACT,KAAK,cAAgB,gBAAkB,KAAK,KAAK,cAAc,EAAG,GAAgB,EAClF,KAAK,cAAc,QAAQ,EAC7B,CAEA,mBAAkC,CAC5B,KAAK,eAAe,cAAc,KAAK,aAAa,EACxD,KAAK,cAAgB,IAAA,EACvB,CAEA,aAAqB,EAAc,EAAyB,CAC1D,GAAI,KAAK,KAAK,SAAW,EAAG,MAAO,CAAC,EACpC,IAAM,EAAY,GAAyB,EAAgB,EAAM,EAAO,GAAG,EACrE,EAAU,EACd,GAAG,EAAM,GAAG,SAAU,GAAG,EAAE,GAAG,EAAM,GAAG,SAAU,cAAuB,KAAK,KAAK,OAAO,EAAE,GAC7F,EACM,EAAQ,KAAK,IAAI,EACjB,EAAQ,CAAC,CAAO,EAEtB,IAAK,GAAM,CAAC,EAAO,KAAQ,KAAK,KAAK,QAAQ,EAAG,CAC9C,IAAM,EAAS,IAAU,KAAK,KAAK,OAAS,EAAI,KAAO,KACvD,EAAM,KAAK,EAAS,GAAG,EAAM,GAAG,MAAO,CAAM,EAAE,GAAG,EAA0B,EAAK,EAAO,CAAK,GAAG,CAAC,CACnG,CAIA,OADA,EAAM,KAAK,EAAE,EACN,CACT,CAEA,SAAgB,CACd,KAAK,kBAAkB,EACnB,KAAK,OAAO,KAAK,MAAM,UAAU,EAA8B,IAAA,EAAS,EAC5E,KAAK,KAAO,CAAC,EACb,KAAK,iBAAmB,GACxB,KAAK,IAAM,IAAA,GACX,KAAK,MAAQ,IAAA,EACf,CACF"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("./doomOverlay.cjs"),t=require("./overlayText.cjs");let n=require("@earendil-works/pi-tui");e.DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;const r=`WORKFLOW PROMPT`,i=`VALUE`,a=`default`,o=[[`↑↓`,`select`],[`enter`,`confirm`],[`esc`,`cancel`]],s=[[`type`,`edit`],[`enter`,`confirm`],[`ctrl+u`,`clear`],[`esc`,`cancel`]],c=`selectedBg`;var l=class extends e.DoomOverlay{options;done;selected;constructor(e,t,n,r){super(e,t),this.options=n,this.done=r,this.selected=Math.max(0,n.preselect?n.choices.indexOf(n.preselect):0)}handleInput(e){if((0,n.matchesKey)(e,`escape`)||(0,n.matchesKey)(e,`ctrl+c`)){this.done(void 0);return}if((0,n.matchesKey)(e,`enter`)){let e=this.options.choices[this.selected];e!==void 0&&this.done(e);return}let t=this.options.choices.length;if(t!==0){if((0,n.matchesKey)(e,`up`)||(0,n.matchesKey)(e,`ctrl+p`))this.selected=Math.max(0,this.selected-1);else if((0,n.matchesKey)(e,`down`)||(0,n.matchesKey)(e,`ctrl+n`))this.selected=Math.min(t-1,this.selected+1);else return;this.tui.requestRender()}}getChrome(){return{title:r,accent:e.DOOM_OVERLAY_ACCENT,breadcrumb:this.options.breadcrumb,headerRight:`${this.options.choices.length} options`,footer:`↑↓ select · enter confirm · esc cancel`,footerHints:o,footerRight:this.options.choices.length>0?`${this.selected+1}/${this.options.choices.length}`:`no options`}}renderBody(e,n){let r=[this.theme.bold(t.fit(this.options.title,e)),``];if(this.options.choices.length===0)return r.push(this.theme.fg(`dim`,`No options are available.`)),r.slice(0,n);r.push(this.theme.fg(`dim`,`CHOOSE`));let i=Math.max(1,n-2-1),o=Math.max(0,Math.min(this.selected-i+1,Math.max(0,this.options.choices.length-i)));for(let[n,s]of this.options.choices.slice(o,o+i).entries()){let i=o+n===this.selected,l=i?this.theme.fg(`accent`,`›`):` `,u=i?this.theme.bold(s):s,d=s===this.options.preselect?this.theme.fg(`dim`,a):``,f=i?c:`userMessageBg`;r.push(this.theme.bg(f,t.rightAligned(`${l} ${u}`,d,e)))}return r.slice(0,n)}},u=class extends e.DoomOverlay{options;done;draft=``;constructor(e,t,n,r){super(e,t),this.options=n,this.done=r}handleInput(e){if((0,n.matchesKey)(e,`escape`)||(0,n.matchesKey)(e,`ctrl+c`)){this.done(void 0);return}if((0,n.matchesKey)(e,`enter`)){this.done(this.draft.trim()||this.options.fallback||``);return}if((0,n.matchesKey)(e,`backspace`))this.draft=this.draft.slice(0,-1);else if(e===``)this.draft=``;else if(!t.isControlInput(e))this.draft+=e;else return;this.tui.requestRender()}getChrome(){return{title:r,accent:e.DOOM_OVERLAY_ACCENT,breadcrumb:this.options.breadcrumb,headerRight:this.options.fallback?`default ${this.options.fallback}`:`no default`,footer:`type to edit · enter confirm · ctrl+u clear · esc cancel`,footerHints:s}}renderBody(e,n){let r=this.options.fallback?t.rightAligned(this.theme.fg(`dim`,i),this.theme.fg(`dim`,`${a} ${this.options.fallback}`),e):this.theme.fg(`dim`,i);return[this.theme.bold(t.fit(this.options.title,e)),``,r,this.theme.bg(c,t.fit(` ${this.draft}█`,e))].slice(0,n)}};async function d(t,n){return t.ui.custom((e,t,r,i)=>new l(e,t,n,i),e.DOOM_FULLSCREEN_UI_OPTIONS)}async function f(t,n){return t.ui.custom((e,t,r,i)=>new u(e,t,n,i),e.DOOM_FULLSCREEN_UI_OPTIONS)}exports.WorkflowChoiceComponent=l,exports.WorkflowInputComponent=u,exports.openWorkflowChoice=d,exports.openWorkflowInput=f;
|
|
2
|
-
//# sourceMappingURL=workflowPrompt.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowPrompt.cjs","names":["DOOM_FULLSCREEN_UI_OPTIONS","DoomOverlay","matchesKey","DOOM_OVERLAY_ACCENT","fit","rightAligned","isControlInput"],"sources":["../../../../src/tui/workflow/workflowPrompt.ts"],"sourcesContent":["/**\n * The short prompts: pick one of a few options, or type one value.\n *\n * These were `ui.select` and `ui.input`, which meant the run manager, the runner\n * picker and every `workflow_dispatch` input rendered as native Pi widgets in the\n * middle of a flow whose other steps are framed doom overlays. Same surface for\n * all of them now, so a launch reads as one sequence rather than three styles.\n *\n * Rendering is pure, so both modes can be asserted as text without a terminal.\n */\n\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey } 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 { CURSOR_BLOCK, fit, isControlInput, rightAligned, SELECTION_MARKER } from './overlayText';\n\nexport const WORKFLOW_PROMPT_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nconst CHOICE_TITLE = 'WORKFLOW PROMPT';\nconst CHOOSE_LABEL = 'CHOOSE';\nconst VALUE_LABEL = 'VALUE';\nconst DEFAULT_MARKER = 'default';\nconst EMPTY_CHOICES = 'No options are available.';\nconst CHOICE_HINTS: readonly (readonly [string, string])[] = [\n ['↑↓', 'select'],\n ['enter', 'confirm'],\n ['esc', 'cancel'],\n];\nconst INPUT_HINTS: readonly (readonly [string, string])[] = [\n ['type', 'edit'],\n ['enter', 'confirm'],\n ['ctrl+u', 'clear'],\n ['esc', 'cancel'],\n];\nconst KEY_CLEAR = '\\x15';\n/** Rows the heading and its spacer take before the first option. */\nconst HEADING_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\nconst DIM: ThemeColor = 'dim';\nconst ACCENT: ThemeColor = 'accent';\nconst SELECTED_BG: ThemeBg = 'selectedBg';\nconst ROW_BG: ThemeBg = 'userMessageBg';\n\nexport interface WorkflowChoiceOptions {\n title: string;\n breadcrumb: string;\n choices: readonly string[];\n /** Marked `default` in the list and pre-selected. */\n preselect?: string;\n}\n\nexport interface WorkflowInputOptions {\n title: string;\n breadcrumb: string;\n /** Committed when the field is left empty, and shown beside the label. */\n fallback?: string;\n}\n\nexport class WorkflowChoiceComponent extends DoomOverlay {\n private selected: number;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly options: WorkflowChoiceOptions,\n private readonly done: (choice: string | undefined) => void,\n ) {\n super(tui, theme);\n this.selected = Math.max(0, options.preselect ? options.choices.indexOf(options.preselect) : 0);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'enter')) {\n const choice = this.options.choices[this.selected];\n if (choice !== undefined) this.done(choice);\n return;\n }\n const count = this.options.choices.length;\n if (count === 0) return;\n if (matchesKey(data, 'up') || matchesKey(data, 'ctrl+p')) this.selected = Math.max(0, this.selected - 1);\n else if (matchesKey(data, 'down') || matchesKey(data, 'ctrl+n'))\n this.selected = Math.min(count - 1, this.selected + 1);\n else return;\n this.tui.requestRender();\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: CHOICE_TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: this.options.breadcrumb,\n headerRight: `${this.options.choices.length} options`,\n footer: '↑↓ select · enter confirm · esc cancel',\n footerHints: CHOICE_HINTS,\n footerRight:\n this.options.choices.length > 0 ? `${this.selected + 1}/${this.options.choices.length}` : 'no options',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n const lines = [this.theme.bold(fit(this.options.title, width)), ''];\n if (this.options.choices.length === 0) {\n lines.push(this.theme.fg(DIM, EMPTY_CHOICES));\n return lines.slice(0, height);\n }\n\n lines.push(this.theme.fg(DIM, CHOOSE_LABEL));\n // The window follows the cursor, so a list longer than the body still scrolls.\n const capacity = Math.max(1, height - HEADING_ROWS - 1);\n const start = Math.max(\n 0,\n Math.min(this.selected - capacity + 1, Math.max(0, this.options.choices.length - capacity)),\n );\n for (const [offset, choice] of this.options.choices.slice(start, start + capacity).entries()) {\n const current = start + offset === this.selected;\n const marker = current ? this.theme.fg(ACCENT, SELECTION_MARKER) : ' ';\n const label = current ? this.theme.bold(choice) : choice;\n const note = choice === this.options.preselect ? this.theme.fg(DIM, DEFAULT_MARKER) : '';\n const background: ThemeBg = current ? SELECTED_BG : ROW_BG;\n lines.push(this.theme.bg(background, rightAligned(`${marker} ${label}`, note, width)));\n }\n return lines.slice(0, height);\n }\n}\n\nexport class WorkflowInputComponent extends DoomOverlay {\n private draft = '';\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly options: WorkflowInputOptions,\n private readonly done: (value: string | undefined) => void,\n ) {\n super(tui, theme);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'enter')) {\n // An empty field commits the fallback rather than an empty string: the\n // caller offered a default, and pressing enter is how you accept it.\n this.done(this.draft.trim() || this.options.fallback || '');\n return;\n }\n if (matchesKey(data, 'backspace')) this.draft = this.draft.slice(0, -1);\n else if (data === KEY_CLEAR) this.draft = '';\n else if (!isControlInput(data)) this.draft += data;\n else return;\n this.tui.requestRender();\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: CHOICE_TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: this.options.breadcrumb,\n headerRight: this.options.fallback ? `default ${this.options.fallback}` : 'no default',\n footer: 'type to edit · enter confirm · ctrl+u clear · esc cancel',\n footerHints: INPUT_HINTS,\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n const label = this.options.fallback\n ? rightAligned(\n this.theme.fg(DIM, VALUE_LABEL),\n this.theme.fg(DIM, `${DEFAULT_MARKER} ${this.options.fallback}`),\n width,\n )\n : this.theme.fg(DIM, VALUE_LABEL);\n return [\n this.theme.bold(fit(this.options.title, width)),\n '',\n label,\n this.theme.bg(SELECTED_BG, fit(` ${this.draft}${CURSOR_BLOCK}`, width)),\n ].slice(0, height);\n }\n}\n\nexport async function openWorkflowChoice(\n ctx: ExtensionContext,\n options: WorkflowChoiceOptions,\n): Promise<string | undefined> {\n return ctx.ui.custom<string | undefined>(\n (tui, theme, _keybindings, done) => new WorkflowChoiceComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n\nexport async function openWorkflowInput(\n ctx: ExtensionContext,\n options: WorkflowInputOptions,\n): Promise<string | undefined> {\n return ctx.ui.custom<string | undefined>(\n (tui, theme, _keybindings, done) => new WorkflowInputComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{DOOM_FULLSCREEN_UI_OPTIONS as e,DOOM_OVERLAY_ACCENT as t,DoomOverlay as n}from"./doomOverlay.mjs";import{fit as r,isControlInput as i,rightAligned as a}from"./overlayText.mjs";import{matchesKey as o}from"@earendil-works/pi-tui";e.overlayOptions;const s=`WORKFLOW PROMPT`,c=`VALUE`,l=`default`,u=[[`↑↓`,`select`],[`enter`,`confirm`],[`esc`,`cancel`]],d=[[`type`,`edit`],[`enter`,`confirm`],[`ctrl+u`,`clear`],[`esc`,`cancel`]],f=`selectedBg`;var p=class extends n{options;done;selected;constructor(e,t,n,r){super(e,t),this.options=n,this.done=r,this.selected=Math.max(0,n.preselect?n.choices.indexOf(n.preselect):0)}handleInput(e){if(o(e,`escape`)||o(e,`ctrl+c`)){this.done(void 0);return}if(o(e,`enter`)){let e=this.options.choices[this.selected];e!==void 0&&this.done(e);return}let t=this.options.choices.length;if(t!==0){if(o(e,`up`)||o(e,`ctrl+p`))this.selected=Math.max(0,this.selected-1);else if(o(e,`down`)||o(e,`ctrl+n`))this.selected=Math.min(t-1,this.selected+1);else return;this.tui.requestRender()}}getChrome(){return{title:s,accent:t,breadcrumb:this.options.breadcrumb,headerRight:`${this.options.choices.length} options`,footer:`↑↓ select · enter confirm · esc cancel`,footerHints:u,footerRight:this.options.choices.length>0?`${this.selected+1}/${this.options.choices.length}`:`no options`}}renderBody(e,t){let n=[this.theme.bold(r(this.options.title,e)),``];if(this.options.choices.length===0)return n.push(this.theme.fg(`dim`,`No options are available.`)),n.slice(0,t);n.push(this.theme.fg(`dim`,`CHOOSE`));let i=Math.max(1,t-2-1),o=Math.max(0,Math.min(this.selected-i+1,Math.max(0,this.options.choices.length-i)));for(let[t,r]of this.options.choices.slice(o,o+i).entries()){let i=o+t===this.selected,s=i?this.theme.fg(`accent`,`›`):` `,c=i?this.theme.bold(r):r,u=r===this.options.preselect?this.theme.fg(`dim`,l):``,d=i?f:`userMessageBg`;n.push(this.theme.bg(d,a(`${s} ${c}`,u,e)))}return n.slice(0,t)}},m=class extends n{options;done;draft=``;constructor(e,t,n,r){super(e,t),this.options=n,this.done=r}handleInput(e){if(o(e,`escape`)||o(e,`ctrl+c`)){this.done(void 0);return}if(o(e,`enter`)){this.done(this.draft.trim()||this.options.fallback||``);return}if(o(e,`backspace`))this.draft=this.draft.slice(0,-1);else if(e===``)this.draft=``;else if(!i(e))this.draft+=e;else return;this.tui.requestRender()}getChrome(){return{title:s,accent:t,breadcrumb:this.options.breadcrumb,headerRight:this.options.fallback?`default ${this.options.fallback}`:`no default`,footer:`type to edit · enter confirm · ctrl+u clear · esc cancel`,footerHints:d}}renderBody(e,t){let n=this.options.fallback?a(this.theme.fg(`dim`,c),this.theme.fg(`dim`,`${l} ${this.options.fallback}`),e):this.theme.fg(`dim`,c);return[this.theme.bold(r(this.options.title,e)),``,n,this.theme.bg(f,r(` ${this.draft}█`,e))].slice(0,t)}};async function h(t,n){return t.ui.custom((e,t,r,i)=>new p(e,t,n,i),e)}async function g(t,n){return t.ui.custom((e,t,r,i)=>new m(e,t,n,i),e)}export{p as WorkflowChoiceComponent,m as WorkflowInputComponent,h as openWorkflowChoice,g as openWorkflowInput};
|
|
2
|
-
//# sourceMappingURL=workflowPrompt.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowPrompt.mjs","names":[],"sources":["../../../../src/tui/workflow/workflowPrompt.ts"],"sourcesContent":["/**\n * The short prompts: pick one of a few options, or type one value.\n *\n * These were `ui.select` and `ui.input`, which meant the run manager, the runner\n * picker and every `workflow_dispatch` input rendered as native Pi widgets in the\n * middle of a flow whose other steps are framed doom overlays. Same surface for\n * all of them now, so a launch reads as one sequence rather than three styles.\n *\n * Rendering is pure, so both modes can be asserted as text without a terminal.\n */\n\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey } 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 { CURSOR_BLOCK, fit, isControlInput, rightAligned, SELECTION_MARKER } from './overlayText';\n\nexport const WORKFLOW_PROMPT_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nconst CHOICE_TITLE = 'WORKFLOW PROMPT';\nconst CHOOSE_LABEL = 'CHOOSE';\nconst VALUE_LABEL = 'VALUE';\nconst DEFAULT_MARKER = 'default';\nconst EMPTY_CHOICES = 'No options are available.';\nconst CHOICE_HINTS: readonly (readonly [string, string])[] = [\n ['↑↓', 'select'],\n ['enter', 'confirm'],\n ['esc', 'cancel'],\n];\nconst INPUT_HINTS: readonly (readonly [string, string])[] = [\n ['type', 'edit'],\n ['enter', 'confirm'],\n ['ctrl+u', 'clear'],\n ['esc', 'cancel'],\n];\nconst KEY_CLEAR = '\\x15';\n/** Rows the heading and its spacer take before the first option. */\nconst HEADING_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\nconst DIM: ThemeColor = 'dim';\nconst ACCENT: ThemeColor = 'accent';\nconst SELECTED_BG: ThemeBg = 'selectedBg';\nconst ROW_BG: ThemeBg = 'userMessageBg';\n\nexport interface WorkflowChoiceOptions {\n title: string;\n breadcrumb: string;\n choices: readonly string[];\n /** Marked `default` in the list and pre-selected. */\n preselect?: string;\n}\n\nexport interface WorkflowInputOptions {\n title: string;\n breadcrumb: string;\n /** Committed when the field is left empty, and shown beside the label. */\n fallback?: string;\n}\n\nexport class WorkflowChoiceComponent extends DoomOverlay {\n private selected: number;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly options: WorkflowChoiceOptions,\n private readonly done: (choice: string | undefined) => void,\n ) {\n super(tui, theme);\n this.selected = Math.max(0, options.preselect ? options.choices.indexOf(options.preselect) : 0);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'enter')) {\n const choice = this.options.choices[this.selected];\n if (choice !== undefined) this.done(choice);\n return;\n }\n const count = this.options.choices.length;\n if (count === 0) return;\n if (matchesKey(data, 'up') || matchesKey(data, 'ctrl+p')) this.selected = Math.max(0, this.selected - 1);\n else if (matchesKey(data, 'down') || matchesKey(data, 'ctrl+n'))\n this.selected = Math.min(count - 1, this.selected + 1);\n else return;\n this.tui.requestRender();\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: CHOICE_TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: this.options.breadcrumb,\n headerRight: `${this.options.choices.length} options`,\n footer: '↑↓ select · enter confirm · esc cancel',\n footerHints: CHOICE_HINTS,\n footerRight:\n this.options.choices.length > 0 ? `${this.selected + 1}/${this.options.choices.length}` : 'no options',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n const lines = [this.theme.bold(fit(this.options.title, width)), ''];\n if (this.options.choices.length === 0) {\n lines.push(this.theme.fg(DIM, EMPTY_CHOICES));\n return lines.slice(0, height);\n }\n\n lines.push(this.theme.fg(DIM, CHOOSE_LABEL));\n // The window follows the cursor, so a list longer than the body still scrolls.\n const capacity = Math.max(1, height - HEADING_ROWS - 1);\n const start = Math.max(\n 0,\n Math.min(this.selected - capacity + 1, Math.max(0, this.options.choices.length - capacity)),\n );\n for (const [offset, choice] of this.options.choices.slice(start, start + capacity).entries()) {\n const current = start + offset === this.selected;\n const marker = current ? this.theme.fg(ACCENT, SELECTION_MARKER) : ' ';\n const label = current ? this.theme.bold(choice) : choice;\n const note = choice === this.options.preselect ? this.theme.fg(DIM, DEFAULT_MARKER) : '';\n const background: ThemeBg = current ? SELECTED_BG : ROW_BG;\n lines.push(this.theme.bg(background, rightAligned(`${marker} ${label}`, note, width)));\n }\n return lines.slice(0, height);\n }\n}\n\nexport class WorkflowInputComponent extends DoomOverlay {\n private draft = '';\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly options: WorkflowInputOptions,\n private readonly done: (value: string | undefined) => void,\n ) {\n super(tui, theme);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'enter')) {\n // An empty field commits the fallback rather than an empty string: the\n // caller offered a default, and pressing enter is how you accept it.\n this.done(this.draft.trim() || this.options.fallback || '');\n return;\n }\n if (matchesKey(data, 'backspace')) this.draft = this.draft.slice(0, -1);\n else if (data === KEY_CLEAR) this.draft = '';\n else if (!isControlInput(data)) this.draft += data;\n else return;\n this.tui.requestRender();\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: CHOICE_TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: this.options.breadcrumb,\n headerRight: this.options.fallback ? `default ${this.options.fallback}` : 'no default',\n footer: 'type to edit · enter confirm · ctrl+u clear · esc cancel',\n footerHints: INPUT_HINTS,\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n const label = this.options.fallback\n ? rightAligned(\n this.theme.fg(DIM, VALUE_LABEL),\n this.theme.fg(DIM, `${DEFAULT_MARKER} ${this.options.fallback}`),\n width,\n )\n : this.theme.fg(DIM, VALUE_LABEL);\n return [\n this.theme.bold(fit(this.options.title, width)),\n '',\n label,\n this.theme.bg(SELECTED_BG, fit(` ${this.draft}${CURSOR_BLOCK}`, width)),\n ].slice(0, height);\n }\n}\n\nexport async function openWorkflowChoice(\n ctx: ExtensionContext,\n options: WorkflowChoiceOptions,\n): Promise<string | undefined> {\n return ctx.ui.custom<string | undefined>(\n (tui, theme, _keybindings, done) => new WorkflowChoiceComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n\nexport async function openWorkflowInput(\n ctx: ExtensionContext,\n options: WorkflowInputOptions,\n): Promise<string | undefined> {\n return ctx.ui.custom<string | undefined>(\n (tui, theme, _keybindings, done) => new WorkflowInputComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("./doomOverlay.cjs"),t=require("./overlayText.cjs"),n=require("./workflowOverlay.cjs");let r=require("@earendil-works/pi-tui");var i=class extends e.DoomOverlay{options;escapes=new n.DoubleEscapeDetector;constructor(e,t,n){super(e,t),this.options=n}handleInput(e){if((0,r.matchesKey)(e,this.options.unfollowShortcut)){this.options.onUnfollow();return}if((0,r.matchesKey)(e,this.options.closeShortcut)){this.options.onClose();return}if(this.escapes.observe(e)===`close`){this.options.flushInput(),this.options.onClose();return}this.options.sendInput(e)}getChrome(){return{title:`WORKFLOW RUN`,accent:e.DOOM_OVERLAY_ACCENT,breadcrumb:this.options.breadcrumb,headerRight:this.options.interactive?this.options.label:`${this.options.label} · view only · this run is hosted natively`,footer:this.options.footer,footerHints:this.options.hints,footerRight:this.options.interactive?`typing goes to the run`:`typing stays here`}}renderBody(e,n){let{progress:r,output:i}=this.options.snapshot(e),a=r.slice(0,n).map(n=>t.fit(n,e)),o=n-a.length;if(o<=2)return a.slice(0,n);let s=o-2;this.options.onViewport?.(e,s);let c=i.slice(-s).map(n=>t.fitTerminalLine(n,e));return[...a,``,this.theme.fg(`dim`,`OUTPUT · newest last`),...c.length>0?c:[this.theme.fg(`dim`,`Waiting for output…`)]].slice(0,n)}dispose(){this.options.onDispose()}};function a(t){return{...e.DOOM_FULLSCREEN_UI_OPTIONS,overlayOptions:{...e.DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions,...t?{}:{nonCapturing:!0}}}}exports.WorkflowRunPanelComponent=i,exports.runPanelUiOptions=a;
|
|
2
|
-
//# sourceMappingURL=workflowRunPanel.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowRunPanel.cjs","names":["DoomOverlay","DoubleEscapeDetector","matchesKey","DOOM_OVERLAY_ACCENT","fit","fitTerminalLine","DOOM_FULLSCREEN_UI_OPTIONS"],"sources":["../../../../src/tui/workflow/workflowRunPanel.ts"],"sourcesContent":["/**\n * Run panel: the live view of one run's job tree and its terminal.\n *\n * A doom overlay rather than the bare header/body/footer it used to draw, so the\n * one surface a user sits inside for minutes at a time carries the same frame,\n * breadcrumb and key legend as every other. The base class owns the chrome and\n * hands the body an exact height, which also retires the hand-rolled row\n * arithmetic that existed only to stop Pi clipping the footer.\n *\n * Key routing is the reason this is a component rather than a renderer: a\n * focused panel receives input before Pi's global dispatcher, so the view\n * controls have to be handled here or the run's TTY swallows them.\n */\n\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { type KeyId, matchesKey } 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 { fit, fitTerminalLine } from './overlayText';\nimport { DoubleEscapeDetector } from './workflowOverlay';\n\nconst TITLE = 'WORKFLOW RUN';\nconst VIEW_ONLY = 'view only · this run is hosted natively';\nconst OUTPUT_HEADING = 'OUTPUT · newest last';\nconst WAITING = 'Waiting for output…';\n/** A divider and its heading earn their rows only when output sits beneath them. */\nconst OUTPUT_CHROME_ROWS = 2;\n\nexport interface WorkflowRunPanelSnapshot {\n /** Job tree lines, already themed by the caller. */\n progress: string[];\n /** Recent terminal output for the running step. */\n output: string[];\n}\n\nexport interface WorkflowRunPanelOptions {\n runKey: string;\n /** Full run label for the header, e.g. `name · workspace/runKey`. */\n label: string;\n breadcrumb: string;\n /** False for a natively hosted run, which has no terminal to type into. */\n interactive: boolean;\n /** Plain-sentence footer, used when the terminal cannot fit the key caps. */\n footer: string;\n /** Key legend, which differs between a typeable panel and a view. */\n hints: readonly (readonly [string, string])[];\n /** Given the body width, because the job tree is fitted as it is built. */\n snapshot: (width: number) => WorkflowRunPanelSnapshot;\n /**\n * The columns and rows the output pane can actually show, reported on each\n * paint so a launcher that supports resizing can match the run to them.\n * Called from render, so an implementation must not do the work inline: the\n * run's geometry changes out of band and the next poll picks up the reflow.\n */\n onViewport?: (columns: number, rows: number) => void;\n /** Hand typing back to the session, leaving the panel open. */\n onUnfollow: () => void;\n onClose: () => void;\n /** Forward a keystroke to the run. */\n sendInput: (data: string) => void;\n /**\n * Send whatever is buffered before the panel goes away, or the Escape that\n * closed it dies with the batcher.\n */\n flushInput: () => void;\n unfollowShortcut: KeyId;\n closeShortcut: KeyId;\n onDispose: () => void;\n}\n\nexport class WorkflowRunPanelComponent extends DoomOverlay {\n private readonly escapes = new DoubleEscapeDetector();\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly options: WorkflowRunPanelOptions,\n ) {\n super(tui, theme);\n }\n\n /**\n * The chords are matched against the whole chunk, as Pi delivers one per key\n * event. A chord buried in a coalesced burst therefore misses, which is\n * acceptable now that double-Escape guarantees an exit: scanning for the bytes\n * anywhere in a chunk would instead eat legitimate input, since `\\x1b\\x17` is\n * also a valid Escape-then-ctrl+w for the run's own editor.\n */\n handleInput(data: string): void {\n if (matchesKey(data, this.options.unfollowShortcut)) {\n this.options.onUnfollow();\n return;\n }\n if (matchesKey(data, this.options.closeShortcut)) {\n this.options.onClose();\n return;\n }\n // The failsafe. The first Escape falls through to the run, so interrupting\n // the step is as fast as it ever was; only the second one inside the window\n // is consumed.\n if (this.escapes.observe(data) === 'close') {\n this.options.flushInput();\n this.options.onClose();\n return;\n }\n this.options.sendInput(data);\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: this.options.breadcrumb,\n headerRight: this.options.interactive ? this.options.label : `${this.options.label} · ${VIEW_ONLY}`,\n footer: this.options.footer,\n footerHints: this.options.hints,\n footerRight: this.options.interactive ? 'typing goes to the run' : 'typing stays here',\n };\n }\n\n /**\n * The tree is never truncated: knowing which job is running matters more than\n * seeing another line of output, so the terminal pane absorbs whatever the\n * tree does not use.\n */\n protected renderBody(width: number, height: number): string[] {\n const { progress, output } = this.options.snapshot(width);\n const tree = progress.slice(0, height).map((line) => fit(line, width));\n\n const spare = height - tree.length;\n if (spare <= OUTPUT_CHROME_ROWS) return tree.slice(0, height);\n\n const rows = spare - OUTPUT_CHROME_ROWS;\n this.options.onViewport?.(width, rows);\n // Output keeps its own colour, so it is fitted by the escape-preserving\n // path rather than the themed-row one; see `fitTerminalLine`.\n const tail = output.slice(-rows).map((line) => fitTerminalLine(line, width));\n return [\n ...tree,\n '',\n this.theme.fg('dim', OUTPUT_HEADING),\n ...(tail.length > 0 ? tail : [this.theme.fg('dim', WAITING)]),\n ].slice(0, height);\n }\n\n dispose(): void {\n this.options.onDispose();\n }\n}\n\n/**\n * A panel whose keys cannot reach anything must never take them. Pi focuses an\n * overlay on show unless told otherwise, which for a native run would swallow\n * the user's typing into a run with no terminal to receive it.\n */\nexport function runPanelUiOptions(interactive: boolean): Record<string, unknown> {\n return {\n ...DOOM_FULLSCREEN_UI_OPTIONS,\n overlayOptions: {\n ...DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions,\n ...(interactive ? {} : { nonCapturing: true }),\n },\n };\n}\n\nexport type WorkflowRunPanelHost = Pick<ExtensionContext['ui'], 'custom'>;\n"],"mappings":"+IA4EA,IAAa,EAAb,cAA+CA,EAAAA,WAAY,CAMtC,QALnB,QAA2B,IAAIC,EAAAA,qBAE/B,YACE,EACA,EACA,EACA,CACA,MAAM,EAAK,CAAK,EAFC,KAAA,QAAA,CAGnB,CASA,YAAY,EAAoB,CAC9B,IAAA,EAAIC,EAAAA,WAAAA,CAAW,EAAM,KAAK,QAAQ,gBAAgB,EAAG,CACnD,KAAK,QAAQ,WAAW,EACxB,MACF,CACA,IAAA,EAAIA,EAAAA,WAAAA,CAAW,EAAM,KAAK,QAAQ,aAAa,EAAG,CAChD,KAAK,QAAQ,QAAQ,EACrB,MACF,CAIA,GAAI,KAAK,QAAQ,QAAQ,CAAI,IAAM,QAAS,CAC1C,KAAK,QAAQ,WAAW,EACxB,KAAK,QAAQ,QAAQ,EACrB,MACF,CACA,KAAK,QAAQ,UAAU,CAAI,CAC7B,CAEA,WAAyC,CACvC,MAAO,CACL,MAAO,eACP,OAAQC,EAAAA,oBACR,WAAY,KAAK,QAAQ,WACzB,YAAa,KAAK,QAAQ,YAAc,KAAK,QAAQ,MAAQ,GAAG,KAAK,QAAQ,MAAM,4CACnF,OAAQ,KAAK,QAAQ,OACrB,YAAa,KAAK,QAAQ,MAC1B,YAAa,KAAK,QAAQ,YAAc,yBAA2B,mBACrE,CACF,CAOA,WAAqB,EAAe,EAA0B,CAC5D,GAAM,CAAE,WAAU,UAAW,KAAK,QAAQ,SAAS,CAAK,EAClD,EAAO,EAAS,MAAM,EAAG,CAAM,CAAC,CAAC,IAAK,GAASC,EAAAA,IAAI,EAAM,CAAK,CAAC,EAE/D,EAAQ,EAAS,EAAK,OAC5B,GAAI,GAAS,EAAoB,OAAO,EAAK,MAAM,EAAG,CAAM,EAE5D,IAAM,EAAO,EAAQ,EACrB,KAAK,QAAQ,aAAa,EAAO,CAAI,EAGrC,IAAM,EAAO,EAAO,MAAM,CAAC,CAAI,CAAC,CAAC,IAAK,GAASC,EAAAA,gBAAgB,EAAM,CAAK,CAAC,EAC3E,MAAO,CACL,GAAG,EACH,GACA,KAAK,MAAM,GAAG,MAAO,sBAAc,EACnC,GAAI,EAAK,OAAS,EAAI,EAAO,CAAC,KAAK,MAAM,GAAG,MAAO,qBAAO,CAAC,CAC7D,CAAC,CAAC,MAAM,EAAG,CAAM,CACnB,CAEA,SAAgB,CACd,KAAK,QAAQ,UAAU,CACzB,CACF,EAOA,SAAgB,EAAkB,EAA+C,CAC/E,MAAO,CACL,GAAGC,EAAAA,2BACH,eAAgB,CACd,GAAGA,EAAAA,2BAA2B,eAC9B,GAAI,EAAc,CAAC,EAAI,CAAE,aAAc,EAAK,CAC9C,CACF,CACF"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{DOOM_FULLSCREEN_UI_OPTIONS as e,DOOM_OVERLAY_ACCENT as t,DoomOverlay as n}from"./doomOverlay.mjs";import{fit as r,fitTerminalLine as i}from"./overlayText.mjs";import{DoubleEscapeDetector as a}from"./workflowOverlay.mjs";import{matchesKey as o}from"@earendil-works/pi-tui";var s=class extends n{options;escapes=new a;constructor(e,t,n){super(e,t),this.options=n}handleInput(e){if(o(e,this.options.unfollowShortcut)){this.options.onUnfollow();return}if(o(e,this.options.closeShortcut)){this.options.onClose();return}if(this.escapes.observe(e)===`close`){this.options.flushInput(),this.options.onClose();return}this.options.sendInput(e)}getChrome(){return{title:`WORKFLOW RUN`,accent:t,breadcrumb:this.options.breadcrumb,headerRight:this.options.interactive?this.options.label:`${this.options.label} · view only · this run is hosted natively`,footer:this.options.footer,footerHints:this.options.hints,footerRight:this.options.interactive?`typing goes to the run`:`typing stays here`}}renderBody(e,t){let{progress:n,output:a}=this.options.snapshot(e),o=n.slice(0,t).map(t=>r(t,e)),s=t-o.length;if(s<=2)return o.slice(0,t);let c=s-2;this.options.onViewport?.(e,c);let l=a.slice(-c).map(t=>i(t,e));return[...o,``,this.theme.fg(`dim`,`OUTPUT · newest last`),...l.length>0?l:[this.theme.fg(`dim`,`Waiting for output…`)]].slice(0,t)}dispose(){this.options.onDispose()}};function c(t){return{...e,overlayOptions:{...e.overlayOptions,...t?{}:{nonCapturing:!0}}}}export{s as WorkflowRunPanelComponent,c as runPanelUiOptions};
|
|
2
|
-
//# sourceMappingURL=workflowRunPanel.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowRunPanel.mjs","names":[],"sources":["../../../../src/tui/workflow/workflowRunPanel.ts"],"sourcesContent":["/**\n * Run panel: the live view of one run's job tree and its terminal.\n *\n * A doom overlay rather than the bare header/body/footer it used to draw, so the\n * one surface a user sits inside for minutes at a time carries the same frame,\n * breadcrumb and key legend as every other. The base class owns the chrome and\n * hands the body an exact height, which also retires the hand-rolled row\n * arithmetic that existed only to stop Pi clipping the footer.\n *\n * Key routing is the reason this is a component rather than a renderer: a\n * focused panel receives input before Pi's global dispatcher, so the view\n * controls have to be handled here or the run's TTY swallows them.\n */\n\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { type KeyId, matchesKey } 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 { fit, fitTerminalLine } from './overlayText';\nimport { DoubleEscapeDetector } from './workflowOverlay';\n\nconst TITLE = 'WORKFLOW RUN';\nconst VIEW_ONLY = 'view only · this run is hosted natively';\nconst OUTPUT_HEADING = 'OUTPUT · newest last';\nconst WAITING = 'Waiting for output…';\n/** A divider and its heading earn their rows only when output sits beneath them. */\nconst OUTPUT_CHROME_ROWS = 2;\n\nexport interface WorkflowRunPanelSnapshot {\n /** Job tree lines, already themed by the caller. */\n progress: string[];\n /** Recent terminal output for the running step. */\n output: string[];\n}\n\nexport interface WorkflowRunPanelOptions {\n runKey: string;\n /** Full run label for the header, e.g. `name · workspace/runKey`. */\n label: string;\n breadcrumb: string;\n /** False for a natively hosted run, which has no terminal to type into. */\n interactive: boolean;\n /** Plain-sentence footer, used when the terminal cannot fit the key caps. */\n footer: string;\n /** Key legend, which differs between a typeable panel and a view. */\n hints: readonly (readonly [string, string])[];\n /** Given the body width, because the job tree is fitted as it is built. */\n snapshot: (width: number) => WorkflowRunPanelSnapshot;\n /**\n * The columns and rows the output pane can actually show, reported on each\n * paint so a launcher that supports resizing can match the run to them.\n * Called from render, so an implementation must not do the work inline: the\n * run's geometry changes out of band and the next poll picks up the reflow.\n */\n onViewport?: (columns: number, rows: number) => void;\n /** Hand typing back to the session, leaving the panel open. */\n onUnfollow: () => void;\n onClose: () => void;\n /** Forward a keystroke to the run. */\n sendInput: (data: string) => void;\n /**\n * Send whatever is buffered before the panel goes away, or the Escape that\n * closed it dies with the batcher.\n */\n flushInput: () => void;\n unfollowShortcut: KeyId;\n closeShortcut: KeyId;\n onDispose: () => void;\n}\n\nexport class WorkflowRunPanelComponent extends DoomOverlay {\n private readonly escapes = new DoubleEscapeDetector();\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly options: WorkflowRunPanelOptions,\n ) {\n super(tui, theme);\n }\n\n /**\n * The chords are matched against the whole chunk, as Pi delivers one per key\n * event. A chord buried in a coalesced burst therefore misses, which is\n * acceptable now that double-Escape guarantees an exit: scanning for the bytes\n * anywhere in a chunk would instead eat legitimate input, since `\\x1b\\x17` is\n * also a valid Escape-then-ctrl+w for the run's own editor.\n */\n handleInput(data: string): void {\n if (matchesKey(data, this.options.unfollowShortcut)) {\n this.options.onUnfollow();\n return;\n }\n if (matchesKey(data, this.options.closeShortcut)) {\n this.options.onClose();\n return;\n }\n // The failsafe. The first Escape falls through to the run, so interrupting\n // the step is as fast as it ever was; only the second one inside the window\n // is consumed.\n if (this.escapes.observe(data) === 'close') {\n this.options.flushInput();\n this.options.onClose();\n return;\n }\n this.options.sendInput(data);\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: this.options.breadcrumb,\n headerRight: this.options.interactive ? this.options.label : `${this.options.label} · ${VIEW_ONLY}`,\n footer: this.options.footer,\n footerHints: this.options.hints,\n footerRight: this.options.interactive ? 'typing goes to the run' : 'typing stays here',\n };\n }\n\n /**\n * The tree is never truncated: knowing which job is running matters more than\n * seeing another line of output, so the terminal pane absorbs whatever the\n * tree does not use.\n */\n protected renderBody(width: number, height: number): string[] {\n const { progress, output } = this.options.snapshot(width);\n const tree = progress.slice(0, height).map((line) => fit(line, width));\n\n const spare = height - tree.length;\n if (spare <= OUTPUT_CHROME_ROWS) return tree.slice(0, height);\n\n const rows = spare - OUTPUT_CHROME_ROWS;\n this.options.onViewport?.(width, rows);\n // Output keeps its own colour, so it is fitted by the escape-preserving\n // path rather than the themed-row one; see `fitTerminalLine`.\n const tail = output.slice(-rows).map((line) => fitTerminalLine(line, width));\n return [\n ...tree,\n '',\n this.theme.fg('dim', OUTPUT_HEADING),\n ...(tail.length > 0 ? tail : [this.theme.fg('dim', WAITING)]),\n ].slice(0, height);\n }\n\n dispose(): void {\n this.options.onDispose();\n }\n}\n\n/**\n * A panel whose keys cannot reach anything must never take them. Pi focuses an\n * overlay on show unless told otherwise, which for a native run would swallow\n * the user's typing into a run with no terminal to receive it.\n */\nexport function runPanelUiOptions(interactive: boolean): Record<string, unknown> {\n return {\n ...DOOM_FULLSCREEN_UI_OPTIONS,\n overlayOptions: {\n ...DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions,\n ...(interactive ? {} : { nonCapturing: true }),\n },\n };\n}\n\nexport type WorkflowRunPanelHost = Pick<ExtensionContext['ui'], 'custom'>;\n"],"mappings":"uRA4EA,IAAa,EAAb,cAA+C,CAAY,CAMtC,QALnB,QAA2B,IAAI,EAE/B,YACE,EACA,EACA,EACA,CACA,MAAM,EAAK,CAAK,EAFC,KAAA,QAAA,CAGnB,CASA,YAAY,EAAoB,CAC9B,GAAI,EAAW,EAAM,KAAK,QAAQ,gBAAgB,EAAG,CACnD,KAAK,QAAQ,WAAW,EACxB,MACF,CACA,GAAI,EAAW,EAAM,KAAK,QAAQ,aAAa,EAAG,CAChD,KAAK,QAAQ,QAAQ,EACrB,MACF,CAIA,GAAI,KAAK,QAAQ,QAAQ,CAAI,IAAM,QAAS,CAC1C,KAAK,QAAQ,WAAW,EACxB,KAAK,QAAQ,QAAQ,EACrB,MACF,CACA,KAAK,QAAQ,UAAU,CAAI,CAC7B,CAEA,WAAyC,CACvC,MAAO,CACL,MAAO,eACP,OAAQ,EACR,WAAY,KAAK,QAAQ,WACzB,YAAa,KAAK,QAAQ,YAAc,KAAK,QAAQ,MAAQ,GAAG,KAAK,QAAQ,MAAM,4CACnF,OAAQ,KAAK,QAAQ,OACrB,YAAa,KAAK,QAAQ,MAC1B,YAAa,KAAK,QAAQ,YAAc,yBAA2B,mBACrE,CACF,CAOA,WAAqB,EAAe,EAA0B,CAC5D,GAAM,CAAE,WAAU,UAAW,KAAK,QAAQ,SAAS,CAAK,EAClD,EAAO,EAAS,MAAM,EAAG,CAAM,CAAC,CAAC,IAAK,GAAS,EAAI,EAAM,CAAK,CAAC,EAE/D,EAAQ,EAAS,EAAK,OAC5B,GAAI,GAAS,EAAoB,OAAO,EAAK,MAAM,EAAG,CAAM,EAE5D,IAAM,EAAO,EAAQ,EACrB,KAAK,QAAQ,aAAa,EAAO,CAAI,EAGrC,IAAM,EAAO,EAAO,MAAM,CAAC,CAAI,CAAC,CAAC,IAAK,GAAS,EAAgB,EAAM,CAAK,CAAC,EAC3E,MAAO,CACL,GAAG,EACH,GACA,KAAK,MAAM,GAAG,MAAO,sBAAc,EACnC,GAAI,EAAK,OAAS,EAAI,EAAO,CAAC,KAAK,MAAM,GAAG,MAAO,qBAAO,CAAC,CAC7D,CAAC,CAAC,MAAM,EAAG,CAAM,CACnB,CAEA,SAAgB,CACd,KAAK,QAAQ,UAAU,CACzB,CACF,EAOA,SAAgB,EAAkB,EAA+C,CAC/E,MAAO,CACL,GAAG,EACH,eAAgB,CACd,GAAG,EAA2B,eAC9B,GAAI,EAAc,CAAC,EAAI,CAAE,aAAc,EAAK,CAC9C,CACF,CACF"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
function e(e){if(!Number.isFinite(e)||e<0)return`?`;if(e<1e3)return`${e}ms`;let t=Math.round(e/1e3);if(t<60)return`${t}s`;let n=Math.floor(t/60);return n<60?`${n}m${String(t%60).padStart(2,`0`)}s`:`${Math.floor(n/60)}h${String(n%60).padStart(2,`0`)}m`}exports.humanizeDuration=e;
|
|
2
|
-
//# sourceMappingURL=workflowStatusRow.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowStatusRow.cjs","names":[],"sources":["../../../../src/tui/workflow/workflowStatusRow.ts"],"sourcesContent":["/**\n * Session reporting for a run's progress log.\n *\n * DESIGN PATTERNS:\n * - Pure derivation. Takes a run's raw progress events and returns report lines,\n * so the behaviour is unit-testable without a registry or a live run.\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported members\n *\n * Live progress now renders through `workflowProgressOverlay.ts`. This module\n * remains the compatibility derivation used by terminal summaries and legacy\n * workflow-step cards already persisted in older sessions.\n */\n\nimport type { WorkflowProgressEvent } from '@agimon-ai/workflow-mcp';\n\n/** Step names come from workflow YAML and can be sentences. */\nconst MAX_STEP_LABEL = 48;\n\n/** One session line, plus the key that stops it being reported twice. */\nexport interface StepReport {\n duration?: string;\n job: string;\n key: string;\n status: 'FAILED' | 'FINISHED' | 'STARTED';\n step: string;\n}\n\nfunction truncateStep(step: string): string {\n return step.length <= MAX_STEP_LABEL ? step : `${step.slice(0, MAX_STEP_LABEL - 1)}…`;\n}\n\n/**\n * Human-scale duration.\n *\n * Rounded hard on purpose: this is read at a glance next to a step name, where\n * milliseconds are noise and \"4m12s\" answers the only question being asked.\n */\nexport function humanizeDuration(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) return '?';\n if (ms < 1000) return `${ms}ms`;\n const seconds = Math.round(ms / 1000);\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m${String(seconds % 60).padStart(2, '0')}s`;\n return `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, '0')}m`;\n}\n\n/**\n * Turn a run's raw progress log into session report lines.\n *\n * Derives durations by pairing each step's start with its end rather than\n * recording a duration in the log, so nothing about the log format has to\n * change and old runs read back the same way.\n *\n * Keyed by the event's own timestamp so a caller polling the same append-only\n * file repeatedly reports each transition exactly once, however often it polls.\n */\nexport function stepReportLines(runKey: string, events: WorkflowProgressEvent[]): StepReport[] {\n const startedAt = new Map<string, string>();\n const reports: StepReport[] = [];\n\n for (const event of events) {\n if (event.type !== 'step' || !event.step) continue;\n const id = `${runKey}/${event.job}`;\n const stepKey = `${id}/${event.step}`;\n const label = truncateStep(event.step);\n\n if (event.status === 'running') {\n startedAt.set(stepKey, event.at);\n reports.push({ job: event.job, key: `started:${stepKey}:${event.at}`, status: 'STARTED', step: label });\n continue;\n }\n\n const began = startedAt.get(stepKey);\n const duration = began ? humanizeDuration(Date.parse(event.at) - Date.parse(began)) : '?';\n // A failed step did finish, but calling it FINISHED buries the one outcome\n // the reader needs to see. Same shape, honest verb.\n const status = event.status === 'failed' ? 'FAILED' : 'FINISHED';\n reports.push({ duration, job: event.job, key: `${status}:${stepKey}:${event.at}`, status, step: label });\n }\n\n return reports;\n}\n"],"mappings":"AAwCA,SAAgB,EAAiB,EAAoB,CACnD,GAAI,CAAC,OAAO,SAAS,CAAE,GAAK,EAAK,EAAG,MAAO,IAC3C,GAAI,EAAK,IAAM,MAAO,GAAG,EAAG,IAC5B,IAAM,EAAU,KAAK,MAAM,EAAK,GAAI,EACpC,GAAI,EAAU,GAAI,MAAO,GAAG,EAAQ,GACpC,IAAM,EAAU,KAAK,MAAM,EAAU,EAAE,EAEvC,OADI,EAAU,GAAW,GAAG,EAAQ,GAAG,OAAO,EAAU,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,GACtE,GAAG,KAAK,MAAM,EAAU,EAAE,EAAE,GAAG,OAAO,EAAU,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,EAC9E"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
function e(e){if(!Number.isFinite(e)||e<0)return`?`;if(e<1e3)return`${e}ms`;let t=Math.round(e/1e3);if(t<60)return`${t}s`;let n=Math.floor(t/60);return n<60?`${n}m${String(t%60).padStart(2,`0`)}s`:`${Math.floor(n/60)}h${String(n%60).padStart(2,`0`)}m`}export{e as humanizeDuration};
|
|
2
|
-
//# sourceMappingURL=workflowStatusRow.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowStatusRow.mjs","names":[],"sources":["../../../../src/tui/workflow/workflowStatusRow.ts"],"sourcesContent":["/**\n * Session reporting for a run's progress log.\n *\n * DESIGN PATTERNS:\n * - Pure derivation. Takes a run's raw progress events and returns report lines,\n * so the behaviour is unit-testable without a registry or a live run.\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported members\n *\n * Live progress now renders through `workflowProgressOverlay.ts`. This module\n * remains the compatibility derivation used by terminal summaries and legacy\n * workflow-step cards already persisted in older sessions.\n */\n\nimport type { WorkflowProgressEvent } from '@agimon-ai/workflow-mcp';\n\n/** Step names come from workflow YAML and can be sentences. */\nconst MAX_STEP_LABEL = 48;\n\n/** One session line, plus the key that stops it being reported twice. */\nexport interface StepReport {\n duration?: string;\n job: string;\n key: string;\n status: 'FAILED' | 'FINISHED' | 'STARTED';\n step: string;\n}\n\nfunction truncateStep(step: string): string {\n return step.length <= MAX_STEP_LABEL ? step : `${step.slice(0, MAX_STEP_LABEL - 1)}…`;\n}\n\n/**\n * Human-scale duration.\n *\n * Rounded hard on purpose: this is read at a glance next to a step name, where\n * milliseconds are noise and \"4m12s\" answers the only question being asked.\n */\nexport function humanizeDuration(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) return '?';\n if (ms < 1000) return `${ms}ms`;\n const seconds = Math.round(ms / 1000);\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m${String(seconds % 60).padStart(2, '0')}s`;\n return `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, '0')}m`;\n}\n\n/**\n * Turn a run's raw progress log into session report lines.\n *\n * Derives durations by pairing each step's start with its end rather than\n * recording a duration in the log, so nothing about the log format has to\n * change and old runs read back the same way.\n *\n * Keyed by the event's own timestamp so a caller polling the same append-only\n * file repeatedly reports each transition exactly once, however often it polls.\n */\nexport function stepReportLines(runKey: string, events: WorkflowProgressEvent[]): StepReport[] {\n const startedAt = new Map<string, string>();\n const reports: StepReport[] = [];\n\n for (const event of events) {\n if (event.type !== 'step' || !event.step) continue;\n const id = `${runKey}/${event.job}`;\n const stepKey = `${id}/${event.step}`;\n const label = truncateStep(event.step);\n\n if (event.status === 'running') {\n startedAt.set(stepKey, event.at);\n reports.push({ job: event.job, key: `started:${stepKey}:${event.at}`, status: 'STARTED', step: label });\n continue;\n }\n\n const began = startedAt.get(stepKey);\n const duration = began ? humanizeDuration(Date.parse(event.at) - Date.parse(began)) : '?';\n // A failed step did finish, but calling it FINISHED buries the one outcome\n // the reader needs to see. Same shape, honest verb.\n const status = event.status === 'failed' ? 'FAILED' : 'FINISHED';\n reports.push({ duration, job: event.job, key: `${status}:${stepKey}:${event.at}`, status, step: label });\n }\n\n return reports;\n}\n"],"mappings":"AAwCA,SAAgB,EAAiB,EAAoB,CACnD,GAAI,CAAC,OAAO,SAAS,CAAE,GAAK,EAAK,EAAG,MAAO,IAC3C,GAAI,EAAK,IAAM,MAAO,GAAG,EAAG,IAC5B,IAAM,EAAU,KAAK,MAAM,EAAK,GAAI,EACpC,GAAI,EAAU,GAAI,MAAO,GAAG,EAAQ,GACpC,IAAM,EAAU,KAAK,MAAM,EAAU,EAAE,EAEvC,OADI,EAAU,GAAW,GAAG,EAAQ,GAAG,OAAO,EAAU,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,GACtE,GAAG,KAAK,MAAM,EAAU,EAAE,EAAE,GAAG,OAAO,EAAU,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,EAC9E"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
let e=require("@earendil-works/pi-tui");function t(e){if(!e||typeof e!=`object`||Array.isArray(e))return!1;let t=e;return typeof t.displayName==`string`&&typeof t.job==`string`&&typeof t.step==`string`&&(t.status===`STARTED`||t.status===`FINISHED`||t.status===`FAILED`)&&(t.duration===void 0||typeof t.duration==`string`)}var n=class{details;theme;constructor(e,t){this.details=e,this.theme=t}render(t){let n=`${this.theme.fg(`accent`,`[${this.details.displayName}]:`)} ${this.details.job}`,r=this.details.duration?`${this.details.status}::${this.details.duration}::${this.details.step}`:`${this.details.status}::${this.details.step}`,i=this.details.status===`FAILED`?`error`:this.details.status===`FINISHED`?`success`:`accent`;return[(0,e.truncateToWidth)(n,t),(0,e.truncateToWidth)(this.theme.fg(i,r),t)]}invalidate(){}};function r(t,r,i,a){let o=new e.Box(i,1,e=>a.bg(`customMessageBg`,e));return o.addChild(t?new n(t,a):new e.Text(r,0,0)),o}exports.isWorkflowStepMessageDetails=t,exports.renderWorkflowStepMessage=r;
|
|
2
|
-
//# sourceMappingURL=workflowStepMessage.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowStepMessage.cjs","names":["truncateToWidth","Box","Text"],"sources":["../../../../src/tui/workflow/workflowStepMessage.ts"],"sourcesContent":["import type { Theme } from '@earendil-works/pi-coding-agent';\nimport { Box, type Component, Text, truncateToWidth } from '@earendil-works/pi-tui';\n\nimport type { StepReport } from './workflowStatusRow';\n\nexport interface WorkflowStepMessageDetails {\n displayName: string;\n duration?: string;\n job: string;\n status: StepReport['status'];\n step: string;\n}\n\nexport function workflowStepMessageDetails(displayName: string, report: StepReport): WorkflowStepMessageDetails {\n return {\n displayName,\n job: report.job,\n status: report.status,\n step: report.step,\n ...(report.duration ? { duration: report.duration } : {}),\n };\n}\n\nexport function formatWorkflowStepMessage(details: WorkflowStepMessageDetails): string {\n const transition = details.duration\n ? `${details.status}::${details.duration}::${details.step}`\n : `${details.status}::${details.step}`;\n return `[${details.displayName}]: ${details.job}\\n${transition}`;\n}\n\nexport function isWorkflowStepMessageDetails(value: unknown): value is WorkflowStepMessageDetails {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const candidate = value as Partial<WorkflowStepMessageDetails>;\n return (\n typeof candidate.displayName === 'string' &&\n typeof candidate.job === 'string' &&\n typeof candidate.step === 'string' &&\n (candidate.status === 'STARTED' || candidate.status === 'FINISHED' || candidate.status === 'FAILED') &&\n (candidate.duration === undefined || typeof candidate.duration === 'string')\n );\n}\n\nclass WorkflowStepMessageLines implements Component {\n constructor(\n private readonly details: WorkflowStepMessageDetails,\n private readonly theme: Theme,\n ) {}\n\n render(width: number): string[] {\n const header = `${this.theme.fg('accent', `[${this.details.displayName}]:`)} ${this.details.job}`;\n const transition = this.details.duration\n ? `${this.details.status}::${this.details.duration}::${this.details.step}`\n : `${this.details.status}::${this.details.step}`;\n const colour =\n this.details.status === 'FAILED' ? 'error' : this.details.status === 'FINISHED' ? 'success' : 'accent';\n return [truncateToWidth(header, width), truncateToWidth(this.theme.fg(colour, transition), width)];\n }\n\n invalidate(): void {}\n}\n\nexport function renderWorkflowStepMessage(\n details: WorkflowStepMessageDetails | undefined,\n fallback: string,\n outputPad: number,\n theme: Theme,\n): Component {\n const box = new Box(outputPad, 1, (text) => theme.bg('customMessageBg', text));\n box.addChild(details ? new WorkflowStepMessageLines(details, theme) : new Text(fallback, 0, 0));\n return box;\n}\n"],"mappings":"wCA8BA,SAAgB,EAA6B,EAAqD,CAChG,GAAI,CAAC,GAAS,OAAO,GAAU,UAAY,MAAM,QAAQ,CAAK,EAAG,MAAO,GACxE,IAAM,EAAY,EAClB,OACE,OAAO,EAAU,aAAgB,UACjC,OAAO,EAAU,KAAQ,UACzB,OAAO,EAAU,MAAS,WACzB,EAAU,SAAW,WAAa,EAAU,SAAW,YAAc,EAAU,SAAW,YAC1F,EAAU,WAAa,IAAA,IAAa,OAAO,EAAU,UAAa,SAEvE,CAEA,IAAM,EAAN,KAAoD,CAE/B,QACA,MAFnB,YACE,EACA,EACA,CAFiB,KAAA,QAAA,EACA,KAAA,MAAA,CAChB,CAEH,OAAO,EAAyB,CAC9B,IAAM,EAAS,GAAG,KAAK,MAAM,GAAG,SAAU,IAAI,KAAK,QAAQ,YAAY,GAAG,EAAE,GAAG,KAAK,QAAQ,MACtF,EAAa,KAAK,QAAQ,SAC5B,GAAG,KAAK,QAAQ,OAAO,IAAI,KAAK,QAAQ,SAAS,IAAI,KAAK,QAAQ,OAClE,GAAG,KAAK,QAAQ,OAAO,IAAI,KAAK,QAAQ,OACtC,EACJ,KAAK,QAAQ,SAAW,SAAW,QAAU,KAAK,QAAQ,SAAW,WAAa,UAAY,SAChG,MAAO,EAAA,EAACA,EAAAA,gBAAAA,CAAgB,EAAQ,CAAK,GAAA,EAAGA,EAAAA,gBAAAA,CAAgB,KAAK,MAAM,GAAG,EAAQ,CAAU,EAAG,CAAK,CAAC,CACnG,CAEA,YAAmB,CAAC,CACtB,EAEA,SAAgB,EACd,EACA,EACA,EACA,EACW,CACX,IAAM,EAAM,IAAIC,EAAAA,IAAI,EAAW,EAAI,GAAS,EAAM,GAAG,kBAAmB,CAAI,CAAC,EAE7E,OADA,EAAI,SAAS,EAAU,IAAI,EAAyB,EAAS,CAAK,EAAI,IAAIC,EAAAA,KAAK,EAAU,EAAG,CAAC,CAAC,EACvF,CACT"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{Box as e,Text as t,truncateToWidth as n}from"@earendil-works/pi-tui";function r(e){if(!e||typeof e!=`object`||Array.isArray(e))return!1;let t=e;return typeof t.displayName==`string`&&typeof t.job==`string`&&typeof t.step==`string`&&(t.status===`STARTED`||t.status===`FINISHED`||t.status===`FAILED`)&&(t.duration===void 0||typeof t.duration==`string`)}var i=class{details;theme;constructor(e,t){this.details=e,this.theme=t}render(e){let t=`${this.theme.fg(`accent`,`[${this.details.displayName}]:`)} ${this.details.job}`,r=this.details.duration?`${this.details.status}::${this.details.duration}::${this.details.step}`:`${this.details.status}::${this.details.step}`,i=this.details.status===`FAILED`?`error`:this.details.status===`FINISHED`?`success`:`accent`;return[n(t,e),n(this.theme.fg(i,r),e)]}invalidate(){}};function a(n,r,a,o){let s=new e(a,1,e=>o.bg(`customMessageBg`,e));return s.addChild(n?new i(n,o):new t(r,0,0)),s}export{r as isWorkflowStepMessageDetails,a as renderWorkflowStepMessage};
|
|
2
|
-
//# sourceMappingURL=workflowStepMessage.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowStepMessage.mjs","names":[],"sources":["../../../../src/tui/workflow/workflowStepMessage.ts"],"sourcesContent":["import type { Theme } from '@earendil-works/pi-coding-agent';\nimport { Box, type Component, Text, truncateToWidth } from '@earendil-works/pi-tui';\n\nimport type { StepReport } from './workflowStatusRow';\n\nexport interface WorkflowStepMessageDetails {\n displayName: string;\n duration?: string;\n job: string;\n status: StepReport['status'];\n step: string;\n}\n\nexport function workflowStepMessageDetails(displayName: string, report: StepReport): WorkflowStepMessageDetails {\n return {\n displayName,\n job: report.job,\n status: report.status,\n step: report.step,\n ...(report.duration ? { duration: report.duration } : {}),\n };\n}\n\nexport function formatWorkflowStepMessage(details: WorkflowStepMessageDetails): string {\n const transition = details.duration\n ? `${details.status}::${details.duration}::${details.step}`\n : `${details.status}::${details.step}`;\n return `[${details.displayName}]: ${details.job}\\n${transition}`;\n}\n\nexport function isWorkflowStepMessageDetails(value: unknown): value is WorkflowStepMessageDetails {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const candidate = value as Partial<WorkflowStepMessageDetails>;\n return (\n typeof candidate.displayName === 'string' &&\n typeof candidate.job === 'string' &&\n typeof candidate.step === 'string' &&\n (candidate.status === 'STARTED' || candidate.status === 'FINISHED' || candidate.status === 'FAILED') &&\n (candidate.duration === undefined || typeof candidate.duration === 'string')\n );\n}\n\nclass WorkflowStepMessageLines implements Component {\n constructor(\n private readonly details: WorkflowStepMessageDetails,\n private readonly theme: Theme,\n ) {}\n\n render(width: number): string[] {\n const header = `${this.theme.fg('accent', `[${this.details.displayName}]:`)} ${this.details.job}`;\n const transition = this.details.duration\n ? `${this.details.status}::${this.details.duration}::${this.details.step}`\n : `${this.details.status}::${this.details.step}`;\n const colour =\n this.details.status === 'FAILED' ? 'error' : this.details.status === 'FINISHED' ? 'success' : 'accent';\n return [truncateToWidth(header, width), truncateToWidth(this.theme.fg(colour, transition), width)];\n }\n\n invalidate(): void {}\n}\n\nexport function renderWorkflowStepMessage(\n details: WorkflowStepMessageDetails | undefined,\n fallback: string,\n outputPad: number,\n theme: Theme,\n): Component {\n const box = new Box(outputPad, 1, (text) => theme.bg('customMessageBg', text));\n box.addChild(details ? new WorkflowStepMessageLines(details, theme) : new Text(fallback, 0, 0));\n return box;\n}\n"],"mappings":"4EA8BA,SAAgB,EAA6B,EAAqD,CAChG,GAAI,CAAC,GAAS,OAAO,GAAU,UAAY,MAAM,QAAQ,CAAK,EAAG,MAAO,GACxE,IAAM,EAAY,EAClB,OACE,OAAO,EAAU,aAAgB,UACjC,OAAO,EAAU,KAAQ,UACzB,OAAO,EAAU,MAAS,WACzB,EAAU,SAAW,WAAa,EAAU,SAAW,YAAc,EAAU,SAAW,YAC1F,EAAU,WAAa,IAAA,IAAa,OAAO,EAAU,UAAa,SAEvE,CAEA,IAAM,EAAN,KAAoD,CAE/B,QACA,MAFnB,YACE,EACA,EACA,CAFiB,KAAA,QAAA,EACA,KAAA,MAAA,CAChB,CAEH,OAAO,EAAyB,CAC9B,IAAM,EAAS,GAAG,KAAK,MAAM,GAAG,SAAU,IAAI,KAAK,QAAQ,YAAY,GAAG,EAAE,GAAG,KAAK,QAAQ,MACtF,EAAa,KAAK,QAAQ,SAC5B,GAAG,KAAK,QAAQ,OAAO,IAAI,KAAK,QAAQ,SAAS,IAAI,KAAK,QAAQ,OAClE,GAAG,KAAK,QAAQ,OAAO,IAAI,KAAK,QAAQ,OACtC,EACJ,KAAK,QAAQ,SAAW,SAAW,QAAU,KAAK,QAAQ,SAAW,WAAa,UAAY,SAChG,MAAO,CAAC,EAAgB,EAAQ,CAAK,EAAG,EAAgB,KAAK,MAAM,GAAG,EAAQ,CAAU,EAAG,CAAK,CAAC,CACnG,CAEA,YAAmB,CAAC,CACtB,EAEA,SAAgB,EACd,EACA,EACA,EACA,EACW,CACX,IAAM,EAAM,IAAI,EAAI,EAAW,EAAI,GAAS,EAAM,GAAG,kBAAmB,CAAI,CAAC,EAE7E,OADA,EAAI,SAAS,EAAU,IAAI,EAAyB,EAAS,CAAK,EAAI,IAAI,EAAK,EAAU,EAAG,CAAC,CAAC,EACvF,CACT"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
let e=require("@agimon-ai/doompi-ui/toolChrome");function t(e){if(e&&typeof e==`object`&&!Array.isArray(e))return e}function n(e){let t=``;for(let n of e){let e=n.codePointAt(0)??0;t+=e<=31||e===127?` `:n}return t.replace(/\s+/gu,` `).trim()}function r(e,t){let r=e?.[t];if(typeof r==`string`)return n(r)||void 0}function i(e,t){let n=e?.[t];return typeof n==`number`&&Number.isFinite(n)?n:void 0}function a(e,t){let n=e?.[t];return typeof n==`boolean`?n:void 0}function o(e,t){let n=e?.[t];return Array.isArray(n)?n.filter(e=>typeof e==`string`):[]}function s(e){if(e)try{return t(JSON.parse(e))}catch{return}}function c(e){return Array.isArray(e.content)?e.content.flatMap(e=>{let n=t(e);return n?.type===`text`&&typeof n.text==`string`?[n.text]:[]}):[]}function l(e){return c(e).flatMap(e=>e.split(`
|
|
2
|
-
`)).map(e=>e.trimEnd()).filter(e=>e.length>0)}function u(e){return s(c(e)[0])}function d(e,t,n=`${t}s`){return e===1?t:n}function f(e,t,n){return`${n.fg(`dim`,`${e} `)}${n.fg(`text`,t)}`}function p(e){let t=r(e,`runKey`);if(!t)return;let n=r(e,`workspace`);return n?`${n}/${t}`:t}function m(e,t){return e===`list_workflows`?`list`:e===`launch_workflow`?`launch`:r(t,`action`)??`run`}function h(e,t){return e===`list_workflows`?r(t,`directory`):e===`launch_workflow`?r(t,`workflowPath`):p(t)}function g(t,n,r){return new e.DoomToolResult(t,n,{wrap:r})}function _(e,t){return t.fg(`dim`,`… ${e} more · ctrl+o`)}function v(e){return e.replace(`T`,` `).replace(/\.\d{3}Z$/,`Z`)}function y(e){if(!e)return`workflow`;let t=e.split(`/`).at(-1)??e;return t.replace(/\.workflow\.ya?ml$/u,``)||t}function b(e,n,s){let c=[];if(e===`list_workflows`){let e=i(n,`page`),t=i(n,`pageSize`);e!==void 0&&c.push(`page ${e}`),t!==void 0&&c.push(`${t}/page`),r(n,`filter`)&&c.push(`filtered`);let a=o(n,`tags`);a.length>0&&c.push(`${a.length} ${d(a.length,`tag`)}`)}else if(e===`launch_workflow`){let e=r(n,`job`),i=r(n,`runner`),o=r(n,`workspace`),s=Object.keys(t(n.inputs)??{}).length,l=Object.keys(t(n.env)??{}).length;o&&c.push(`workspace ${o}`),e&&c.push(`job ${e}`),i&&c.push(`runner ${i}`),a(n,`dryRun`)&&c.push(`dry run`),r(n,`prompt`)&&c.push(`with prompt`),s>0&&c.push(`${s} ${d(s,`input`)}`),l>0&&c.push(`${l} env`)}else r(n,`expectedRunId`)&&c.push(`generation checked`),r(n,`reason`)&&c.push(`with reason`),a(n,`dryRun`)&&c.push(`dry run`),r(n,`job`)&&c.push(`job override`),r(n,`runner`)&&c.push(`runner override`);return c.map(e=>s.fg(`dim`,e))}function x(t,n,r){let i=m(t,n),a=h(t,n),o=b(t,n,r),s=[r.bold(i),a?r.fg(`muted`,a):void 0,...o].filter(e=>!!e).join(r.fg(`dim`,` · `));return new e.DoomToolCall((0,e.renderToolHeading)(`workflow`,s,r))}function S(e){if(!Array.isArray(e?.workflows))return;let n=e.workflows.flatMap(e=>{let n=t(e),i=r(n,`path`);return!n||!i?[]:[{description:r(n,`description`)??``,name:r(n,`name`)??``,path:i,tags:o(n,`tags`)}]}),a=Array.isArray(e.tags)?e.tags.flatMap(e=>{let n=t(e),a=r(n,`tag`),o=i(n,`count`);return a&&o!==void 0?[{count:o,tag:a}]:[]}):[];return{directory:r(e,`directory`),page:i(e,`page`)??1,pageSize:i(e,`pageSize`)??n.length,tags:a,total:i(e,`total`)??n.length,totalPages:i(e,`totalPages`)??+(n.length>0),workflows:n}}function C(e,t,n){let r=e.name||y(e.path),i=[`${n.fg(`accent`,`›`)} ${n.bold(r)} ${n.fg(`muted`,`· ${e.path}`)}`];return t?(e.description&&i.push(` ${n.fg(`text`,e.description)}`),e.tags.length>0&&i.push(` ${f(`tags`,e.tags.join(`, `),n)}`),i):i}function w(e,t,n){let r=S(u(e));if(!r)return;let i=`page ${r.page}/${r.totalPages}`,a=`${n.fg(r.total>0?`success`:`muted`,r.total>0?`✓`:`○`)} ${n.bold(`${r.total} ${d(r.total,`workflow`)}`)} ${n.fg(`dim`,`· ${i}`)}`,o=t.expanded?r.workflows:r.workflows.slice(0,6),s=[a,...o.flatMap(e=>C(e,t.expanded,n))],c=r.workflows.length-o.length;if(c>0&&s.push(_(c,n)),t.expanded){if(r.tags.length>0){let e=r.tags.map(({count:e,tag:t})=>`${t} (${e})`).join(`, `);s.push(f(`available tags`,e,n))}r.directory&&s.push(f(`directory`,r.directory,n)),s.push(f(`page size`,String(r.pageSize),n))}return g(s,n,t.expanded)}function T(e){let t=r(e,`effectiveStage`)??r(e,`stage`)??`unknown`,n=r(e,`effectiveOutcome`)??r(e,`outcome`),i=r(e,`executionState`);return n===`interrupted`?{color:`warning`,glyph:`!`,label:`interrupted`}:t===`error`||n===`failed`?{color:`error`,glyph:`✗`,label:`failed`}:t===`completed`&&n===`skipped`?{color:`muted`,glyph:`○`,label:`skipped`}:t===`completed`?{color:`success`,glyph:`✓`,label:`completed`}:i===`paused`?{color:`warning`,glyph:`Ⅱ`,label:`paused`}:i===`pause_requested`?{color:`warning`,glyph:`◐`,label:`pause requested`}:i===`resume_requested`?{color:`warning`,glyph:`◐`,label:`resume requested`}:t===`running`?{color:`accent`,glyph:`◐`,label:`running`}:{color:`muted`,glyph:`○`,label:t}}function E(e){let t=r(e,`runKey`)??`unknown run`,n=r(e,`workspace`);return n?`${n}/${t}`:t}function D(e,n){let i=t(e.executionCursor),a=r(i,`job`)??r(e,`job`),o=r(i,`stepName`),s=r(i,`phase`);if(!a&&!o&&!s)return;let c=a??s??`workflow`;return` ${n.fg(`muted`,`[${c}]`)} ${n.fg(`text`,o??s??`running`)}`}function O(e){let n=t(e.launcher),i=r(n,`type`);if(!i)return;let a=r(n,`sessionName`)??r(n,`workspaceId`);return a?`${i} ${a}`:i}function k(e,t,n){let o=r(e,`runKey`),s=r(e,`stage`);if(!o||!s)return;let c=T(e),l=r(e,`displayName`)??o,u=[`${n.fg(c.color,c.glyph)} ${n.bold(l)} ${n.fg(`dim`,`· ${E(e)} ·`)} ${n.fg(c.color,c.label)}`],d=D(e,n);d&&u.push(d);let p=r(e,`errorMessage`)??r(e,`failureReason`);p&&u.push(`${n.fg(`error`,`error`)} ${n.fg(`text`,p)}`);let m=r(e,`staleReason`);m&&u.push(`${n.fg(`warning`,`stale`)} ${n.fg(`text`,m)}`);let h=[r(e,`runner`)?`runner ${r(e,`runner`)}`:void 0,a(e,`dryRun`)?`dry run`:void 0,i(e,`exitCode`)===void 0?void 0:`exit ${i(e,`exitCode`)}`,r(e,`startedAt`)?`started ${v(r(e,`startedAt`)??``)}`:void 0].filter(e=>!!e);if(h.length>0&&u.push(n.fg(`dim`,h.join(` · `))),!t)return u;let g=[[`workflow`,r(e,`workflowPath`)],[`workflow id`,r(e,`workflowId`)],[`run id`,r(e,`runId`)],[`launcher`,O(e)],[`job`,r(e,`job`)],[`outcome`,r(e,`effectiveOutcome`)??r(e,`outcome`)],[`finished`,r(e,`finishedAt`)?v(r(e,`finishedAt`)??``):void 0],[`pid`,i(e,`pid`)?.toString()],[`repair`,r(e,`activeRepairId`)?`#${r(e,`activeRepairId`)}`:void 0],[`worktree`,r(e,`worktreeBranch`)??r(e,`worktreePath`)]];for(let[e,t]of g)t&&u.push(f(e,t,n));return u}function A(e,t,n){let r=u(e);if(!r)return;let i=k(r,t.expanded,n);return i?g(i,n,t.expanded):void 0}function j(e){return r(t(e?.request),`requestedAt`)??r(e,`requestedAt`)}function M(e,t,n,i,a){let o=u(n),s=p(t)??r(o,`runKey`)??`workflow`,c=r(o,`workspace`),l=c&&!s.includes(`/`)?`${c}/${s}`:s,d=[`${a.fg(`warning`,`◐`)} ${a.bold(`${e} requested`)} ${a.fg(`dim`,`· ${l}`)}`],m=j(o);return m&&d.push(f(`requested`,v(m),a)),i.expanded&&d.push(a.fg(`dim`,`verify the transition with workflow_run status`)),g(d,a,i.expanded)}function N(e,t){return e.match(t)?.[1]?.trim()}function P(e,t){let n=N(e,/^Started (.+) in workspace .+\.$/mu),i=N(e,/^Started .+ in workspace (.+)\.$/mu)??r(t,`workspace`),a=N(e,/^Run key:\s*(.+)$/mu),o=r(t,`workflowPath`),s=n??y(o),c=a?i?`${i}/${a}`:a:i??o??`workflow`;return/Workflow completed successfully/iu.test(e)?{name:s,scope:c,state:{color:`success`,glyph:`✓`,label:`completed`}}:/Workflow skipped/iu.test(e)?{name:s,scope:c,state:{color:`muted`,glyph:`○`,label:`skipped`}}:/no run was registered/iu.test(e)?{name:s,scope:c,state:{color:`warning`,glyph:`◐`,label:`starting · run key pending`}}:{name:s,scope:c,state:{color:`accent`,glyph:`◐`,label:a?`started`:`launch accepted`}}}function F(e,t,n,i){let o=P(c(t).join(`
|
|
3
|
-
`),e),s=[`${i.fg(o.state.color,o.state.glyph)} ${i.bold(o.name)} ${i.fg(`dim`,`· ${o.scope} ·`)} ${i.fg(o.state.color,o.state.label)}`],u=[r(e,`job`)?`job ${r(e,`job`)}`:void 0,r(e,`runner`)?`runner ${r(e,`runner`)}`:void 0,a(e,`dryRun`)?`dry run`:void 0].filter(e=>!!e);if(u.length>0&&s.push(i.fg(`dim`,u.join(` · `))),n.expanded){let e=l(t).filter(e=>!e.startsWith(`Next steps for the agent:`));e.length>0&&s.push(...e)}return g(s,i,n.expanded)}function I(e){let t=[],n;for(let r of e.split(`
|
|
4
|
-
`)){let e=r.match(/^--- (.+) ---$/u);if(e?.[1]){n={body:[],name:e[1]},t.push(n);continue}n?.body.push(r)}return t}function L(e){return s(e.find(e=>e.name===`run.json`)?.body.join(`
|
|
5
|
-
`))}function R(e,t,n,r){let i=I(c(t).join(`
|
|
6
|
-
`)),a=L(i),o=a?k(a,n.expanded,r):void 0,s=i.filter(e=>e.name!==`run.json`),l=p(e)??`workflow`,u=o??[`${r.fg(`success`,`✓`)} ${r.bold(`recovery evidence`)} ${r.fg(`dim`,`· ${l}`)}`],d=s.map(e=>e.name);if(u.push(d.length>0?f(`evidence`,d.join(`, `),r):r.fg(`muted`,`no durable evidence files recorded`)),n.expanded)for(let e of s)u.push(r.bold(e.name),...e.body.filter(e=>e.length>0));return g(u,r,n.expanded)}function z(e,t,n,r,i){let o=p(t)??`workflow`,s={follow:`following output`,open:`launcher opened`,recover:a(t,`dryRun`)?`recovery dry run finished`:`recovery accepted`,tail:`launcher output checked`},c=e===`recover`?{color:`accent`,glyph:`◐`,label:s[e]??e}:{color:`success`,glyph:`✓`,label:s[e]??e},u=[`${i.fg(c.color,c.glyph)} ${i.bold(c.label)} ${i.fg(`dim`,`· ${o}`)}`],d=l(n);if(e===`tail`){let e=d.find(e=>e.startsWith(`Stage:`)),t=d.find(e=>e.startsWith(`Workflow:`));t&&u.push(t),e&&u.push(e)}return r.expanded&&d.length>0&&u.push(...d),g(u,i,r.expanded)}function B(e,t,n,r,i){let a=m(e,t),o=h(e,t),s=`${i.fg(`error`,`✗`)} ${i.bold(`${a} failed`)}${o?i.fg(`dim`,` · ${o}`):``}`,c=l(n),u=r.expanded?c:c.slice(0,8),d=[s,...u.length>0?u:[i.fg(`error`,`Unknown workflow error`)]],f=c.length-u.length;return f>0&&d.push(_(f,i)),g(d,i,r.expanded)}function V(e,t,n,r,i){let a=l(n),o=a.slice(-4),s=m(e,t),c=h(e,t),u=`${i.fg(`accent`,`◐`)} ${i.bold(s)}${c?i.fg(`dim`,` · ${c}`):``}`,d=a.length-o.length,f=[u];return d>0&&f.push(i.fg(`dim`,`… ${d} earlier`)),f.push(...o),g(f,i,r.expanded)}function H(e,t,n){let r=l(e);if(r.length===0)return g([n.fg(`muted`,`No workflow output.`)],n,t.expanded);let i=t.expanded?r:r.slice(0,8),a=[...i],o=r.length-i.length;return o>0&&a.push(_(o,n)),g(a,n,t.expanded)}function U(e,t,n,i,a){if(i.isPartial)return V(e,t,n,i,a);if(i.isError===!0||n.isError===!0)return B(e,t,n,i,a);if(e===`list_workflows`)return w(n,i,a)??H(n,i,a);if(e===`launch_workflow`)return F(t,n,i,a);let o=r(t,`action`);return o===`status`?A(n,i,a)??H(n,i,a):o===`pause`||o===`resume`||o===`stop`?M(o,t,n,i,a):o===`recovery-evidence`?R(t,n,i,a):o===`follow`||o===`open`||o===`recover`||o===`tail`?z(o,t,n,i,a):H(n,i,a)}exports.renderWorkflowToolCall=x,exports.renderWorkflowToolResult=U;
|
|
7
|
-
//# sourceMappingURL=workflowToolRender.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflowToolRender.cjs","names":["DoomToolResult","DoomToolCall","renderToolHeading"],"sources":["../../../../src/tui/workflow/workflowToolRender.ts"],"sourcesContent":["import { DoomToolCall, DoomToolResult, renderToolHeading } from '@agimon-ai/doompi-ui/toolChrome';\nimport type { Theme, ThemeColor } from '@earendil-works/pi-coding-agent';\nimport type { Component } from '@earendil-works/pi-tui';\n\nexport type WorkflowToolName = 'launch_workflow' | 'list_workflows' | 'workflow_run';\nexport type WorkflowPiToolName = WorkflowToolName;\n\ninterface WorkflowToolResultLike {\n content?: unknown;\n isError?: boolean;\n}\n\ninterface WorkflowToolRenderOptions {\n expanded: boolean;\n isError?: boolean;\n isPartial?: boolean;\n}\n\ntype JsonRecord = Record<string, unknown>;\n\ntype WorkflowState = {\n color: ThemeColor;\n glyph: string;\n label: string;\n};\n\ninterface WorkflowCatalogItem {\n description: string;\n name: string;\n path: string;\n tags: string[];\n}\n\ninterface WorkflowCatalog {\n directory?: string;\n page: number;\n pageSize: number;\n tags: Array<{ count: number; tag: string }>;\n total: number;\n totalPages: number;\n workflows: WorkflowCatalogItem[];\n}\n\ninterface EvidenceSection {\n body: string[];\n name: string;\n}\n\nconst COLLAPSED_CATALOG_ROWS = 6;\nconst COLLAPSED_DETAIL_LINES = 8;\nconst PARTIAL_DETAIL_LINES = 4;\n\nfunction asRecord(value: unknown): JsonRecord | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as JsonRecord;\n}\n\nfunction inlineText(value: string): string {\n let normalized = '';\n for (const character of value) {\n const code = character.codePointAt(0) ?? 0;\n normalized += code <= 31 || code === 127 ? ' ' : character;\n }\n return normalized.replace(/\\s+/gu, ' ').trim();\n}\n\nfunction readString(record: JsonRecord | undefined, key: string): string | undefined {\n const value = record?.[key];\n if (typeof value !== 'string') return undefined;\n return inlineText(value) || undefined;\n}\n\nfunction readNumber(record: JsonRecord | undefined, key: string): number | undefined {\n const value = record?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction readBoolean(record: JsonRecord | undefined, key: string): boolean | undefined {\n const value = record?.[key];\n return typeof value === 'boolean' ? value : undefined;\n}\n\nfunction readStringArray(record: JsonRecord | undefined, key: string): string[] {\n const value = record?.[key];\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === 'string');\n}\n\nfunction parseJsonRecord(text: string | undefined): JsonRecord | undefined {\n if (!text) return undefined;\n try {\n return asRecord(JSON.parse(text));\n } catch {\n return undefined;\n }\n}\n\nfunction textBlocks(result: WorkflowToolResultLike): string[] {\n if (!Array.isArray(result.content)) return [];\n return result.content.flatMap((item) => {\n const record = asRecord(item);\n return record?.type === 'text' && typeof record.text === 'string' ? [record.text] : [];\n });\n}\n\nfunction detailLines(result: WorkflowToolResultLike): string[] {\n return textBlocks(result)\n .flatMap((block) => block.split('\\n'))\n .map((line) => line.trimEnd())\n .filter((line) => line.length > 0);\n}\n\nfunction firstPayload(result: WorkflowToolResultLike): JsonRecord | undefined {\n return parseJsonRecord(textBlocks(result)[0]);\n}\n\nfunction plural(count: number, singular: string, pluralForm = `${singular}s`): string {\n return count === 1 ? singular : pluralForm;\n}\n\nfunction labelValue(label: string, value: string, theme: Theme): string {\n return `${theme.fg('dim', `${label} `)}${theme.fg('text', value)}`;\n}\n\nfunction targetScope(args: Record<string, unknown>): string | undefined {\n const runKey = readString(args, 'runKey');\n if (!runKey) return undefined;\n const workspace = readString(args, 'workspace');\n return workspace ? `${workspace}/${runKey}` : runKey;\n}\n\nfunction toolAction(toolName: WorkflowPiToolName, args: Record<string, unknown>): string {\n if (toolName === 'list_workflows') return 'list';\n if (toolName === 'launch_workflow') return 'launch';\n return readString(args, 'action') ?? 'run';\n}\n\nfunction toolTarget(toolName: WorkflowPiToolName, args: Record<string, unknown>): string | undefined {\n if (toolName === 'list_workflows') return readString(args, 'directory');\n if (toolName === 'launch_workflow') return readString(args, 'workflowPath');\n return targetScope(args);\n}\n\nfunction renderResult(lines: readonly string[], theme: Theme, expanded: boolean): Component {\n return new DoomToolResult(lines, theme, { wrap: expanded });\n}\n\nfunction hiddenDetailLine(hidden: number, theme: Theme): string {\n return theme.fg('dim', `… ${hidden} more · ctrl+o`);\n}\n\nfunction statusTimestamp(value: string): string {\n return value.replace('T', ' ').replace(/\\.\\d{3}Z$/, 'Z');\n}\n\nfunction workflowNameFromPath(path: string | undefined): string {\n if (!path) return 'workflow';\n const filename = path.split('/').at(-1) ?? path;\n return filename.replace(/\\.workflow\\.ya?ml$/u, '') || filename;\n}\n\nfunction callMetadata(toolName: WorkflowPiToolName, args: Record<string, unknown>, theme: Theme): string[] {\n const values: string[] = [];\n\n if (toolName === 'list_workflows') {\n const page = readNumber(args, 'page');\n const pageSize = readNumber(args, 'pageSize');\n if (page !== undefined) values.push(`page ${page}`);\n if (pageSize !== undefined) values.push(`${pageSize}/page`);\n if (readString(args, 'filter')) values.push('filtered');\n const tags = readStringArray(args, 'tags');\n if (tags.length > 0) values.push(`${tags.length} ${plural(tags.length, 'tag')}`);\n } else if (toolName === 'launch_workflow') {\n const job = readString(args, 'job');\n const runner = readString(args, 'runner');\n const workspace = readString(args, 'workspace');\n const inputCount = Object.keys(asRecord(args.inputs) ?? {}).length;\n const environmentCount = Object.keys(asRecord(args.env) ?? {}).length;\n if (workspace) values.push(`workspace ${workspace}`);\n if (job) values.push(`job ${job}`);\n if (runner) values.push(`runner ${runner}`);\n if (readBoolean(args, 'dryRun')) values.push('dry run');\n if (readString(args, 'prompt')) values.push('with prompt');\n if (inputCount > 0) values.push(`${inputCount} ${plural(inputCount, 'input')}`);\n if (environmentCount > 0) values.push(`${environmentCount} env`);\n } else {\n const expectedRunId = readString(args, 'expectedRunId');\n if (expectedRunId) values.push('generation checked');\n if (readString(args, 'reason')) values.push('with reason');\n if (readBoolean(args, 'dryRun')) values.push('dry run');\n if (readString(args, 'job')) values.push('job override');\n if (readString(args, 'runner')) values.push('runner override');\n }\n\n return values.map((value) => theme.fg('dim', value));\n}\n\nexport function renderWorkflowToolCall(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n theme: Theme,\n): Component {\n const action = toolAction(toolName, args);\n const target = toolTarget(toolName, args);\n const metadata = callMetadata(toolName, args, theme);\n const purpose = [theme.bold(action), target ? theme.fg('muted', target) : undefined, ...metadata]\n .filter((part): part is string => !!part)\n .join(theme.fg('dim', ' · '));\n return new DoomToolCall(renderToolHeading('workflow', purpose, theme));\n}\n\nfunction parseCatalog(payload: JsonRecord | undefined): WorkflowCatalog | undefined {\n if (!Array.isArray(payload?.workflows)) return undefined;\n\n const workflows = payload.workflows.flatMap((value) => {\n const record = asRecord(value);\n const path = readString(record, 'path');\n if (!record || !path) return [];\n return [\n {\n description: readString(record, 'description') ?? '',\n name: readString(record, 'name') ?? '',\n path,\n tags: readStringArray(record, 'tags'),\n },\n ];\n });\n const tags = Array.isArray(payload.tags)\n ? payload.tags.flatMap((value) => {\n const record = asRecord(value);\n const tag = readString(record, 'tag');\n const count = readNumber(record, 'count');\n return tag && count !== undefined ? [{ count, tag }] : [];\n })\n : [];\n\n return {\n directory: readString(payload, 'directory'),\n page: readNumber(payload, 'page') ?? 1,\n pageSize: readNumber(payload, 'pageSize') ?? workflows.length,\n tags,\n total: readNumber(payload, 'total') ?? workflows.length,\n totalPages: readNumber(payload, 'totalPages') ?? (workflows.length > 0 ? 1 : 0),\n workflows,\n };\n}\n\nfunction catalogItemLines(item: WorkflowCatalogItem, expanded: boolean, theme: Theme): string[] {\n const name = item.name || workflowNameFromPath(item.path);\n const lines = [`${theme.fg('accent', '›')} ${theme.bold(name)} ${theme.fg('muted', `· ${item.path}`)}`];\n if (!expanded) return lines;\n if (item.description) lines.push(` ${theme.fg('text', item.description)}`);\n if (item.tags.length > 0) lines.push(` ${labelValue('tags', item.tags.join(', '), theme)}`);\n return lines;\n}\n\nfunction renderCatalogResult(\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component | undefined {\n const catalog = parseCatalog(firstPayload(result));\n if (!catalog) return undefined;\n\n const pageLabel = `page ${catalog.page}/${catalog.totalPages}`;\n const summary = `${theme.fg(catalog.total > 0 ? 'success' : 'muted', catalog.total > 0 ? '✓' : '○')} ${theme.bold(\n `${catalog.total} ${plural(catalog.total, 'workflow')}`,\n )} ${theme.fg('dim', `· ${pageLabel}`)}`;\n const shown = options.expanded ? catalog.workflows : catalog.workflows.slice(0, COLLAPSED_CATALOG_ROWS);\n const lines = [summary, ...shown.flatMap((item) => catalogItemLines(item, options.expanded, theme))];\n const hidden = catalog.workflows.length - shown.length;\n if (hidden > 0) lines.push(hiddenDetailLine(hidden, theme));\n\n if (options.expanded) {\n if (catalog.tags.length > 0) {\n const tags = catalog.tags.map(({ count, tag }) => `${tag} (${count})`).join(', ');\n lines.push(labelValue('available tags', tags, theme));\n }\n if (catalog.directory) lines.push(labelValue('directory', catalog.directory, theme));\n lines.push(labelValue('page size', String(catalog.pageSize), theme));\n }\n\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction readWorkflowState(record: JsonRecord): WorkflowState {\n const stage = readString(record, 'effectiveStage') ?? readString(record, 'stage') ?? 'unknown';\n const outcome = readString(record, 'effectiveOutcome') ?? readString(record, 'outcome');\n const executionState = readString(record, 'executionState');\n\n if (outcome === 'interrupted') return { color: 'warning', glyph: '!', label: 'interrupted' };\n if (stage === 'error' || outcome === 'failed') return { color: 'error', glyph: '✗', label: 'failed' };\n if (stage === 'completed' && outcome === 'skipped') return { color: 'muted', glyph: '○', label: 'skipped' };\n if (stage === 'completed') return { color: 'success', glyph: '✓', label: 'completed' };\n if (executionState === 'paused') return { color: 'warning', glyph: 'Ⅱ', label: 'paused' };\n if (executionState === 'pause_requested') return { color: 'warning', glyph: '◐', label: 'pause requested' };\n if (executionState === 'resume_requested') return { color: 'warning', glyph: '◐', label: 'resume requested' };\n if (stage === 'running') return { color: 'accent', glyph: '◐', label: 'running' };\n return { color: 'muted', glyph: '○', label: stage };\n}\n\nfunction recordScope(record: JsonRecord): string {\n const runKey = readString(record, 'runKey') ?? 'unknown run';\n const workspace = readString(record, 'workspace');\n return workspace ? `${workspace}/${runKey}` : runKey;\n}\n\nfunction currentActivity(record: JsonRecord, theme: Theme): string | undefined {\n const cursor = asRecord(record.executionCursor);\n const job = readString(cursor, 'job') ?? readString(record, 'job');\n const step = readString(cursor, 'stepName');\n const phase = readString(cursor, 'phase');\n if (!job && !step && !phase) return undefined;\n const context = job ?? phase ?? 'workflow';\n return ` ${theme.fg('muted', `[${context}]`)} ${theme.fg('text', step ?? phase ?? 'running')}`;\n}\n\nfunction launcherLabel(record: JsonRecord): string | undefined {\n const launcher = asRecord(record.launcher);\n const type = readString(launcher, 'type');\n if (!type) return undefined;\n const identity = readString(launcher, 'sessionName') ?? readString(launcher, 'workspaceId');\n return identity ? `${type} ${identity}` : type;\n}\n\nfunction workflowRecordLines(record: JsonRecord, expanded: boolean, theme: Theme): string[] | undefined {\n const runKey = readString(record, 'runKey');\n const stage = readString(record, 'stage');\n if (!runKey || !stage) return undefined;\n\n const state = readWorkflowState(record);\n const displayName = readString(record, 'displayName') ?? runKey;\n const heading = `${theme.fg(state.color, state.glyph)} ${theme.bold(displayName)} ${theme.fg(\n 'dim',\n `· ${recordScope(record)} ·`,\n )} ${theme.fg(state.color, state.label)}`;\n const lines = [heading];\n const activity = currentActivity(record, theme);\n if (activity) lines.push(activity);\n\n const failure = readString(record, 'errorMessage') ?? readString(record, 'failureReason');\n if (failure) lines.push(`${theme.fg('error', 'error')} ${theme.fg('text', failure)}`);\n const staleReason = readString(record, 'staleReason');\n if (staleReason) lines.push(`${theme.fg('warning', 'stale')} ${theme.fg('text', staleReason)}`);\n\n const conciseMetadata = [\n readString(record, 'runner') ? `runner ${readString(record, 'runner')}` : undefined,\n readBoolean(record, 'dryRun') ? 'dry run' : undefined,\n readNumber(record, 'exitCode') !== undefined ? `exit ${readNumber(record, 'exitCode')}` : undefined,\n readString(record, 'startedAt') ? `started ${statusTimestamp(readString(record, 'startedAt') ?? '')}` : undefined,\n ].filter((value): value is string => !!value);\n if (conciseMetadata.length > 0) lines.push(theme.fg('dim', conciseMetadata.join(' · ')));\n\n if (!expanded) return lines;\n\n const detailedValues: Array<[string, string | undefined]> = [\n ['workflow', readString(record, 'workflowPath')],\n ['workflow id', readString(record, 'workflowId')],\n ['run id', readString(record, 'runId')],\n ['launcher', launcherLabel(record)],\n ['job', readString(record, 'job')],\n ['outcome', readString(record, 'effectiveOutcome') ?? readString(record, 'outcome')],\n [\n 'finished',\n readString(record, 'finishedAt') ? statusTimestamp(readString(record, 'finishedAt') ?? '') : undefined,\n ],\n ['pid', readNumber(record, 'pid')?.toString()],\n ['repair', readString(record, 'activeRepairId') ? `#${readString(record, 'activeRepairId')}` : undefined],\n ['worktree', readString(record, 'worktreeBranch') ?? readString(record, 'worktreePath')],\n ];\n for (const [label, value] of detailedValues) {\n if (value) lines.push(labelValue(label, value, theme));\n }\n return lines;\n}\n\nfunction renderStatusResult(\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component | undefined {\n const payload = firstPayload(result);\n if (!payload) return undefined;\n const lines = workflowRecordLines(payload, options.expanded, theme);\n return lines ? renderResult(lines, theme, options.expanded) : undefined;\n}\n\nfunction nestedRequestedAt(payload: JsonRecord | undefined): string | undefined {\n return readString(asRecord(payload?.request), 'requestedAt') ?? readString(payload, 'requestedAt');\n}\n\nfunction renderControlResult(\n action: string,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const payload = firstPayload(result);\n const scope = targetScope(args) ?? readString(payload, 'runKey') ?? 'workflow';\n const payloadWorkspace = readString(payload, 'workspace');\n const resolvedScope = payloadWorkspace && !scope.includes('/') ? `${payloadWorkspace}/${scope}` : scope;\n const lines = [\n `${theme.fg('warning', '◐')} ${theme.bold(`${action} requested`)} ${theme.fg('dim', `· ${resolvedScope}`)}`,\n ];\n const requestedAt = nestedRequestedAt(payload);\n if (requestedAt) lines.push(labelValue('requested', statusTimestamp(requestedAt), theme));\n if (options.expanded) lines.push(theme.fg('dim', 'verify the transition with workflow_run status'));\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction matchLine(text: string, pattern: RegExp): string | undefined {\n return text.match(pattern)?.[1]?.trim();\n}\n\nfunction launchSummary(\n text: string,\n args: Record<string, unknown>,\n): { name: string; scope: string; state: WorkflowState } {\n const startedName = matchLine(text, /^Started (.+) in workspace .+\\.$/mu);\n const workspace = matchLine(text, /^Started .+ in workspace (.+)\\.$/mu) ?? readString(args, 'workspace');\n const runKey = matchLine(text, /^Run key:\\s*(.+)$/mu);\n const path = readString(args, 'workflowPath');\n const name = startedName ?? workflowNameFromPath(path);\n const scope = runKey ? (workspace ? `${workspace}/${runKey}` : runKey) : (workspace ?? path ?? 'workflow');\n\n if (/Workflow completed successfully/iu.test(text)) {\n return { name, scope, state: { color: 'success', glyph: '✓', label: 'completed' } };\n }\n if (/Workflow skipped/iu.test(text)) {\n return { name, scope, state: { color: 'muted', glyph: '○', label: 'skipped' } };\n }\n if (/no run was registered/iu.test(text)) {\n return { name, scope, state: { color: 'warning', glyph: '◐', label: 'starting · run key pending' } };\n }\n return { name, scope, state: { color: 'accent', glyph: '◐', label: runKey ? 'started' : 'launch accepted' } };\n}\n\nfunction renderLaunchResult(\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const blocks = textBlocks(result);\n const text = blocks.join('\\n');\n const summary = launchSummary(text, args);\n const lines = [\n `${theme.fg(summary.state.color, summary.state.glyph)} ${theme.bold(summary.name)} ${theme.fg(\n 'dim',\n `· ${summary.scope} ·`,\n )} ${theme.fg(summary.state.color, summary.state.label)}`,\n ];\n const metadata = [\n readString(args, 'job') ? `job ${readString(args, 'job')}` : undefined,\n readString(args, 'runner') ? `runner ${readString(args, 'runner')}` : undefined,\n readBoolean(args, 'dryRun') ? 'dry run' : undefined,\n ].filter((value): value is string => !!value);\n if (metadata.length > 0) lines.push(theme.fg('dim', metadata.join(' · ')));\n if (options.expanded) {\n const details = detailLines(result).filter((line) => !line.startsWith('Next steps for the agent:'));\n if (details.length > 0) lines.push(...details);\n }\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction splitEvidence(text: string): EvidenceSection[] {\n const sections: EvidenceSection[] = [];\n let current: EvidenceSection | undefined;\n for (const line of text.split('\\n')) {\n const match = line.match(/^--- (.+) ---$/u);\n if (match?.[1]) {\n current = { body: [], name: match[1] };\n sections.push(current);\n continue;\n }\n current?.body.push(line);\n }\n return sections;\n}\n\nfunction recoveryRecord(sections: EvidenceSection[]): JsonRecord | undefined {\n const runSection = sections.find((section) => section.name === 'run.json');\n return parseJsonRecord(runSection?.body.join('\\n'));\n}\n\nfunction renderRecoveryEvidenceResult(\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const text = textBlocks(result).join('\\n');\n const sections = splitEvidence(text);\n const record = recoveryRecord(sections);\n const recordLines = record ? workflowRecordLines(record, options.expanded, theme) : undefined;\n const evidenceSections = sections.filter((section) => section.name !== 'run.json');\n const scope = targetScope(args) ?? 'workflow';\n const lines = recordLines ?? [\n `${theme.fg('success', '✓')} ${theme.bold('recovery evidence')} ${theme.fg('dim', `· ${scope}`)}`,\n ];\n const names = evidenceSections.map((section) => section.name);\n lines.push(\n names.length > 0\n ? labelValue('evidence', names.join(', '), theme)\n : theme.fg('muted', 'no durable evidence files recorded'),\n );\n\n if (options.expanded) {\n for (const section of evidenceSections) {\n lines.push(theme.bold(section.name), ...section.body.filter((line) => line.length > 0));\n }\n }\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderSimpleActionResult(\n action: string,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const scope = targetScope(args) ?? 'workflow';\n const actionLabel: Record<string, string> = {\n follow: 'following output',\n open: 'launcher opened',\n recover: readBoolean(args, 'dryRun') ? 'recovery dry run finished' : 'recovery accepted',\n tail: 'launcher output checked',\n };\n const state: WorkflowState =\n action === 'recover'\n ? { color: 'accent', glyph: '◐', label: actionLabel[action] ?? action }\n : { color: 'success', glyph: '✓', label: actionLabel[action] ?? action };\n const lines = [`${theme.fg(state.color, state.glyph)} ${theme.bold(state.label)} ${theme.fg('dim', `· ${scope}`)}`];\n const details = detailLines(result);\n if (action === 'tail') {\n const stageLine = details.find((line) => line.startsWith('Stage:'));\n const workflowLine = details.find((line) => line.startsWith('Workflow:'));\n if (workflowLine) lines.push(workflowLine);\n if (stageLine) lines.push(stageLine);\n }\n if (options.expanded && details.length > 0) lines.push(...details);\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderFailure(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const action = toolAction(toolName, args);\n const target = toolTarget(toolName, args);\n const summary = `${theme.fg('error', '✗')} ${theme.bold(`${action} failed`)}${\n target ? theme.fg('dim', ` · ${target}`) : ''\n }`;\n const details = detailLines(result);\n const shown = options.expanded ? details : details.slice(0, COLLAPSED_DETAIL_LINES);\n const lines = [summary, ...(shown.length > 0 ? shown : [theme.fg('error', 'Unknown workflow error')])];\n const hidden = details.length - shown.length;\n if (hidden > 0) lines.push(hiddenDetailLine(hidden, theme));\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderPartial(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n const details = detailLines(result);\n const shown = details.slice(-PARTIAL_DETAIL_LINES);\n const action = toolAction(toolName, args);\n const target = toolTarget(toolName, args);\n const summary = `${theme.fg('accent', '◐')} ${theme.bold(action)}${target ? theme.fg('dim', ` · ${target}`) : ''}`;\n const hidden = details.length - shown.length;\n const lines = [summary];\n if (hidden > 0) lines.push(theme.fg('dim', `… ${hidden} earlier`));\n lines.push(...shown);\n return renderResult(lines, theme, options.expanded);\n}\n\nfunction renderFallback(result: WorkflowToolResultLike, options: WorkflowToolRenderOptions, theme: Theme): Component {\n const details = detailLines(result);\n if (details.length === 0) return renderResult([theme.fg('muted', 'No workflow output.')], theme, options.expanded);\n const shown = options.expanded ? details : details.slice(0, COLLAPSED_DETAIL_LINES);\n const lines = [...shown];\n const hidden = details.length - shown.length;\n if (hidden > 0) lines.push(hiddenDetailLine(hidden, theme));\n return renderResult(lines, theme, options.expanded);\n}\n\nexport function renderWorkflowToolResult(\n toolName: WorkflowPiToolName,\n args: Record<string, unknown>,\n result: WorkflowToolResultLike,\n options: WorkflowToolRenderOptions,\n theme: Theme,\n): Component {\n if (options.isPartial) return renderPartial(toolName, args, result, options, theme);\n if (options.isError === true || result.isError === true) {\n return renderFailure(toolName, args, result, options, theme);\n }\n\n if (toolName === 'list_workflows') {\n return renderCatalogResult(result, options, theme) ?? renderFallback(result, options, theme);\n }\n if (toolName === 'launch_workflow') return renderLaunchResult(args, result, options, theme);\n\n const action = readString(args, 'action');\n if (action === 'status') {\n return renderStatusResult(result, options, theme) ?? renderFallback(result, options, theme);\n }\n if (action === 'pause' || action === 'resume' || action === 'stop') {\n return renderControlResult(action, args, result, options, theme);\n }\n if (action === 'recovery-evidence') {\n return renderRecoveryEvidenceResult(args, result, options, theme);\n }\n if (action === 'follow' || action === 'open' || action === 'recover' || action === 'tail') {\n return renderSimpleActionResult(action, args, result, options, theme);\n }\n return renderFallback(result, options, theme);\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import{DoomToolCall as e,DoomToolResult as t,renderToolHeading as n}from"@agimon-ai/doompi-ui/toolChrome";function r(e){if(e&&typeof e==`object`&&!Array.isArray(e))return e}function i(e){let t=``;for(let n of e){let e=n.codePointAt(0)??0;t+=e<=31||e===127?` `:n}return t.replace(/\s+/gu,` `).trim()}function a(e,t){let n=e?.[t];if(typeof n==`string`)return i(n)||void 0}function o(e,t){let n=e?.[t];return typeof n==`number`&&Number.isFinite(n)?n:void 0}function s(e,t){let n=e?.[t];return typeof n==`boolean`?n:void 0}function c(e,t){let n=e?.[t];return Array.isArray(n)?n.filter(e=>typeof e==`string`):[]}function l(e){if(e)try{return r(JSON.parse(e))}catch{return}}function u(e){return Array.isArray(e.content)?e.content.flatMap(e=>{let t=r(e);return t?.type===`text`&&typeof t.text==`string`?[t.text]:[]}):[]}function d(e){return u(e).flatMap(e=>e.split(`
|
|
2
|
-
`)).map(e=>e.trimEnd()).filter(e=>e.length>0)}function f(e){return l(u(e)[0])}function p(e,t,n=`${t}s`){return e===1?t:n}function m(e,t,n){return`${n.fg(`dim`,`${e} `)}${n.fg(`text`,t)}`}function h(e){let t=a(e,`runKey`);if(!t)return;let n=a(e,`workspace`);return n?`${n}/${t}`:t}function g(e,t){return e===`list_workflows`?`list`:e===`launch_workflow`?`launch`:a(t,`action`)??`run`}function _(e,t){return e===`list_workflows`?a(t,`directory`):e===`launch_workflow`?a(t,`workflowPath`):h(t)}function v(e,n,r){return new t(e,n,{wrap:r})}function y(e,t){return t.fg(`dim`,`… ${e} more · ctrl+o`)}function b(e){return e.replace(`T`,` `).replace(/\.\d{3}Z$/,`Z`)}function x(e){if(!e)return`workflow`;let t=e.split(`/`).at(-1)??e;return t.replace(/\.workflow\.ya?ml$/u,``)||t}function S(e,t,n){let i=[];if(e===`list_workflows`){let e=o(t,`page`),n=o(t,`pageSize`);e!==void 0&&i.push(`page ${e}`),n!==void 0&&i.push(`${n}/page`),a(t,`filter`)&&i.push(`filtered`);let r=c(t,`tags`);r.length>0&&i.push(`${r.length} ${p(r.length,`tag`)}`)}else if(e===`launch_workflow`){let e=a(t,`job`),n=a(t,`runner`),o=a(t,`workspace`),c=Object.keys(r(t.inputs)??{}).length,l=Object.keys(r(t.env)??{}).length;o&&i.push(`workspace ${o}`),e&&i.push(`job ${e}`),n&&i.push(`runner ${n}`),s(t,`dryRun`)&&i.push(`dry run`),a(t,`prompt`)&&i.push(`with prompt`),c>0&&i.push(`${c} ${p(c,`input`)}`),l>0&&i.push(`${l} env`)}else a(t,`expectedRunId`)&&i.push(`generation checked`),a(t,`reason`)&&i.push(`with reason`),s(t,`dryRun`)&&i.push(`dry run`),a(t,`job`)&&i.push(`job override`),a(t,`runner`)&&i.push(`runner override`);return i.map(e=>n.fg(`dim`,e))}function C(t,r,i){let a=g(t,r),o=_(t,r),s=S(t,r,i),c=[i.bold(a),o?i.fg(`muted`,o):void 0,...s].filter(e=>!!e).join(i.fg(`dim`,` · `));return new e(n(`workflow`,c,i))}function w(e){if(!Array.isArray(e?.workflows))return;let t=e.workflows.flatMap(e=>{let t=r(e),n=a(t,`path`);return!t||!n?[]:[{description:a(t,`description`)??``,name:a(t,`name`)??``,path:n,tags:c(t,`tags`)}]}),n=Array.isArray(e.tags)?e.tags.flatMap(e=>{let t=r(e),n=a(t,`tag`),i=o(t,`count`);return n&&i!==void 0?[{count:i,tag:n}]:[]}):[];return{directory:a(e,`directory`),page:o(e,`page`)??1,pageSize:o(e,`pageSize`)??t.length,tags:n,total:o(e,`total`)??t.length,totalPages:o(e,`totalPages`)??+(t.length>0),workflows:t}}function T(e,t,n){let r=e.name||x(e.path),i=[`${n.fg(`accent`,`›`)} ${n.bold(r)} ${n.fg(`muted`,`· ${e.path}`)}`];return t?(e.description&&i.push(` ${n.fg(`text`,e.description)}`),e.tags.length>0&&i.push(` ${m(`tags`,e.tags.join(`, `),n)}`),i):i}function E(e,t,n){let r=w(f(e));if(!r)return;let i=`page ${r.page}/${r.totalPages}`,a=`${n.fg(r.total>0?`success`:`muted`,r.total>0?`✓`:`○`)} ${n.bold(`${r.total} ${p(r.total,`workflow`)}`)} ${n.fg(`dim`,`· ${i}`)}`,o=t.expanded?r.workflows:r.workflows.slice(0,6),s=[a,...o.flatMap(e=>T(e,t.expanded,n))],c=r.workflows.length-o.length;if(c>0&&s.push(y(c,n)),t.expanded){if(r.tags.length>0){let e=r.tags.map(({count:e,tag:t})=>`${t} (${e})`).join(`, `);s.push(m(`available tags`,e,n))}r.directory&&s.push(m(`directory`,r.directory,n)),s.push(m(`page size`,String(r.pageSize),n))}return v(s,n,t.expanded)}function D(e){let t=a(e,`effectiveStage`)??a(e,`stage`)??`unknown`,n=a(e,`effectiveOutcome`)??a(e,`outcome`),r=a(e,`executionState`);return n===`interrupted`?{color:`warning`,glyph:`!`,label:`interrupted`}:t===`error`||n===`failed`?{color:`error`,glyph:`✗`,label:`failed`}:t===`completed`&&n===`skipped`?{color:`muted`,glyph:`○`,label:`skipped`}:t===`completed`?{color:`success`,glyph:`✓`,label:`completed`}:r===`paused`?{color:`warning`,glyph:`Ⅱ`,label:`paused`}:r===`pause_requested`?{color:`warning`,glyph:`◐`,label:`pause requested`}:r===`resume_requested`?{color:`warning`,glyph:`◐`,label:`resume requested`}:t===`running`?{color:`accent`,glyph:`◐`,label:`running`}:{color:`muted`,glyph:`○`,label:t}}function O(e){let t=a(e,`runKey`)??`unknown run`,n=a(e,`workspace`);return n?`${n}/${t}`:t}function k(e,t){let n=r(e.executionCursor),i=a(n,`job`)??a(e,`job`),o=a(n,`stepName`),s=a(n,`phase`);if(!i&&!o&&!s)return;let c=i??s??`workflow`;return` ${t.fg(`muted`,`[${c}]`)} ${t.fg(`text`,o??s??`running`)}`}function A(e){let t=r(e.launcher),n=a(t,`type`);if(!n)return;let i=a(t,`sessionName`)??a(t,`workspaceId`);return i?`${n} ${i}`:n}function j(e,t,n){let r=a(e,`runKey`),i=a(e,`stage`);if(!r||!i)return;let c=D(e),l=a(e,`displayName`)??r,u=[`${n.fg(c.color,c.glyph)} ${n.bold(l)} ${n.fg(`dim`,`· ${O(e)} ·`)} ${n.fg(c.color,c.label)}`],d=k(e,n);d&&u.push(d);let f=a(e,`errorMessage`)??a(e,`failureReason`);f&&u.push(`${n.fg(`error`,`error`)} ${n.fg(`text`,f)}`);let p=a(e,`staleReason`);p&&u.push(`${n.fg(`warning`,`stale`)} ${n.fg(`text`,p)}`);let h=[a(e,`runner`)?`runner ${a(e,`runner`)}`:void 0,s(e,`dryRun`)?`dry run`:void 0,o(e,`exitCode`)===void 0?void 0:`exit ${o(e,`exitCode`)}`,a(e,`startedAt`)?`started ${b(a(e,`startedAt`)??``)}`:void 0].filter(e=>!!e);if(h.length>0&&u.push(n.fg(`dim`,h.join(` · `))),!t)return u;let g=[[`workflow`,a(e,`workflowPath`)],[`workflow id`,a(e,`workflowId`)],[`run id`,a(e,`runId`)],[`launcher`,A(e)],[`job`,a(e,`job`)],[`outcome`,a(e,`effectiveOutcome`)??a(e,`outcome`)],[`finished`,a(e,`finishedAt`)?b(a(e,`finishedAt`)??``):void 0],[`pid`,o(e,`pid`)?.toString()],[`repair`,a(e,`activeRepairId`)?`#${a(e,`activeRepairId`)}`:void 0],[`worktree`,a(e,`worktreeBranch`)??a(e,`worktreePath`)]];for(let[e,t]of g)t&&u.push(m(e,t,n));return u}function M(e,t,n){let r=f(e);if(!r)return;let i=j(r,t.expanded,n);return i?v(i,n,t.expanded):void 0}function N(e){return a(r(e?.request),`requestedAt`)??a(e,`requestedAt`)}function P(e,t,n,r,i){let o=f(n),s=h(t)??a(o,`runKey`)??`workflow`,c=a(o,`workspace`),l=c&&!s.includes(`/`)?`${c}/${s}`:s,u=[`${i.fg(`warning`,`◐`)} ${i.bold(`${e} requested`)} ${i.fg(`dim`,`· ${l}`)}`],d=N(o);return d&&u.push(m(`requested`,b(d),i)),r.expanded&&u.push(i.fg(`dim`,`verify the transition with workflow_run status`)),v(u,i,r.expanded)}function F(e,t){return e.match(t)?.[1]?.trim()}function I(e,t){let n=F(e,/^Started (.+) in workspace .+\.$/mu),r=F(e,/^Started .+ in workspace (.+)\.$/mu)??a(t,`workspace`),i=F(e,/^Run key:\s*(.+)$/mu),o=a(t,`workflowPath`),s=n??x(o),c=i?r?`${r}/${i}`:i:r??o??`workflow`;return/Workflow completed successfully/iu.test(e)?{name:s,scope:c,state:{color:`success`,glyph:`✓`,label:`completed`}}:/Workflow skipped/iu.test(e)?{name:s,scope:c,state:{color:`muted`,glyph:`○`,label:`skipped`}}:/no run was registered/iu.test(e)?{name:s,scope:c,state:{color:`warning`,glyph:`◐`,label:`starting · run key pending`}}:{name:s,scope:c,state:{color:`accent`,glyph:`◐`,label:i?`started`:`launch accepted`}}}function L(e,t,n,r){let i=I(u(t).join(`
|
|
3
|
-
`),e),o=[`${r.fg(i.state.color,i.state.glyph)} ${r.bold(i.name)} ${r.fg(`dim`,`· ${i.scope} ·`)} ${r.fg(i.state.color,i.state.label)}`],c=[a(e,`job`)?`job ${a(e,`job`)}`:void 0,a(e,`runner`)?`runner ${a(e,`runner`)}`:void 0,s(e,`dryRun`)?`dry run`:void 0].filter(e=>!!e);if(c.length>0&&o.push(r.fg(`dim`,c.join(` · `))),n.expanded){let e=d(t).filter(e=>!e.startsWith(`Next steps for the agent:`));e.length>0&&o.push(...e)}return v(o,r,n.expanded)}function R(e){let t=[],n;for(let r of e.split(`
|
|
4
|
-
`)){let e=r.match(/^--- (.+) ---$/u);if(e?.[1]){n={body:[],name:e[1]},t.push(n);continue}n?.body.push(r)}return t}function z(e){return l(e.find(e=>e.name===`run.json`)?.body.join(`
|
|
5
|
-
`))}function B(e,t,n,r){let i=R(u(t).join(`
|
|
6
|
-
`)),a=z(i),o=a?j(a,n.expanded,r):void 0,s=i.filter(e=>e.name!==`run.json`),c=h(e)??`workflow`,l=o??[`${r.fg(`success`,`✓`)} ${r.bold(`recovery evidence`)} ${r.fg(`dim`,`· ${c}`)}`],d=s.map(e=>e.name);if(l.push(d.length>0?m(`evidence`,d.join(`, `),r):r.fg(`muted`,`no durable evidence files recorded`)),n.expanded)for(let e of s)l.push(r.bold(e.name),...e.body.filter(e=>e.length>0));return v(l,r,n.expanded)}function V(e,t,n,r,i){let a=h(t)??`workflow`,o={follow:`following output`,open:`launcher opened`,recover:s(t,`dryRun`)?`recovery dry run finished`:`recovery accepted`,tail:`launcher output checked`},c=e===`recover`?{color:`accent`,glyph:`◐`,label:o[e]??e}:{color:`success`,glyph:`✓`,label:o[e]??e},l=[`${i.fg(c.color,c.glyph)} ${i.bold(c.label)} ${i.fg(`dim`,`· ${a}`)}`],u=d(n);if(e===`tail`){let e=u.find(e=>e.startsWith(`Stage:`)),t=u.find(e=>e.startsWith(`Workflow:`));t&&l.push(t),e&&l.push(e)}return r.expanded&&u.length>0&&l.push(...u),v(l,i,r.expanded)}function H(e,t,n,r,i){let a=g(e,t),o=_(e,t),s=`${i.fg(`error`,`✗`)} ${i.bold(`${a} failed`)}${o?i.fg(`dim`,` · ${o}`):``}`,c=d(n),l=r.expanded?c:c.slice(0,8),u=[s,...l.length>0?l:[i.fg(`error`,`Unknown workflow error`)]],f=c.length-l.length;return f>0&&u.push(y(f,i)),v(u,i,r.expanded)}function U(e,t,n,r,i){let a=d(n),o=a.slice(-4),s=g(e,t),c=_(e,t),l=`${i.fg(`accent`,`◐`)} ${i.bold(s)}${c?i.fg(`dim`,` · ${c}`):``}`,u=a.length-o.length,f=[l];return u>0&&f.push(i.fg(`dim`,`… ${u} earlier`)),f.push(...o),v(f,i,r.expanded)}function W(e,t,n){let r=d(e);if(r.length===0)return v([n.fg(`muted`,`No workflow output.`)],n,t.expanded);let i=t.expanded?r:r.slice(0,8),a=[...i],o=r.length-i.length;return o>0&&a.push(y(o,n)),v(a,n,t.expanded)}function G(e,t,n,r,i){if(r.isPartial)return U(e,t,n,r,i);if(r.isError===!0||n.isError===!0)return H(e,t,n,r,i);if(e===`list_workflows`)return E(n,r,i)??W(n,r,i);if(e===`launch_workflow`)return L(t,n,r,i);let o=a(t,`action`);return o===`status`?M(n,r,i)??W(n,r,i):o===`pause`||o===`resume`||o===`stop`?P(o,t,n,r,i):o===`recovery-evidence`?B(t,n,r,i):o===`follow`||o===`open`||o===`recover`||o===`tail`?V(o,t,n,r,i):W(n,r,i)}export{C as renderWorkflowToolCall,G as renderWorkflowToolResult};
|
|
7
|
-
//# sourceMappingURL=workflowToolRender.mjs.map
|