@agimon-ai/doompi-runner 0.0.1-alpha.13 → 0.0.1-alpha.14

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.
@@ -1,6 +1,6 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./format.cjs");let t=require("@earendil-works/pi-tui"),n=require("@agimon-ai/doompi-ui/components/doomOverlay");const r=n.DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;var i=class extends n.DoomOverlay{options;done;selected=0;attached;viewedLog;unsubscribe;logRefresh;notice;stopReason;constructor(e,t,n,r){super(e,t),this.options=n,this.done=r}handleInput(e){if(this.stopReason){this.handleStopReasonInput(e);return}if(this.attached){this.handleAttachedInput(e);return}if(this.viewedLog){(0,t.matchesKey)(e,`escape`)&&this.detach();return}this.handleListInput(e)}getChrome(){let e=this.options.getRunners();return{title:`RUNNER SPACE`,accent:n.DOOM_OVERLAY_ACCENT,breadcrumb:`SPC › r / runners › r / runner space`,headerRight:this.attached?`attached · ${this.attached.name}`:this.viewedLog?`logs · ${this.viewedLog.name}`:`${e.length} runners · this worktree`,footer:this.attached?`esc back · everything else goes to the runner`:this.viewedLog?`esc back · live stdout/stderr`:`↑↓/jk select · enter open · s stop · r reason · esc close`,footerHints:this.attached||this.viewedLog?void 0:[[`↑↓/jk`,`select`],[`enter`,`open`],[`s`,`stop`],[`r`,`reason`],[`esc`,`close`]],footerRight:this.attached?`live tty`:this.viewedLog?`live log`:e.length>0?`${Math.min(this.selected,e.length-1)+1}/${e.length}`:`empty`}}renderBody(e,n){let r=n=>(0,t.truncateToWidth)(n,e,`…`);return this.attached?this.renderAttached(r,n):this.viewedLog?this.renderLog(r,n):this.renderList(r,n)}handleAttachedInput(e){if((0,t.matchesKey)(e,`escape`)){this.detach();return}this.attached?.write(e)}handleListInput(e){let n=this.options.getRunners();if((0,t.matchesKey)(e,`escape`)||(0,t.matchesKey)(e,`ctrl+c`)){this.close();return}(0,t.matchesKey)(e,`up`)||e===`k`?this.move(-1,n.length):(0,t.matchesKey)(e,`down`)||e===`j`?this.move(1,n.length):e===`\r`||e===`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./format.cjs");let t=require("@earendil-works/pi-tui"),n=require("@agimon-ai/doompi-ui/components/doomOverlay");const r=n.DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions,i=`${n.DOOM_NAVIGATION_KEYS.list} select · enter open · s stop · r reason · esc close`;var a=class extends n.DoomOverlay{options;done;selected=0;attached;viewedLog;unsubscribe;logRefresh;notice;stopReason;constructor(e,t,n,r){super(e,t),this.options=n,this.done=r}handleInput(e){if(this.stopReason){this.handleStopReasonInput(e);return}if(this.attached){this.handleAttachedInput(e);return}if(this.viewedLog){(0,t.matchesKey)(e,`escape`)&&this.detach();return}this.handleListInput(e)}getChrome(){let e=this.options.getRunners();return{title:`RUNNER SPACE`,accent:n.DOOM_OVERLAY_ACCENT,breadcrumb:`SPC › r / runners › r / runner space`,headerRight:this.attached?`attached · ${this.attached.name}`:this.viewedLog?`logs · ${this.viewedLog.name}`:`${e.length} runners · this worktree`,footer:this.attached?`esc back · everything else goes to the runner`:this.viewedLog?`esc back · live stdout/stderr`:i,footerHints:this.attached||this.viewedLog?void 0:[[n.DOOM_NAVIGATION_KEYS.list,`select`],[`enter`,`open`],[`s`,`stop`],[`r`,`reason`],[`esc`,`close`]],footerRight:this.attached?`live tty`:this.viewedLog?`live log`:e.length>0?`${Math.min(this.selected,e.length-1)+1}/${e.length}`:`empty`}}renderBody(e,n){let r=n=>(0,t.truncateToWidth)(n,e,`…`);return this.attached?this.renderAttached(r,n):this.viewedLog?this.renderLog(r,n):this.renderList(r,n)}handleAttachedInput(e){if((0,t.matchesKey)(e,`escape`)){this.detach();return}this.attached?.write(e)}handleListInput(e){let n=this.options.getRunners();if((0,t.matchesKey)(e,`escape`)||(0,t.matchesKey)(e,`ctrl+c`)){this.close();return}(0,t.matchesKey)(e,`up`)||e===`k`?this.move(-1,n.length):(0,t.matchesKey)(e,`down`)||e===`j`?this.move(1,n.length):e===`\r`||e===`
2
2
  `?this.attach(n):e===`s`?this.stop(n):e===`r`&&this.promptStopReason(n)}move(e,t){t!==0&&(this.selected=Math.max(0,Math.min(t-1,this.selected+e)),this.notice=void 0,this.tui.requestRender())}current(e){if(e.length!==0)return e[Math.min(this.selected,e.length-1)]}attach(e){let t=this.current(e);if(!t)return;let n=t.interactive?this.options.getPtyRun(t.name):void 0;this.notice=void 0,n?(this.attached=n,this.unsubscribe=n.onData(()=>this.tui.requestRender())):(this.viewedLog=t,this.logRefresh=setInterval(()=>this.tui.requestRender(!0),250),this.logRefresh.unref?.()),this.tui.requestRender(!0)}detach(){this.unsubscribe?.(),this.unsubscribe=void 0,this.logRefresh&&clearInterval(this.logRefresh),this.logRefresh=void 0,this.attached=void 0,this.viewedLog=void 0,this.tui.requestRender(!0)}stop(e){let t=this.current(e);t&&this.options.stopRunner(t.id).then(()=>{this.notice={text:`Stopped ${t.name}`,color:`success`},this.tui.requestRender(!0)}).catch(e=>{this.notice={text:e instanceof Error?e.message:String(e),color:`error`},this.tui.requestRender()})}promptStopReason(e){let t=this.current(e);t&&(this.stopReason={runner:t,text:``},this.tui.requestRender())}handleStopReasonInput(e){let n=this.stopReason;if(n){if((0,t.matchesKey)(e,`escape`)||(0,t.matchesKey)(e,`ctrl+c`)){this.stopReason=void 0,this.tui.requestRender();return}if(e===`\r`||e===`
3
3
  `){this.stopReason=void 0,this.options.stopRunner(n.runner.id,n.text.trim()||void 0).then(()=>{this.notice={text:`Stopped ${n.runner.name}`,color:`success`},this.tui.requestRender(!0)}).catch(e=>{this.notice={text:e instanceof Error?e.message:String(e),color:`error`},this.tui.requestRender()});return}(0,t.matchesKey)(e,`backspace`)?n.text=n.text.slice(0,-1):e.includes(`\x1B`)||(n.text+=e),this.tui.requestRender()}}close(){this.detach(),this.done(void 0)}renderList(t,n){let r=this.options.getRunners(),i=e.toRunnerRows(r,Date.now()),a=[];if(this.stopReason)return[t(`Stop reason for ${this.stopReason.runner.name}: ${this.stopReason.text}`)];if(i.length===0)a.push(t(this.theme.fg(`dim`,`No background runners. bash backgrounds anything that outlives the threshold.`)));else{let e=this.notice?2:0,r=Math.max(1,n-e),o=Math.max(0,i.length-r),s=Math.min(o,Math.max(0,this.selected-r+1));i.slice(s,s+r).forEach((e,n)=>{let r=s+n===Math.min(this.selected,i.length-1)?`›`:` `,o=e.interactive?` [tty]`:``;a.push(t(`${r} ${e.name}${o} ${this.theme.fg(`dim`,e.detail)}`))})}return this.notice&&a.push(``,t(this.theme.fg(this.notice.color,this.notice.text))),a.slice(0,n)}renderAttached(e,t){let n=this.attached;return n?n.screen().split(`
4
4
  `).slice(-t).map(e):[]}renderLog(e,t){let n=this.viewedLog;if(!n)return[];let r=this.options.readLog(n.logPath);return r?r.split(`
5
- `).slice(-t).map(e):[e(this.theme.fg(`dim`,`Waiting for output…`))]}};async function a(e,t){await e.ui.custom((e,n,r,a)=>new i(e,n,t,a),n.DOOM_FULLSCREEN_UI_OPTIONS)}exports.RUNNER_SPACE_OVERLAY_OPTIONS=r,exports.RunnerSpaceComponent=i,exports.openRunnerSpace=a;
5
+ `).slice(-t).map(e):[e(this.theme.fg(`dim`,`Waiting for output…`))]}};async function o(e,t){await e.ui.custom((e,n,r,i)=>new a(e,n,t,i),n.DOOM_FULLSCREEN_UI_OPTIONS)}exports.RUNNER_SPACE_OVERLAY_OPTIONS=r,exports.RunnerSpaceComponent=a,exports.openRunnerSpace=o;
6
6
  //# sourceMappingURL=runnerSpace.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"runnerSpace.cjs","names":["DOOM_FULLSCREEN_UI_OPTIONS","DoomOverlay","matchesKey","DOOM_OVERLAY_ACCENT","truncateToWidth","toRunnerRows"],"sources":["../../src/tui/runnerSpace.ts"],"sourcesContent":["/**\n * Runner Space: the `SPC r r` overlay.\n *\n * Two modes behind one component. The list shows every runner in this worktree\n * and can stop them; attaching to an interactive runner renders its live\n * terminal and forwards keystrokes to it, which is the whole reason PTY runs\n * exist. Rendering is pure, so the surface can be asserted as text.\n */\n\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/components/doomOverlay';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth } from '@earendil-works/pi-tui';\nimport type { PtyRun } from '../services/PtyHost/index.ts';\nimport type { RunnerRecord } from '../services/RunnerRegistry/index.ts';\nimport { toRunnerRows } from './format.ts';\n\nexport const RUNNER_SPACE_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nconst KEY_ENTER = '\\r';\nconst KEY_NEWLINE = '\\n';\nconst TITLE = 'RUNNER SPACE';\nconst EMPTY_MESSAGE = 'No background runners. bash backgrounds anything that outlives the threshold.';\nconst LIST_FOOTER = '↑↓/jk select · enter open · s stop · r reason · esc close';\nconst ATTACH_FOOTER = 'esc back · everything else goes to the runner';\nconst LOG_FOOTER = 'esc back · live stdout/stderr';\nconst LOG_REFRESH_MS = 250;\nconst ELLIPSIS = '…';\n\nexport interface RunnerSpaceOptions {\n getRunners: () => readonly RunnerRecord[];\n /** The hosted terminal for a runner, when this session owns one. */\n getPtyRun: (name: string) => PtyRun | undefined;\n readLog: (logPath: string) => string;\n stopRunner: (id: string, reason?: string) => Promise<void>;\n}\n\nexport class RunnerSpaceComponent extends DoomOverlay {\n private selected = 0;\n private attached: PtyRun | undefined;\n private viewedLog: RunnerRecord | undefined;\n private unsubscribe: (() => void) | undefined;\n private logRefresh: ReturnType<typeof setInterval> | undefined;\n private notice: { text: string; color: 'success' | 'error' } | undefined;\n private stopReason: { runner: RunnerRecord; text: string } | undefined;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly options: RunnerSpaceOptions,\n private readonly done: (result: undefined) => void,\n ) {\n super(tui, theme);\n }\n\n handleInput(data: string): void {\n if (this.stopReason) {\n this.handleStopReasonInput(data);\n return;\n }\n if (this.attached) {\n this.handleAttachedInput(data);\n return;\n }\n if (this.viewedLog) {\n if (matchesKey(data, 'escape')) this.detach();\n return;\n }\n this.handleListInput(data);\n }\n\n protected getChrome(): DoomOverlayChrome {\n const runners = this.options.getRunners();\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: 'SPC › r / runners › r / runner space',\n headerRight: this.attached\n ? `attached · ${this.attached.name}`\n : this.viewedLog\n ? `logs · ${this.viewedLog.name}`\n : `${runners.length} runners · this worktree`,\n footer: this.attached ? ATTACH_FOOTER : this.viewedLog ? LOG_FOOTER : LIST_FOOTER,\n footerHints:\n this.attached || this.viewedLog\n ? undefined\n : [\n ['↑↓/jk', 'select'],\n ['enter', 'open'],\n ['s', 'stop'],\n ['r', 'reason'],\n ['esc', 'close'],\n ],\n footerRight: this.attached\n ? 'live tty'\n : this.viewedLog\n ? 'live log'\n : runners.length > 0\n ? `${Math.min(this.selected, runners.length - 1) + 1}/${runners.length}`\n : 'empty',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n const truncate = (line: string): string => truncateToWidth(line, width, ELLIPSIS);\n if (this.attached) return this.renderAttached(truncate, height);\n if (this.viewedLog) return this.renderLog(truncate, height);\n return this.renderList(truncate, height);\n }\n\n private handleAttachedInput(data: string): void {\n if (matchesKey(data, 'escape')) {\n this.detach();\n return;\n }\n this.attached?.write(data);\n }\n\n private handleListInput(data: string): void {\n const runners = this.options.getRunners();\n\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.close();\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') this.move(-1, runners.length);\n else if (matchesKey(data, 'down') || data === 'j') this.move(1, runners.length);\n else if (data === KEY_ENTER || data === KEY_NEWLINE) this.attach(runners);\n else if (data === 's') this.stop(runners);\n else if (data === 'r') this.promptStopReason(runners);\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 this.notice = undefined;\n this.tui.requestRender();\n }\n\n private current(runners: readonly RunnerRecord[]): RunnerRecord | undefined {\n if (runners.length === 0) return undefined;\n return runners[Math.min(this.selected, runners.length - 1)];\n }\n\n private attach(runners: readonly RunnerRecord[]): void {\n const record = this.current(runners);\n if (!record) return;\n\n const run = record.interactive ? this.options.getPtyRun(record.name) : undefined;\n this.notice = undefined;\n if (run) {\n this.attached = run;\n this.unsubscribe = run.onData(() => this.tui.requestRender());\n } else {\n this.viewedLog = record;\n this.logRefresh = setInterval(() => this.tui.requestRender(true), LOG_REFRESH_MS);\n this.logRefresh.unref?.();\n }\n this.tui.requestRender(true);\n }\n\n private detach(): void {\n this.unsubscribe?.();\n this.unsubscribe = undefined;\n if (this.logRefresh) clearInterval(this.logRefresh);\n this.logRefresh = undefined;\n this.attached = undefined;\n this.viewedLog = undefined;\n this.tui.requestRender(true);\n }\n\n private stop(runners: readonly RunnerRecord[]): void {\n const record = this.current(runners);\n if (!record) return;\n\n this.options\n .stopRunner(record.id)\n .then(() => {\n this.notice = { text: `Stopped ${record.name}`, color: 'success' };\n this.tui.requestRender(true);\n })\n .catch((error: unknown) => {\n // Shown rather than swallowed: a failed stop means the process is still\n // running and the operator needs to know.\n this.notice = { text: error instanceof Error ? error.message : String(error), color: 'error' };\n this.tui.requestRender();\n });\n }\n\n private promptStopReason(runners: readonly RunnerRecord[]): void {\n const runner = this.current(runners);\n if (!runner) return;\n this.stopReason = { runner, text: '' };\n this.tui.requestRender();\n }\n\n private handleStopReasonInput(data: string): void {\n const prompt = this.stopReason;\n if (!prompt) return;\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.stopReason = undefined;\n this.tui.requestRender();\n return;\n }\n if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.stopReason = undefined;\n this.options\n .stopRunner(prompt.runner.id, prompt.text.trim() || undefined)\n .then(() => {\n this.notice = { text: `Stopped ${prompt.runner.name}`, color: 'success' };\n this.tui.requestRender(true);\n })\n .catch((error: unknown) => {\n this.notice = { text: error instanceof Error ? error.message : String(error), color: 'error' };\n this.tui.requestRender();\n });\n return;\n }\n if (matchesKey(data, 'backspace')) prompt.text = prompt.text.slice(0, -1);\n else if (!data.includes('\\u001b')) prompt.text += data;\n this.tui.requestRender();\n }\n\n private close(): void {\n this.detach();\n this.done(undefined);\n }\n\n private renderList(truncate: (line: string) => string, height: number): string[] {\n const runners = this.options.getRunners();\n const rows = toRunnerRows(runners, Date.now());\n const lines: string[] = [];\n\n if (this.stopReason) {\n return [truncate(`Stop reason for ${this.stopReason.runner.name}: ${this.stopReason.text}`)];\n }\n\n if (rows.length === 0) lines.push(truncate(this.theme.fg('dim', EMPTY_MESSAGE)));\n else {\n const noticeRows = this.notice ? 2 : 0;\n const capacity = Math.max(1, height - noticeRows);\n const maxStart = Math.max(0, rows.length - capacity);\n const start = Math.min(maxStart, Math.max(0, this.selected - capacity + 1));\n rows.slice(start, start + capacity).forEach((row, offset) => {\n const index = start + offset;\n const marker = index === Math.min(this.selected, rows.length - 1) ? '›' : ' ';\n const tty = row.interactive ? ' [tty]' : '';\n lines.push(truncate(`${marker} ${row.name}${tty} ${this.theme.fg('dim', row.detail)}`));\n });\n }\n\n if (this.notice) lines.push('', truncate(this.theme.fg(this.notice.color, this.notice.text)));\n return lines.slice(0, height);\n }\n\n private renderAttached(truncate: (line: string) => string, height: number): string[] {\n const run = this.attached;\n if (!run) return [];\n\n return run.screen().split('\\n').slice(-height).map(truncate);\n }\n\n private renderLog(truncate: (line: string) => string, height: number): string[] {\n const record = this.viewedLog;\n if (!record) return [];\n\n const text = this.options.readLog(record.logPath);\n if (!text) return [truncate(this.theme.fg('dim', 'Waiting for output…'))];\n return text.split('\\n').slice(-height).map(truncate);\n }\n}\n\nexport async function openRunnerSpace(ctx: ExtensionContext, options: RunnerSpaceOptions): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new RunnerSpaceComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runnerSpace.cjs","names":["DOOM_FULLSCREEN_UI_OPTIONS","DOOM_NAVIGATION_KEYS","DoomOverlay","matchesKey","DOOM_OVERLAY_ACCENT","truncateToWidth","toRunnerRows"],"sources":["../../src/tui/runnerSpace.ts"],"sourcesContent":["/**\n * Runner Space: the `SPC r r` overlay.\n *\n * Two modes behind one component. The list shows every runner in this worktree\n * and can stop them; attaching to an interactive runner renders its live\n * terminal and forwards keystrokes to it, which is the whole reason PTY runs\n * exist. Rendering is pure, so the surface can be asserted as text.\n */\n\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_NAVIGATION_KEYS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/components/doomOverlay';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth } from '@earendil-works/pi-tui';\nimport type { PtyRun } from '../services/PtyHost/index.ts';\nimport type { RunnerRecord } from '../services/RunnerRegistry/index.ts';\nimport { toRunnerRows } from './format.ts';\n\nexport const RUNNER_SPACE_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nconst KEY_ENTER = '\\r';\nconst KEY_NEWLINE = '\\n';\nconst TITLE = 'RUNNER SPACE';\nconst EMPTY_MESSAGE = 'No background runners. bash backgrounds anything that outlives the threshold.';\nconst LIST_FOOTER = `${DOOM_NAVIGATION_KEYS.list} select · enter open · s stop · r reason · esc close`;\nconst ATTACH_FOOTER = 'esc back · everything else goes to the runner';\nconst LOG_FOOTER = 'esc back · live stdout/stderr';\nconst LOG_REFRESH_MS = 250;\nconst ELLIPSIS = '…';\n\nexport interface RunnerSpaceOptions {\n getRunners: () => readonly RunnerRecord[];\n /** The hosted terminal for a runner, when this session owns one. */\n getPtyRun: (name: string) => PtyRun | undefined;\n readLog: (logPath: string) => string;\n stopRunner: (id: string, reason?: string) => Promise<void>;\n}\n\nexport class RunnerSpaceComponent extends DoomOverlay {\n private selected = 0;\n private attached: PtyRun | undefined;\n private viewedLog: RunnerRecord | undefined;\n private unsubscribe: (() => void) | undefined;\n private logRefresh: ReturnType<typeof setInterval> | undefined;\n private notice: { text: string; color: 'success' | 'error' } | undefined;\n private stopReason: { runner: RunnerRecord; text: string } | undefined;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly options: RunnerSpaceOptions,\n private readonly done: (result: undefined) => void,\n ) {\n super(tui, theme);\n }\n\n handleInput(data: string): void {\n if (this.stopReason) {\n this.handleStopReasonInput(data);\n return;\n }\n if (this.attached) {\n this.handleAttachedInput(data);\n return;\n }\n if (this.viewedLog) {\n if (matchesKey(data, 'escape')) this.detach();\n return;\n }\n this.handleListInput(data);\n }\n\n protected getChrome(): DoomOverlayChrome {\n const runners = this.options.getRunners();\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: 'SPC › r / runners › r / runner space',\n headerRight: this.attached\n ? `attached · ${this.attached.name}`\n : this.viewedLog\n ? `logs · ${this.viewedLog.name}`\n : `${runners.length} runners · this worktree`,\n footer: this.attached ? ATTACH_FOOTER : this.viewedLog ? LOG_FOOTER : LIST_FOOTER,\n footerHints:\n this.attached || this.viewedLog\n ? undefined\n : [\n [DOOM_NAVIGATION_KEYS.list, 'select'],\n ['enter', 'open'],\n ['s', 'stop'],\n ['r', 'reason'],\n ['esc', 'close'],\n ],\n footerRight: this.attached\n ? 'live tty'\n : this.viewedLog\n ? 'live log'\n : runners.length > 0\n ? `${Math.min(this.selected, runners.length - 1) + 1}/${runners.length}`\n : 'empty',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n const truncate = (line: string): string => truncateToWidth(line, width, ELLIPSIS);\n if (this.attached) return this.renderAttached(truncate, height);\n if (this.viewedLog) return this.renderLog(truncate, height);\n return this.renderList(truncate, height);\n }\n\n private handleAttachedInput(data: string): void {\n if (matchesKey(data, 'escape')) {\n this.detach();\n return;\n }\n this.attached?.write(data);\n }\n\n private handleListInput(data: string): void {\n const runners = this.options.getRunners();\n\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.close();\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') this.move(-1, runners.length);\n else if (matchesKey(data, 'down') || data === 'j') this.move(1, runners.length);\n else if (data === KEY_ENTER || data === KEY_NEWLINE) this.attach(runners);\n else if (data === 's') this.stop(runners);\n else if (data === 'r') this.promptStopReason(runners);\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 this.notice = undefined;\n this.tui.requestRender();\n }\n\n private current(runners: readonly RunnerRecord[]): RunnerRecord | undefined {\n if (runners.length === 0) return undefined;\n return runners[Math.min(this.selected, runners.length - 1)];\n }\n\n private attach(runners: readonly RunnerRecord[]): void {\n const record = this.current(runners);\n if (!record) return;\n\n const run = record.interactive ? this.options.getPtyRun(record.name) : undefined;\n this.notice = undefined;\n if (run) {\n this.attached = run;\n this.unsubscribe = run.onData(() => this.tui.requestRender());\n } else {\n this.viewedLog = record;\n this.logRefresh = setInterval(() => this.tui.requestRender(true), LOG_REFRESH_MS);\n this.logRefresh.unref?.();\n }\n this.tui.requestRender(true);\n }\n\n private detach(): void {\n this.unsubscribe?.();\n this.unsubscribe = undefined;\n if (this.logRefresh) clearInterval(this.logRefresh);\n this.logRefresh = undefined;\n this.attached = undefined;\n this.viewedLog = undefined;\n this.tui.requestRender(true);\n }\n\n private stop(runners: readonly RunnerRecord[]): void {\n const record = this.current(runners);\n if (!record) return;\n\n this.options\n .stopRunner(record.id)\n .then(() => {\n this.notice = { text: `Stopped ${record.name}`, color: 'success' };\n this.tui.requestRender(true);\n })\n .catch((error: unknown) => {\n // Shown rather than swallowed: a failed stop means the process is still\n // running and the operator needs to know.\n this.notice = { text: error instanceof Error ? error.message : String(error), color: 'error' };\n this.tui.requestRender();\n });\n }\n\n private promptStopReason(runners: readonly RunnerRecord[]): void {\n const runner = this.current(runners);\n if (!runner) return;\n this.stopReason = { runner, text: '' };\n this.tui.requestRender();\n }\n\n private handleStopReasonInput(data: string): void {\n const prompt = this.stopReason;\n if (!prompt) return;\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.stopReason = undefined;\n this.tui.requestRender();\n return;\n }\n if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.stopReason = undefined;\n this.options\n .stopRunner(prompt.runner.id, prompt.text.trim() || undefined)\n .then(() => {\n this.notice = { text: `Stopped ${prompt.runner.name}`, color: 'success' };\n this.tui.requestRender(true);\n })\n .catch((error: unknown) => {\n this.notice = { text: error instanceof Error ? error.message : String(error), color: 'error' };\n this.tui.requestRender();\n });\n return;\n }\n if (matchesKey(data, 'backspace')) prompt.text = prompt.text.slice(0, -1);\n else if (!data.includes('\\u001b')) prompt.text += data;\n this.tui.requestRender();\n }\n\n private close(): void {\n this.detach();\n this.done(undefined);\n }\n\n private renderList(truncate: (line: string) => string, height: number): string[] {\n const runners = this.options.getRunners();\n const rows = toRunnerRows(runners, Date.now());\n const lines: string[] = [];\n\n if (this.stopReason) {\n return [truncate(`Stop reason for ${this.stopReason.runner.name}: ${this.stopReason.text}`)];\n }\n\n if (rows.length === 0) lines.push(truncate(this.theme.fg('dim', EMPTY_MESSAGE)));\n else {\n const noticeRows = this.notice ? 2 : 0;\n const capacity = Math.max(1, height - noticeRows);\n const maxStart = Math.max(0, rows.length - capacity);\n const start = Math.min(maxStart, Math.max(0, this.selected - capacity + 1));\n rows.slice(start, start + capacity).forEach((row, offset) => {\n const index = start + offset;\n const marker = index === Math.min(this.selected, rows.length - 1) ? '›' : ' ';\n const tty = row.interactive ? ' [tty]' : '';\n lines.push(truncate(`${marker} ${row.name}${tty} ${this.theme.fg('dim', row.detail)}`));\n });\n }\n\n if (this.notice) lines.push('', truncate(this.theme.fg(this.notice.color, this.notice.text)));\n return lines.slice(0, height);\n }\n\n private renderAttached(truncate: (line: string) => string, height: number): string[] {\n const run = this.attached;\n if (!run) return [];\n\n return run.screen().split('\\n').slice(-height).map(truncate);\n }\n\n private renderLog(truncate: (line: string) => string, height: number): string[] {\n const record = this.viewedLog;\n if (!record) return [];\n\n const text = this.options.readLog(record.logPath);\n if (!text) return [truncate(this.theme.fg('dim', 'Waiting for output…'))];\n return text.split('\\n').slice(-height).map(truncate);\n }\n}\n\nexport async function openRunnerSpace(ctx: ExtensionContext, options: RunnerSpaceOptions): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new RunnerSpaceComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"oMAuBA,MAAa,EAA+BA,EAAAA,2BAA2B,eAMjE,EAAc,GAAGC,EAAAA,qBAAqB,KAAK,sDAcjD,IAAa,EAAb,cAA0CC,EAAAA,WAAY,CAYjC,QACA,KAZnB,SAAmB,EACnB,SACA,UACA,YACA,WACA,OACA,WAEA,YACE,EACA,EACA,EACA,EACA,CACA,MAAM,EAAK,CAAK,EAHC,KAAA,QAAA,EACA,KAAA,KAAA,CAGnB,CAEA,YAAY,EAAoB,CAC9B,GAAI,KAAK,WAAY,CACnB,KAAK,sBAAsB,CAAI,EAC/B,MACF,CACA,GAAI,KAAK,SAAU,CACjB,KAAK,oBAAoB,CAAI,EAC7B,MACF,CACA,GAAI,KAAK,UAAW,EAClB,EAAIC,EAAAA,WAAAA,CAAW,EAAM,QAAQ,GAAG,KAAK,OAAO,EAC5C,MACF,CACA,KAAK,gBAAgB,CAAI,CAC3B,CAEA,WAAyC,CACvC,IAAM,EAAU,KAAK,QAAQ,WAAW,EACxC,MAAO,CACL,MAAO,eACP,OAAQC,EAAAA,oBACR,WAAY,uCACZ,YAAa,KAAK,SACd,cAAc,KAAK,SAAS,OAC5B,KAAK,UACH,UAAU,KAAK,UAAU,OACzB,GAAG,EAAQ,OAAO,0BACxB,OAAQ,KAAK,SAAW,gDAAgB,KAAK,UAAY,gCAAa,EACtE,YACE,KAAK,UAAY,KAAK,UAClB,IAAA,GACA,CACE,CAACH,EAAAA,qBAAqB,KAAM,QAAQ,EACpC,CAAC,QAAS,MAAM,EAChB,CAAC,IAAK,MAAM,EACZ,CAAC,IAAK,QAAQ,EACd,CAAC,MAAO,OAAO,CACjB,EACN,YAAa,KAAK,SACd,WACA,KAAK,UACH,WACA,EAAQ,OAAS,EACf,GAAG,KAAK,IAAI,KAAK,SAAU,EAAQ,OAAS,CAAC,EAAI,EAAE,GAAG,EAAQ,SAC9D,OACV,CACF,CAEA,WAAqB,EAAe,EAA0B,CAC5D,IAAM,EAAY,IAAA,EAAyBI,EAAAA,gBAAAA,CAAgB,EAAM,EAAO,GAAQ,EAGhF,OAFI,KAAK,SAAiB,KAAK,eAAe,EAAU,CAAM,EAC1D,KAAK,UAAkB,KAAK,UAAU,EAAU,CAAM,EACnD,KAAK,WAAW,EAAU,CAAM,CACzC,CAEA,oBAA4B,EAAoB,CAC9C,IAAA,EAAIF,EAAAA,WAAAA,CAAW,EAAM,QAAQ,EAAG,CAC9B,KAAK,OAAO,EACZ,MACF,CACA,KAAK,UAAU,MAAM,CAAI,CAC3B,CAEA,gBAAwB,EAAoB,CAC1C,IAAM,EAAU,KAAK,QAAQ,WAAW,EAExC,IAAA,EAAIA,EAAAA,WAAAA,CAAW,EAAM,QAAQ,IAAA,EAAKA,EAAAA,WAAAA,CAAW,EAAM,QAAQ,EAAG,CAC5D,KAAK,MAAM,EACX,MACF,EACA,EAAIA,EAAAA,WAAAA,CAAW,EAAM,IAAI,GAAK,IAAS,IAAK,KAAK,KAAK,GAAI,EAAQ,MAAM,GACnE,EAAIA,EAAAA,WAAAA,CAAW,EAAM,MAAM,GAAK,IAAS,IAAK,KAAK,KAAK,EAAG,EAAQ,MAAM,EACrE,IAAS,MAAa,IAAS;EAAa,KAAK,OAAO,CAAO,EAC/D,IAAS,IAAK,KAAK,KAAK,CAAO,EAC/B,IAAS,KAAK,KAAK,iBAAiB,CAAO,CACtD,CAEA,KAAa,EAAe,EAAqB,CAC3C,IAAU,IACd,KAAK,SAAW,KAAK,IAAI,EAAG,KAAK,IAAI,EAAQ,EAAG,KAAK,SAAW,CAAK,CAAC,EACtE,KAAK,OAAS,IAAA,GACd,KAAK,IAAI,cAAc,EACzB,CAEA,QAAgB,EAA4D,CACtE,KAAQ,SAAW,EACvB,OAAO,EAAQ,KAAK,IAAI,KAAK,SAAU,EAAQ,OAAS,CAAC,EAC3D,CAEA,OAAe,EAAwC,CACrD,IAAM,EAAS,KAAK,QAAQ,CAAO,EACnC,GAAI,CAAC,EAAQ,OAEb,IAAM,EAAM,EAAO,YAAc,KAAK,QAAQ,UAAU,EAAO,IAAI,EAAI,IAAA,GACvE,KAAK,OAAS,IAAA,GACV,GACF,KAAK,SAAW,EAChB,KAAK,YAAc,EAAI,WAAa,KAAK,IAAI,cAAc,CAAC,IAE5D,KAAK,UAAY,EACjB,KAAK,WAAa,gBAAkB,KAAK,IAAI,cAAc,EAAI,EAAG,GAAc,EAChF,KAAK,WAAW,QAAQ,GAE1B,KAAK,IAAI,cAAc,EAAI,CAC7B,CAEA,QAAuB,CACrB,KAAK,cAAc,EACnB,KAAK,YAAc,IAAA,GACf,KAAK,YAAY,cAAc,KAAK,UAAU,EAClD,KAAK,WAAa,IAAA,GAClB,KAAK,SAAW,IAAA,GAChB,KAAK,UAAY,IAAA,GACjB,KAAK,IAAI,cAAc,EAAI,CAC7B,CAEA,KAAa,EAAwC,CACnD,IAAM,EAAS,KAAK,QAAQ,CAAO,EAC9B,GAEL,KAAK,QACF,WAAW,EAAO,EAAE,CAAC,CACrB,SAAW,CACV,KAAK,OAAS,CAAE,KAAM,WAAW,EAAO,OAAQ,MAAO,SAAU,EACjE,KAAK,IAAI,cAAc,EAAI,CAC7B,CAAC,CAAC,CACD,MAAO,GAAmB,CAGzB,KAAK,OAAS,CAAE,KAAM,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAAG,MAAO,OAAQ,EAC7F,KAAK,IAAI,cAAc,CACzB,CAAC,CACL,CAEA,iBAAyB,EAAwC,CAC/D,IAAM,EAAS,KAAK,QAAQ,CAAO,EAC9B,IACL,KAAK,WAAa,CAAE,SAAQ,KAAM,EAAG,EACrC,KAAK,IAAI,cAAc,EACzB,CAEA,sBAA8B,EAAoB,CAChD,IAAM,EAAS,KAAK,WACf,KACL,KAAA,EAAIA,EAAAA,WAAAA,CAAW,EAAM,QAAQ,IAAA,EAAKA,EAAAA,WAAAA,CAAW,EAAM,QAAQ,EAAG,CAC5D,KAAK,WAAa,IAAA,GAClB,KAAK,IAAI,cAAc,EACvB,MACF,CACA,GAAI,IAAS,MAAa,IAAS;EAAa,CAC9C,KAAK,WAAa,IAAA,GAClB,KAAK,QACF,WAAW,EAAO,OAAO,GAAI,EAAO,KAAK,KAAK,GAAK,IAAA,EAAS,CAAC,CAC7D,SAAW,CACV,KAAK,OAAS,CAAE,KAAM,WAAW,EAAO,OAAO,OAAQ,MAAO,SAAU,EACxE,KAAK,IAAI,cAAc,EAAI,CAC7B,CAAC,CAAC,CACD,MAAO,GAAmB,CACzB,KAAK,OAAS,CAAE,KAAM,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAAG,MAAO,OAAQ,EAC7F,KAAK,IAAI,cAAc,CACzB,CAAC,EACH,MACF,EACA,EAAIA,EAAAA,WAAAA,CAAW,EAAM,WAAW,EAAG,EAAO,KAAO,EAAO,KAAK,MAAM,EAAG,EAAE,EAC9D,EAAK,SAAS,MAAQ,IAAG,EAAO,MAAQ,GAClD,KAAK,IAAI,cAAc,CAjBvB,CAkBF,CAEA,OAAsB,CACpB,KAAK,OAAO,EACZ,KAAK,KAAK,IAAA,EAAS,CACrB,CAEA,WAAmB,EAAoC,EAA0B,CAC/E,IAAM,EAAU,KAAK,QAAQ,WAAW,EAClC,EAAOG,EAAAA,aAAa,EAAS,KAAK,IAAI,CAAC,EACvC,EAAkB,CAAC,EAEzB,GAAI,KAAK,WACP,MAAO,CAAC,EAAS,mBAAmB,KAAK,WAAW,OAAO,KAAK,IAAI,KAAK,WAAW,MAAM,CAAC,EAG7F,GAAI,EAAK,SAAW,EAAG,EAAM,KAAK,EAAS,KAAK,MAAM,GAAG,MAAO,+EAAa,CAAC,CAAC,MAC1E,CACH,IAAM,EAAa,KAAK,OAAS,EAAI,EAC/B,EAAW,KAAK,IAAI,EAAG,EAAS,CAAU,EAC1C,EAAW,KAAK,IAAI,EAAG,EAAK,OAAS,CAAQ,EAC7C,EAAQ,KAAK,IAAI,EAAU,KAAK,IAAI,EAAG,KAAK,SAAW,EAAW,CAAC,CAAC,EAC1E,EAAK,MAAM,EAAO,EAAQ,CAAQ,CAAC,CAAC,SAAS,EAAK,IAAW,CAE3D,IAAM,EADQ,EAAQ,IACG,KAAK,IAAI,KAAK,SAAU,EAAK,OAAS,CAAC,EAAI,IAAM,IACpE,EAAM,EAAI,YAAc,SAAW,GACzC,EAAM,KAAK,EAAS,GAAG,EAAO,GAAG,EAAI,OAAO,EAAI,IAAI,KAAK,MAAM,GAAG,MAAO,EAAI,MAAM,GAAG,CAAC,CACzF,CAAC,CACH,CAGA,OADI,KAAK,QAAQ,EAAM,KAAK,GAAI,EAAS,KAAK,MAAM,GAAG,KAAK,OAAO,MAAO,KAAK,OAAO,IAAI,CAAC,CAAC,EACrF,EAAM,MAAM,EAAG,CAAM,CAC9B,CAEA,eAAuB,EAAoC,EAA0B,CACnF,IAAM,EAAM,KAAK,SAGjB,OAFK,EAEE,EAAI,OAAO,CAAC,CAAC,MAAM;CAAI,CAAC,CAAC,MAAM,CAAC,CAAM,CAAC,CAAC,IAAI,CAAQ,EAF1C,CAAC,CAGpB,CAEA,UAAkB,EAAoC,EAA0B,CAC9E,IAAM,EAAS,KAAK,UACpB,GAAI,CAAC,EAAQ,MAAO,CAAC,EAErB,IAAM,EAAO,KAAK,QAAQ,QAAQ,EAAO,OAAO,EAEhD,OADK,EACE,EAAK,MAAM;CAAI,CAAC,CAAC,MAAM,CAAC,CAAM,CAAC,CAAC,IAAI,CAAQ,EADjC,CAAC,EAAS,KAAK,MAAM,GAAG,MAAO,qBAAqB,CAAC,CAAC,CAE1E,CACF,EAEA,eAAsB,EAAgB,EAAuB,EAA4C,CACvG,MAAM,EAAI,GAAG,QACV,EAAK,EAAO,EAAc,IAAS,IAAI,EAAqB,EAAK,EAAO,EAAS,CAAI,EACtFN,EAAAA,0BACF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"runnerSpace.d.cts","names":[],"sources":["../../src/tui/runnerSpace.ts"],"mappings":";;;;;;;cAsBa;;;;;;UAYI;EACf,2BAA2B;;EAE3B,YAAY,iBAAiB;EAC7B,UAAU;EACV,aAAa,YAAY,oBAAoB;;cAGlC,6BAA6B;mBAYrB;mBACA;UAZX;UACA;UACA;UACA;UACA;UACA;UACA;EAGN,YAAA,KAAK,gBACL,OAAO,OACU,SAAS,oBACT,OAAO;EAK1B,YAAY;YAgBF,aAAa;YAgCb,WAAW,eAAe;UAO5B;UAQA;UAcA;UAOA;UAKA;UAiBA;UAUA;UAkBA;UAOA;UA2BA;UAKA;UA2BA;UAOA;;iBAUY,gBAAgB,KAAK,kBAAkB,SAAS,qBAAqB"}
1
+ {"version":3,"file":"runnerSpace.d.cts","names":[],"sources":["../../src/tui/runnerSpace.ts"],"mappings":";;;;;;;cAuBa;;;;;;UAYI;EACf,2BAA2B;;EAE3B,YAAY,iBAAiB;EAC7B,UAAU;EACV,aAAa,YAAY,oBAAoB;;cAGlC,6BAA6B;mBAYrB;mBACA;UAZX;UACA;UACA;UACA;UACA;UACA;UACA;EAGN,YAAA,KAAK,gBACL,OAAO,OACU,SAAS,oBACT,OAAO;EAK1B,YAAY;YAgBF,aAAa;YAgCb,WAAW,eAAe;UAO5B;UAQA;UAcA;UAOA;UAKA;UAiBA;UAUA;UAkBA;UAOA;UA2BA;UAKA;UA2BA;UAOA;;iBAUY,gBAAgB,KAAK,kBAAkB,SAAS,qBAAqB"}
@@ -1 +1 @@
1
- {"version":3,"file":"runnerSpace.d.mts","names":[],"sources":["../../src/tui/runnerSpace.ts"],"mappings":";;;;;;;cAsBa;;;;;;UAYI;EACf,2BAA2B;;EAE3B,YAAY,iBAAiB;EAC7B,UAAU;EACV,aAAa,YAAY,oBAAoB;;cAGlC,6BAA6B;mBAYrB;mBACA;UAZX;UACA;UACA;UACA;UACA;UACA;UACA;EAGN,YAAA,KAAK,gBACL,OAAO,OACU,SAAS,oBACT,OAAO;EAK1B,YAAY;YAgBF,aAAa;YAgCb,WAAW,eAAe;UAO5B;UAQA;UAcA;UAOA;UAKA;UAiBA;UAUA;UAkBA;UAOA;UA2BA;UAKA;UA2BA;UAOA;;iBAUY,gBAAgB,KAAK,kBAAkB,SAAS,qBAAqB"}
1
+ {"version":3,"file":"runnerSpace.d.mts","names":[],"sources":["../../src/tui/runnerSpace.ts"],"mappings":";;;;;;;cAuBa;;;;;;UAYI;EACf,2BAA2B;;EAE3B,YAAY,iBAAiB;EAC7B,UAAU;EACV,aAAa,YAAY,oBAAoB;;cAGlC,6BAA6B;mBAYrB;mBACA;UAZX;UACA;UACA;UACA;UACA;UACA;UACA;EAGN,YAAA,KAAK,gBACL,OAAO,OACU,SAAS,oBACT,OAAO;EAK1B,YAAY;YAgBF,aAAa;YAgCb,WAAW,eAAe;UAO5B;UAQA;UAcA;UAOA;UAKA;UAiBA;UAUA;UAkBA;UAOA;UA2BA;UAKA;UA2BA;UAOA;;iBAUY,gBAAgB,KAAK,kBAAkB,SAAS,qBAAqB"}
@@ -1,6 +1,6 @@
1
- import{toRunnerRows as e}from"./format.mjs";import{matchesKey as t,truncateToWidth as n}from"@earendil-works/pi-tui";import{DOOM_FULLSCREEN_UI_OPTIONS as r,DOOM_OVERLAY_ACCENT as i,DoomOverlay as a}from"@agimon-ai/doompi-ui/components/doomOverlay";const o=r.overlayOptions;var s=class extends a{options;done;selected=0;attached;viewedLog;unsubscribe;logRefresh;notice;stopReason;constructor(e,t,n,r){super(e,t),this.options=n,this.done=r}handleInput(e){if(this.stopReason){this.handleStopReasonInput(e);return}if(this.attached){this.handleAttachedInput(e);return}if(this.viewedLog){t(e,`escape`)&&this.detach();return}this.handleListInput(e)}getChrome(){let e=this.options.getRunners();return{title:`RUNNER SPACE`,accent:i,breadcrumb:`SPC › r / runners › r / runner space`,headerRight:this.attached?`attached · ${this.attached.name}`:this.viewedLog?`logs · ${this.viewedLog.name}`:`${e.length} runners · this worktree`,footer:this.attached?`esc back · everything else goes to the runner`:this.viewedLog?`esc back · live stdout/stderr`:`↑↓/jk select · enter open · s stop · r reason · esc close`,footerHints:this.attached||this.viewedLog?void 0:[[`↑↓/jk`,`select`],[`enter`,`open`],[`s`,`stop`],[`r`,`reason`],[`esc`,`close`]],footerRight:this.attached?`live tty`:this.viewedLog?`live log`:e.length>0?`${Math.min(this.selected,e.length-1)+1}/${e.length}`:`empty`}}renderBody(e,t){let r=t=>n(t,e,`…`);return this.attached?this.renderAttached(r,t):this.viewedLog?this.renderLog(r,t):this.renderList(r,t)}handleAttachedInput(e){if(t(e,`escape`)){this.detach();return}this.attached?.write(e)}handleListInput(e){let n=this.options.getRunners();if(t(e,`escape`)||t(e,`ctrl+c`)){this.close();return}t(e,`up`)||e===`k`?this.move(-1,n.length):t(e,`down`)||e===`j`?this.move(1,n.length):e===`\r`||e===`
1
+ import{toRunnerRows as e}from"./format.mjs";import{matchesKey as t,truncateToWidth as n}from"@earendil-works/pi-tui";import{DOOM_FULLSCREEN_UI_OPTIONS as r,DOOM_NAVIGATION_KEYS as i,DOOM_OVERLAY_ACCENT as a,DoomOverlay as o}from"@agimon-ai/doompi-ui/components/doomOverlay";const s=r.overlayOptions,c=`${i.list} select · enter open · s stop · r reason · esc close`;var l=class extends o{options;done;selected=0;attached;viewedLog;unsubscribe;logRefresh;notice;stopReason;constructor(e,t,n,r){super(e,t),this.options=n,this.done=r}handleInput(e){if(this.stopReason){this.handleStopReasonInput(e);return}if(this.attached){this.handleAttachedInput(e);return}if(this.viewedLog){t(e,`escape`)&&this.detach();return}this.handleListInput(e)}getChrome(){let e=this.options.getRunners();return{title:`RUNNER SPACE`,accent:a,breadcrumb:`SPC › r / runners › r / runner space`,headerRight:this.attached?`attached · ${this.attached.name}`:this.viewedLog?`logs · ${this.viewedLog.name}`:`${e.length} runners · this worktree`,footer:this.attached?`esc back · everything else goes to the runner`:this.viewedLog?`esc back · live stdout/stderr`:c,footerHints:this.attached||this.viewedLog?void 0:[[i.list,`select`],[`enter`,`open`],[`s`,`stop`],[`r`,`reason`],[`esc`,`close`]],footerRight:this.attached?`live tty`:this.viewedLog?`live log`:e.length>0?`${Math.min(this.selected,e.length-1)+1}/${e.length}`:`empty`}}renderBody(e,t){let r=t=>n(t,e,`…`);return this.attached?this.renderAttached(r,t):this.viewedLog?this.renderLog(r,t):this.renderList(r,t)}handleAttachedInput(e){if(t(e,`escape`)){this.detach();return}this.attached?.write(e)}handleListInput(e){let n=this.options.getRunners();if(t(e,`escape`)||t(e,`ctrl+c`)){this.close();return}t(e,`up`)||e===`k`?this.move(-1,n.length):t(e,`down`)||e===`j`?this.move(1,n.length):e===`\r`||e===`
2
2
  `?this.attach(n):e===`s`?this.stop(n):e===`r`&&this.promptStopReason(n)}move(e,t){t!==0&&(this.selected=Math.max(0,Math.min(t-1,this.selected+e)),this.notice=void 0,this.tui.requestRender())}current(e){if(e.length!==0)return e[Math.min(this.selected,e.length-1)]}attach(e){let t=this.current(e);if(!t)return;let n=t.interactive?this.options.getPtyRun(t.name):void 0;this.notice=void 0,n?(this.attached=n,this.unsubscribe=n.onData(()=>this.tui.requestRender())):(this.viewedLog=t,this.logRefresh=setInterval(()=>this.tui.requestRender(!0),250),this.logRefresh.unref?.()),this.tui.requestRender(!0)}detach(){this.unsubscribe?.(),this.unsubscribe=void 0,this.logRefresh&&clearInterval(this.logRefresh),this.logRefresh=void 0,this.attached=void 0,this.viewedLog=void 0,this.tui.requestRender(!0)}stop(e){let t=this.current(e);t&&this.options.stopRunner(t.id).then(()=>{this.notice={text:`Stopped ${t.name}`,color:`success`},this.tui.requestRender(!0)}).catch(e=>{this.notice={text:e instanceof Error?e.message:String(e),color:`error`},this.tui.requestRender()})}promptStopReason(e){let t=this.current(e);t&&(this.stopReason={runner:t,text:``},this.tui.requestRender())}handleStopReasonInput(e){let n=this.stopReason;if(n){if(t(e,`escape`)||t(e,`ctrl+c`)){this.stopReason=void 0,this.tui.requestRender();return}if(e===`\r`||e===`
3
3
  `){this.stopReason=void 0,this.options.stopRunner(n.runner.id,n.text.trim()||void 0).then(()=>{this.notice={text:`Stopped ${n.runner.name}`,color:`success`},this.tui.requestRender(!0)}).catch(e=>{this.notice={text:e instanceof Error?e.message:String(e),color:`error`},this.tui.requestRender()});return}t(e,`backspace`)?n.text=n.text.slice(0,-1):e.includes(`\x1B`)||(n.text+=e),this.tui.requestRender()}}close(){this.detach(),this.done(void 0)}renderList(t,n){let r=this.options.getRunners(),i=e(r,Date.now()),a=[];if(this.stopReason)return[t(`Stop reason for ${this.stopReason.runner.name}: ${this.stopReason.text}`)];if(i.length===0)a.push(t(this.theme.fg(`dim`,`No background runners. bash backgrounds anything that outlives the threshold.`)));else{let e=this.notice?2:0,r=Math.max(1,n-e),o=Math.max(0,i.length-r),s=Math.min(o,Math.max(0,this.selected-r+1));i.slice(s,s+r).forEach((e,n)=>{let r=s+n===Math.min(this.selected,i.length-1)?`›`:` `,o=e.interactive?` [tty]`:``;a.push(t(`${r} ${e.name}${o} ${this.theme.fg(`dim`,e.detail)}`))})}return this.notice&&a.push(``,t(this.theme.fg(this.notice.color,this.notice.text))),a.slice(0,n)}renderAttached(e,t){let n=this.attached;return n?n.screen().split(`
4
4
  `).slice(-t).map(e):[]}renderLog(e,t){let n=this.viewedLog;if(!n)return[];let r=this.options.readLog(n.logPath);return r?r.split(`
5
- `).slice(-t).map(e):[e(this.theme.fg(`dim`,`Waiting for output…`))]}};async function c(e,t){await e.ui.custom((e,n,r,i)=>new s(e,n,t,i),r)}export{o as RUNNER_SPACE_OVERLAY_OPTIONS,s as RunnerSpaceComponent,c as openRunnerSpace};
5
+ `).slice(-t).map(e):[e(this.theme.fg(`dim`,`Waiting for output…`))]}};async function u(e,t){await e.ui.custom((e,n,r,i)=>new l(e,n,t,i),r)}export{s as RUNNER_SPACE_OVERLAY_OPTIONS,l as RunnerSpaceComponent,u as openRunnerSpace};
6
6
  //# sourceMappingURL=runnerSpace.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"runnerSpace.mjs","names":[],"sources":["../../src/tui/runnerSpace.ts"],"sourcesContent":["/**\n * Runner Space: the `SPC r r` overlay.\n *\n * Two modes behind one component. The list shows every runner in this worktree\n * and can stop them; attaching to an interactive runner renders its live\n * terminal and forwards keystrokes to it, which is the whole reason PTY runs\n * exist. Rendering is pure, so the surface can be asserted as text.\n */\n\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/components/doomOverlay';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth } from '@earendil-works/pi-tui';\nimport type { PtyRun } from '../services/PtyHost/index.ts';\nimport type { RunnerRecord } from '../services/RunnerRegistry/index.ts';\nimport { toRunnerRows } from './format.ts';\n\nexport const RUNNER_SPACE_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nconst KEY_ENTER = '\\r';\nconst KEY_NEWLINE = '\\n';\nconst TITLE = 'RUNNER SPACE';\nconst EMPTY_MESSAGE = 'No background runners. bash backgrounds anything that outlives the threshold.';\nconst LIST_FOOTER = '↑↓/jk select · enter open · s stop · r reason · esc close';\nconst ATTACH_FOOTER = 'esc back · everything else goes to the runner';\nconst LOG_FOOTER = 'esc back · live stdout/stderr';\nconst LOG_REFRESH_MS = 250;\nconst ELLIPSIS = '…';\n\nexport interface RunnerSpaceOptions {\n getRunners: () => readonly RunnerRecord[];\n /** The hosted terminal for a runner, when this session owns one. */\n getPtyRun: (name: string) => PtyRun | undefined;\n readLog: (logPath: string) => string;\n stopRunner: (id: string, reason?: string) => Promise<void>;\n}\n\nexport class RunnerSpaceComponent extends DoomOverlay {\n private selected = 0;\n private attached: PtyRun | undefined;\n private viewedLog: RunnerRecord | undefined;\n private unsubscribe: (() => void) | undefined;\n private logRefresh: ReturnType<typeof setInterval> | undefined;\n private notice: { text: string; color: 'success' | 'error' } | undefined;\n private stopReason: { runner: RunnerRecord; text: string } | undefined;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly options: RunnerSpaceOptions,\n private readonly done: (result: undefined) => void,\n ) {\n super(tui, theme);\n }\n\n handleInput(data: string): void {\n if (this.stopReason) {\n this.handleStopReasonInput(data);\n return;\n }\n if (this.attached) {\n this.handleAttachedInput(data);\n return;\n }\n if (this.viewedLog) {\n if (matchesKey(data, 'escape')) this.detach();\n return;\n }\n this.handleListInput(data);\n }\n\n protected getChrome(): DoomOverlayChrome {\n const runners = this.options.getRunners();\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: 'SPC › r / runners › r / runner space',\n headerRight: this.attached\n ? `attached · ${this.attached.name}`\n : this.viewedLog\n ? `logs · ${this.viewedLog.name}`\n : `${runners.length} runners · this worktree`,\n footer: this.attached ? ATTACH_FOOTER : this.viewedLog ? LOG_FOOTER : LIST_FOOTER,\n footerHints:\n this.attached || this.viewedLog\n ? undefined\n : [\n ['↑↓/jk', 'select'],\n ['enter', 'open'],\n ['s', 'stop'],\n ['r', 'reason'],\n ['esc', 'close'],\n ],\n footerRight: this.attached\n ? 'live tty'\n : this.viewedLog\n ? 'live log'\n : runners.length > 0\n ? `${Math.min(this.selected, runners.length - 1) + 1}/${runners.length}`\n : 'empty',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n const truncate = (line: string): string => truncateToWidth(line, width, ELLIPSIS);\n if (this.attached) return this.renderAttached(truncate, height);\n if (this.viewedLog) return this.renderLog(truncate, height);\n return this.renderList(truncate, height);\n }\n\n private handleAttachedInput(data: string): void {\n if (matchesKey(data, 'escape')) {\n this.detach();\n return;\n }\n this.attached?.write(data);\n }\n\n private handleListInput(data: string): void {\n const runners = this.options.getRunners();\n\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.close();\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') this.move(-1, runners.length);\n else if (matchesKey(data, 'down') || data === 'j') this.move(1, runners.length);\n else if (data === KEY_ENTER || data === KEY_NEWLINE) this.attach(runners);\n else if (data === 's') this.stop(runners);\n else if (data === 'r') this.promptStopReason(runners);\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 this.notice = undefined;\n this.tui.requestRender();\n }\n\n private current(runners: readonly RunnerRecord[]): RunnerRecord | undefined {\n if (runners.length === 0) return undefined;\n return runners[Math.min(this.selected, runners.length - 1)];\n }\n\n private attach(runners: readonly RunnerRecord[]): void {\n const record = this.current(runners);\n if (!record) return;\n\n const run = record.interactive ? this.options.getPtyRun(record.name) : undefined;\n this.notice = undefined;\n if (run) {\n this.attached = run;\n this.unsubscribe = run.onData(() => this.tui.requestRender());\n } else {\n this.viewedLog = record;\n this.logRefresh = setInterval(() => this.tui.requestRender(true), LOG_REFRESH_MS);\n this.logRefresh.unref?.();\n }\n this.tui.requestRender(true);\n }\n\n private detach(): void {\n this.unsubscribe?.();\n this.unsubscribe = undefined;\n if (this.logRefresh) clearInterval(this.logRefresh);\n this.logRefresh = undefined;\n this.attached = undefined;\n this.viewedLog = undefined;\n this.tui.requestRender(true);\n }\n\n private stop(runners: readonly RunnerRecord[]): void {\n const record = this.current(runners);\n if (!record) return;\n\n this.options\n .stopRunner(record.id)\n .then(() => {\n this.notice = { text: `Stopped ${record.name}`, color: 'success' };\n this.tui.requestRender(true);\n })\n .catch((error: unknown) => {\n // Shown rather than swallowed: a failed stop means the process is still\n // running and the operator needs to know.\n this.notice = { text: error instanceof Error ? error.message : String(error), color: 'error' };\n this.tui.requestRender();\n });\n }\n\n private promptStopReason(runners: readonly RunnerRecord[]): void {\n const runner = this.current(runners);\n if (!runner) return;\n this.stopReason = { runner, text: '' };\n this.tui.requestRender();\n }\n\n private handleStopReasonInput(data: string): void {\n const prompt = this.stopReason;\n if (!prompt) return;\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.stopReason = undefined;\n this.tui.requestRender();\n return;\n }\n if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.stopReason = undefined;\n this.options\n .stopRunner(prompt.runner.id, prompt.text.trim() || undefined)\n .then(() => {\n this.notice = { text: `Stopped ${prompt.runner.name}`, color: 'success' };\n this.tui.requestRender(true);\n })\n .catch((error: unknown) => {\n this.notice = { text: error instanceof Error ? error.message : String(error), color: 'error' };\n this.tui.requestRender();\n });\n return;\n }\n if (matchesKey(data, 'backspace')) prompt.text = prompt.text.slice(0, -1);\n else if (!data.includes('\\u001b')) prompt.text += data;\n this.tui.requestRender();\n }\n\n private close(): void {\n this.detach();\n this.done(undefined);\n }\n\n private renderList(truncate: (line: string) => string, height: number): string[] {\n const runners = this.options.getRunners();\n const rows = toRunnerRows(runners, Date.now());\n const lines: string[] = [];\n\n if (this.stopReason) {\n return [truncate(`Stop reason for ${this.stopReason.runner.name}: ${this.stopReason.text}`)];\n }\n\n if (rows.length === 0) lines.push(truncate(this.theme.fg('dim', EMPTY_MESSAGE)));\n else {\n const noticeRows = this.notice ? 2 : 0;\n const capacity = Math.max(1, height - noticeRows);\n const maxStart = Math.max(0, rows.length - capacity);\n const start = Math.min(maxStart, Math.max(0, this.selected - capacity + 1));\n rows.slice(start, start + capacity).forEach((row, offset) => {\n const index = start + offset;\n const marker = index === Math.min(this.selected, rows.length - 1) ? '›' : ' ';\n const tty = row.interactive ? ' [tty]' : '';\n lines.push(truncate(`${marker} ${row.name}${tty} ${this.theme.fg('dim', row.detail)}`));\n });\n }\n\n if (this.notice) lines.push('', truncate(this.theme.fg(this.notice.color, this.notice.text)));\n return lines.slice(0, height);\n }\n\n private renderAttached(truncate: (line: string) => string, height: number): string[] {\n const run = this.attached;\n if (!run) return [];\n\n return run.screen().split('\\n').slice(-height).map(truncate);\n }\n\n private renderLog(truncate: (line: string) => string, height: number): string[] {\n const record = this.viewedLog;\n if (!record) return [];\n\n const text = this.options.readLog(record.logPath);\n if (!text) return [truncate(this.theme.fg('dim', 'Waiting for output…'))];\n return text.split('\\n').slice(-height).map(truncate);\n }\n}\n\nexport async function openRunnerSpace(ctx: ExtensionContext, options: RunnerSpaceOptions): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new RunnerSpaceComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"wPAsBA,MAAa,EAA+B,EAA2B,eAoBvE,IAAa,EAAb,cAA0C,CAAY,CAYjC,QACA,KAZnB,SAAmB,EACnB,SACA,UACA,YACA,WACA,OACA,WAEA,YACE,EACA,EACA,EACA,EACA,CACA,MAAM,EAAK,CAAK,EAHC,KAAA,QAAA,EACA,KAAA,KAAA,CAGnB,CAEA,YAAY,EAAoB,CAC9B,GAAI,KAAK,WAAY,CACnB,KAAK,sBAAsB,CAAI,EAC/B,MACF,CACA,GAAI,KAAK,SAAU,CACjB,KAAK,oBAAoB,CAAI,EAC7B,MACF,CACA,GAAI,KAAK,UAAW,CACd,EAAW,EAAM,QAAQ,GAAG,KAAK,OAAO,EAC5C,MACF,CACA,KAAK,gBAAgB,CAAI,CAC3B,CAEA,WAAyC,CACvC,IAAM,EAAU,KAAK,QAAQ,WAAW,EACxC,MAAO,CACL,MAAO,eACP,OAAQ,EACR,WAAY,uCACZ,YAAa,KAAK,SACd,cAAc,KAAK,SAAS,OAC5B,KAAK,UACH,UAAU,KAAK,UAAU,OACzB,GAAG,EAAQ,OAAO,0BACxB,OAAQ,KAAK,SAAW,gDAAgB,KAAK,UAAY,gCAAa,4DACtE,YACE,KAAK,UAAY,KAAK,UAClB,IAAA,GACA,CACE,CAAC,QAAS,QAAQ,EAClB,CAAC,QAAS,MAAM,EAChB,CAAC,IAAK,MAAM,EACZ,CAAC,IAAK,QAAQ,EACd,CAAC,MAAO,OAAO,CACjB,EACN,YAAa,KAAK,SACd,WACA,KAAK,UACH,WACA,EAAQ,OAAS,EACf,GAAG,KAAK,IAAI,KAAK,SAAU,EAAQ,OAAS,CAAC,EAAI,EAAE,GAAG,EAAQ,SAC9D,OACV,CACF,CAEA,WAAqB,EAAe,EAA0B,CAC5D,IAAM,EAAY,GAAyB,EAAgB,EAAM,EAAO,GAAQ,EAGhF,OAFI,KAAK,SAAiB,KAAK,eAAe,EAAU,CAAM,EAC1D,KAAK,UAAkB,KAAK,UAAU,EAAU,CAAM,EACnD,KAAK,WAAW,EAAU,CAAM,CACzC,CAEA,oBAA4B,EAAoB,CAC9C,GAAI,EAAW,EAAM,QAAQ,EAAG,CAC9B,KAAK,OAAO,EACZ,MACF,CACA,KAAK,UAAU,MAAM,CAAI,CAC3B,CAEA,gBAAwB,EAAoB,CAC1C,IAAM,EAAU,KAAK,QAAQ,WAAW,EAExC,GAAI,EAAW,EAAM,QAAQ,GAAK,EAAW,EAAM,QAAQ,EAAG,CAC5D,KAAK,MAAM,EACX,MACF,CACI,EAAW,EAAM,IAAI,GAAK,IAAS,IAAK,KAAK,KAAK,GAAI,EAAQ,MAAM,EAC/D,EAAW,EAAM,MAAM,GAAK,IAAS,IAAK,KAAK,KAAK,EAAG,EAAQ,MAAM,EACrE,IAAS,MAAa,IAAS;EAAa,KAAK,OAAO,CAAO,EAC/D,IAAS,IAAK,KAAK,KAAK,CAAO,EAC/B,IAAS,KAAK,KAAK,iBAAiB,CAAO,CACtD,CAEA,KAAa,EAAe,EAAqB,CAC3C,IAAU,IACd,KAAK,SAAW,KAAK,IAAI,EAAG,KAAK,IAAI,EAAQ,EAAG,KAAK,SAAW,CAAK,CAAC,EACtE,KAAK,OAAS,IAAA,GACd,KAAK,IAAI,cAAc,EACzB,CAEA,QAAgB,EAA4D,CACtE,KAAQ,SAAW,EACvB,OAAO,EAAQ,KAAK,IAAI,KAAK,SAAU,EAAQ,OAAS,CAAC,EAC3D,CAEA,OAAe,EAAwC,CACrD,IAAM,EAAS,KAAK,QAAQ,CAAO,EACnC,GAAI,CAAC,EAAQ,OAEb,IAAM,EAAM,EAAO,YAAc,KAAK,QAAQ,UAAU,EAAO,IAAI,EAAI,IAAA,GACvE,KAAK,OAAS,IAAA,GACV,GACF,KAAK,SAAW,EAChB,KAAK,YAAc,EAAI,WAAa,KAAK,IAAI,cAAc,CAAC,IAE5D,KAAK,UAAY,EACjB,KAAK,WAAa,gBAAkB,KAAK,IAAI,cAAc,EAAI,EAAG,GAAc,EAChF,KAAK,WAAW,QAAQ,GAE1B,KAAK,IAAI,cAAc,EAAI,CAC7B,CAEA,QAAuB,CACrB,KAAK,cAAc,EACnB,KAAK,YAAc,IAAA,GACf,KAAK,YAAY,cAAc,KAAK,UAAU,EAClD,KAAK,WAAa,IAAA,GAClB,KAAK,SAAW,IAAA,GAChB,KAAK,UAAY,IAAA,GACjB,KAAK,IAAI,cAAc,EAAI,CAC7B,CAEA,KAAa,EAAwC,CACnD,IAAM,EAAS,KAAK,QAAQ,CAAO,EAC9B,GAEL,KAAK,QACF,WAAW,EAAO,EAAE,CAAC,CACrB,SAAW,CACV,KAAK,OAAS,CAAE,KAAM,WAAW,EAAO,OAAQ,MAAO,SAAU,EACjE,KAAK,IAAI,cAAc,EAAI,CAC7B,CAAC,CAAC,CACD,MAAO,GAAmB,CAGzB,KAAK,OAAS,CAAE,KAAM,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAAG,MAAO,OAAQ,EAC7F,KAAK,IAAI,cAAc,CACzB,CAAC,CACL,CAEA,iBAAyB,EAAwC,CAC/D,IAAM,EAAS,KAAK,QAAQ,CAAO,EAC9B,IACL,KAAK,WAAa,CAAE,SAAQ,KAAM,EAAG,EACrC,KAAK,IAAI,cAAc,EACzB,CAEA,sBAA8B,EAAoB,CAChD,IAAM,EAAS,KAAK,WACf,KACL,IAAI,EAAW,EAAM,QAAQ,GAAK,EAAW,EAAM,QAAQ,EAAG,CAC5D,KAAK,WAAa,IAAA,GAClB,KAAK,IAAI,cAAc,EACvB,MACF,CACA,GAAI,IAAS,MAAa,IAAS;EAAa,CAC9C,KAAK,WAAa,IAAA,GAClB,KAAK,QACF,WAAW,EAAO,OAAO,GAAI,EAAO,KAAK,KAAK,GAAK,IAAA,EAAS,CAAC,CAC7D,SAAW,CACV,KAAK,OAAS,CAAE,KAAM,WAAW,EAAO,OAAO,OAAQ,MAAO,SAAU,EACxE,KAAK,IAAI,cAAc,EAAI,CAC7B,CAAC,CAAC,CACD,MAAO,GAAmB,CACzB,KAAK,OAAS,CAAE,KAAM,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAAG,MAAO,OAAQ,EAC7F,KAAK,IAAI,cAAc,CACzB,CAAC,EACH,MACF,CACI,EAAW,EAAM,WAAW,EAAG,EAAO,KAAO,EAAO,KAAK,MAAM,EAAG,EAAE,EAC9D,EAAK,SAAS,MAAQ,IAAG,EAAO,MAAQ,GAClD,KAAK,IAAI,cAAc,CAjBvB,CAkBF,CAEA,OAAsB,CACpB,KAAK,OAAO,EACZ,KAAK,KAAK,IAAA,EAAS,CACrB,CAEA,WAAmB,EAAoC,EAA0B,CAC/E,IAAM,EAAU,KAAK,QAAQ,WAAW,EAClC,EAAO,EAAa,EAAS,KAAK,IAAI,CAAC,EACvC,EAAkB,CAAC,EAEzB,GAAI,KAAK,WACP,MAAO,CAAC,EAAS,mBAAmB,KAAK,WAAW,OAAO,KAAK,IAAI,KAAK,WAAW,MAAM,CAAC,EAG7F,GAAI,EAAK,SAAW,EAAG,EAAM,KAAK,EAAS,KAAK,MAAM,GAAG,MAAO,+EAAa,CAAC,CAAC,MAC1E,CACH,IAAM,EAAa,KAAK,OAAS,EAAI,EAC/B,EAAW,KAAK,IAAI,EAAG,EAAS,CAAU,EAC1C,EAAW,KAAK,IAAI,EAAG,EAAK,OAAS,CAAQ,EAC7C,EAAQ,KAAK,IAAI,EAAU,KAAK,IAAI,EAAG,KAAK,SAAW,EAAW,CAAC,CAAC,EAC1E,EAAK,MAAM,EAAO,EAAQ,CAAQ,CAAC,CAAC,SAAS,EAAK,IAAW,CAE3D,IAAM,EADQ,EAAQ,IACG,KAAK,IAAI,KAAK,SAAU,EAAK,OAAS,CAAC,EAAI,IAAM,IACpE,EAAM,EAAI,YAAc,SAAW,GACzC,EAAM,KAAK,EAAS,GAAG,EAAO,GAAG,EAAI,OAAO,EAAI,IAAI,KAAK,MAAM,GAAG,MAAO,EAAI,MAAM,GAAG,CAAC,CACzF,CAAC,CACH,CAGA,OADI,KAAK,QAAQ,EAAM,KAAK,GAAI,EAAS,KAAK,MAAM,GAAG,KAAK,OAAO,MAAO,KAAK,OAAO,IAAI,CAAC,CAAC,EACrF,EAAM,MAAM,EAAG,CAAM,CAC9B,CAEA,eAAuB,EAAoC,EAA0B,CACnF,IAAM,EAAM,KAAK,SAGjB,OAFK,EAEE,EAAI,OAAO,CAAC,CAAC,MAAM;CAAI,CAAC,CAAC,MAAM,CAAC,CAAM,CAAC,CAAC,IAAI,CAAQ,EAF1C,CAAC,CAGpB,CAEA,UAAkB,EAAoC,EAA0B,CAC9E,IAAM,EAAS,KAAK,UACpB,GAAI,CAAC,EAAQ,MAAO,CAAC,EAErB,IAAM,EAAO,KAAK,QAAQ,QAAQ,EAAO,OAAO,EAEhD,OADK,EACE,EAAK,MAAM;CAAI,CAAC,CAAC,MAAM,CAAC,CAAM,CAAC,CAAC,IAAI,CAAQ,EADjC,CAAC,EAAS,KAAK,MAAM,GAAG,MAAO,qBAAqB,CAAC,CAAC,CAE1E,CACF,EAEA,eAAsB,EAAgB,EAAuB,EAA4C,CACvG,MAAM,EAAI,GAAG,QACV,EAAK,EAAO,EAAc,IAAS,IAAI,EAAqB,EAAK,EAAO,EAAS,CAAI,EACtF,CACF,CACF"}
1
+ {"version":3,"file":"runnerSpace.mjs","names":[],"sources":["../../src/tui/runnerSpace.ts"],"sourcesContent":["/**\n * Runner Space: the `SPC r r` overlay.\n *\n * Two modes behind one component. The list shows every runner in this worktree\n * and can stop them; attaching to an interactive runner renders its live\n * terminal and forwards keystrokes to it, which is the whole reason PTY runs\n * exist. Rendering is pure, so the surface can be asserted as text.\n */\n\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_NAVIGATION_KEYS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/components/doomOverlay';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth } from '@earendil-works/pi-tui';\nimport type { PtyRun } from '../services/PtyHost/index.ts';\nimport type { RunnerRecord } from '../services/RunnerRegistry/index.ts';\nimport { toRunnerRows } from './format.ts';\n\nexport const RUNNER_SPACE_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nconst KEY_ENTER = '\\r';\nconst KEY_NEWLINE = '\\n';\nconst TITLE = 'RUNNER SPACE';\nconst EMPTY_MESSAGE = 'No background runners. bash backgrounds anything that outlives the threshold.';\nconst LIST_FOOTER = `${DOOM_NAVIGATION_KEYS.list} select · enter open · s stop · r reason · esc close`;\nconst ATTACH_FOOTER = 'esc back · everything else goes to the runner';\nconst LOG_FOOTER = 'esc back · live stdout/stderr';\nconst LOG_REFRESH_MS = 250;\nconst ELLIPSIS = '…';\n\nexport interface RunnerSpaceOptions {\n getRunners: () => readonly RunnerRecord[];\n /** The hosted terminal for a runner, when this session owns one. */\n getPtyRun: (name: string) => PtyRun | undefined;\n readLog: (logPath: string) => string;\n stopRunner: (id: string, reason?: string) => Promise<void>;\n}\n\nexport class RunnerSpaceComponent extends DoomOverlay {\n private selected = 0;\n private attached: PtyRun | undefined;\n private viewedLog: RunnerRecord | undefined;\n private unsubscribe: (() => void) | undefined;\n private logRefresh: ReturnType<typeof setInterval> | undefined;\n private notice: { text: string; color: 'success' | 'error' } | undefined;\n private stopReason: { runner: RunnerRecord; text: string } | undefined;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly options: RunnerSpaceOptions,\n private readonly done: (result: undefined) => void,\n ) {\n super(tui, theme);\n }\n\n handleInput(data: string): void {\n if (this.stopReason) {\n this.handleStopReasonInput(data);\n return;\n }\n if (this.attached) {\n this.handleAttachedInput(data);\n return;\n }\n if (this.viewedLog) {\n if (matchesKey(data, 'escape')) this.detach();\n return;\n }\n this.handleListInput(data);\n }\n\n protected getChrome(): DoomOverlayChrome {\n const runners = this.options.getRunners();\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: 'SPC › r / runners › r / runner space',\n headerRight: this.attached\n ? `attached · ${this.attached.name}`\n : this.viewedLog\n ? `logs · ${this.viewedLog.name}`\n : `${runners.length} runners · this worktree`,\n footer: this.attached ? ATTACH_FOOTER : this.viewedLog ? LOG_FOOTER : LIST_FOOTER,\n footerHints:\n this.attached || this.viewedLog\n ? undefined\n : [\n [DOOM_NAVIGATION_KEYS.list, 'select'],\n ['enter', 'open'],\n ['s', 'stop'],\n ['r', 'reason'],\n ['esc', 'close'],\n ],\n footerRight: this.attached\n ? 'live tty'\n : this.viewedLog\n ? 'live log'\n : runners.length > 0\n ? `${Math.min(this.selected, runners.length - 1) + 1}/${runners.length}`\n : 'empty',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n const truncate = (line: string): string => truncateToWidth(line, width, ELLIPSIS);\n if (this.attached) return this.renderAttached(truncate, height);\n if (this.viewedLog) return this.renderLog(truncate, height);\n return this.renderList(truncate, height);\n }\n\n private handleAttachedInput(data: string): void {\n if (matchesKey(data, 'escape')) {\n this.detach();\n return;\n }\n this.attached?.write(data);\n }\n\n private handleListInput(data: string): void {\n const runners = this.options.getRunners();\n\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.close();\n return;\n }\n if (matchesKey(data, 'up') || data === 'k') this.move(-1, runners.length);\n else if (matchesKey(data, 'down') || data === 'j') this.move(1, runners.length);\n else if (data === KEY_ENTER || data === KEY_NEWLINE) this.attach(runners);\n else if (data === 's') this.stop(runners);\n else if (data === 'r') this.promptStopReason(runners);\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 this.notice = undefined;\n this.tui.requestRender();\n }\n\n private current(runners: readonly RunnerRecord[]): RunnerRecord | undefined {\n if (runners.length === 0) return undefined;\n return runners[Math.min(this.selected, runners.length - 1)];\n }\n\n private attach(runners: readonly RunnerRecord[]): void {\n const record = this.current(runners);\n if (!record) return;\n\n const run = record.interactive ? this.options.getPtyRun(record.name) : undefined;\n this.notice = undefined;\n if (run) {\n this.attached = run;\n this.unsubscribe = run.onData(() => this.tui.requestRender());\n } else {\n this.viewedLog = record;\n this.logRefresh = setInterval(() => this.tui.requestRender(true), LOG_REFRESH_MS);\n this.logRefresh.unref?.();\n }\n this.tui.requestRender(true);\n }\n\n private detach(): void {\n this.unsubscribe?.();\n this.unsubscribe = undefined;\n if (this.logRefresh) clearInterval(this.logRefresh);\n this.logRefresh = undefined;\n this.attached = undefined;\n this.viewedLog = undefined;\n this.tui.requestRender(true);\n }\n\n private stop(runners: readonly RunnerRecord[]): void {\n const record = this.current(runners);\n if (!record) return;\n\n this.options\n .stopRunner(record.id)\n .then(() => {\n this.notice = { text: `Stopped ${record.name}`, color: 'success' };\n this.tui.requestRender(true);\n })\n .catch((error: unknown) => {\n // Shown rather than swallowed: a failed stop means the process is still\n // running and the operator needs to know.\n this.notice = { text: error instanceof Error ? error.message : String(error), color: 'error' };\n this.tui.requestRender();\n });\n }\n\n private promptStopReason(runners: readonly RunnerRecord[]): void {\n const runner = this.current(runners);\n if (!runner) return;\n this.stopReason = { runner, text: '' };\n this.tui.requestRender();\n }\n\n private handleStopReasonInput(data: string): void {\n const prompt = this.stopReason;\n if (!prompt) return;\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.stopReason = undefined;\n this.tui.requestRender();\n return;\n }\n if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.stopReason = undefined;\n this.options\n .stopRunner(prompt.runner.id, prompt.text.trim() || undefined)\n .then(() => {\n this.notice = { text: `Stopped ${prompt.runner.name}`, color: 'success' };\n this.tui.requestRender(true);\n })\n .catch((error: unknown) => {\n this.notice = { text: error instanceof Error ? error.message : String(error), color: 'error' };\n this.tui.requestRender();\n });\n return;\n }\n if (matchesKey(data, 'backspace')) prompt.text = prompt.text.slice(0, -1);\n else if (!data.includes('\\u001b')) prompt.text += data;\n this.tui.requestRender();\n }\n\n private close(): void {\n this.detach();\n this.done(undefined);\n }\n\n private renderList(truncate: (line: string) => string, height: number): string[] {\n const runners = this.options.getRunners();\n const rows = toRunnerRows(runners, Date.now());\n const lines: string[] = [];\n\n if (this.stopReason) {\n return [truncate(`Stop reason for ${this.stopReason.runner.name}: ${this.stopReason.text}`)];\n }\n\n if (rows.length === 0) lines.push(truncate(this.theme.fg('dim', EMPTY_MESSAGE)));\n else {\n const noticeRows = this.notice ? 2 : 0;\n const capacity = Math.max(1, height - noticeRows);\n const maxStart = Math.max(0, rows.length - capacity);\n const start = Math.min(maxStart, Math.max(0, this.selected - capacity + 1));\n rows.slice(start, start + capacity).forEach((row, offset) => {\n const index = start + offset;\n const marker = index === Math.min(this.selected, rows.length - 1) ? '›' : ' ';\n const tty = row.interactive ? ' [tty]' : '';\n lines.push(truncate(`${marker} ${row.name}${tty} ${this.theme.fg('dim', row.detail)}`));\n });\n }\n\n if (this.notice) lines.push('', truncate(this.theme.fg(this.notice.color, this.notice.text)));\n return lines.slice(0, height);\n }\n\n private renderAttached(truncate: (line: string) => string, height: number): string[] {\n const run = this.attached;\n if (!run) return [];\n\n return run.screen().split('\\n').slice(-height).map(truncate);\n }\n\n private renderLog(truncate: (line: string) => string, height: number): string[] {\n const record = this.viewedLog;\n if (!record) return [];\n\n const text = this.options.readLog(record.logPath);\n if (!text) return [truncate(this.theme.fg('dim', 'Waiting for output…'))];\n return text.split('\\n').slice(-height).map(truncate);\n }\n}\n\nexport async function openRunnerSpace(ctx: ExtensionContext, options: RunnerSpaceOptions): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new RunnerSpaceComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-runner",
3
- "version": "0.0.1-alpha.13",
3
+ "version": "0.0.1-alpha.14",
4
4
  "description": "Pi bash tool replacement with background process supervision",
5
5
  "homepage": "https://agimon.ai",
6
6
  "license": "MIT",
@@ -285,10 +285,10 @@
285
285
  "node-pty": "1.1.0",
286
286
  "reflect-metadata": "0.2.2",
287
287
  "typebox": "1.1.38",
288
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.13",
289
- "@agimon-ai/doompi-ui": "0.0.1-alpha.13",
290
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.13",
291
- "@agimon-ai/foundation-process-registry": "0.29.3"
288
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.14",
289
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.14",
290
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.14",
291
+ "@agimon-ai/foundation-process-registry": "0.29.4"
292
292
  },
293
293
  "devDependencies": {
294
294
  "@earendil-works/pi-coding-agent": "0.84.1",
@@ -312,10 +312,10 @@
312
312
  }
313
313
  },
314
314
  "optionalDependencies": {
315
- "@agimon-ai/doompi-runner-rmux-darwin-arm64": "0.0.1-alpha.13",
316
- "@agimon-ai/doompi-runner-rmux-darwin-x64": "0.0.1-alpha.13",
317
- "@agimon-ai/doompi-runner-rmux-linux-x64": "0.0.1-alpha.13",
318
- "@agimon-ai/doompi-runner-rmux-linux-arm64": "0.0.1-alpha.13"
315
+ "@agimon-ai/doompi-runner-rmux-darwin-arm64": "0.0.1-alpha.14",
316
+ "@agimon-ai/doompi-runner-rmux-darwin-x64": "0.0.1-alpha.14",
317
+ "@agimon-ai/doompi-runner-rmux-linux-x64": "0.0.1-alpha.14",
318
+ "@agimon-ai/doompi-runner-rmux-linux-arm64": "0.0.1-alpha.14"
319
319
  },
320
320
  "engines": {
321
321
  "node": ">=22.19.0"