@agimon-ai/po-runner 0.0.0-alpha.111 → 0.0.0-alpha.113
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-CZGASZQW.mjs","names":[],"sources":["../src/constants/headless.ts","../src/constants/runners.ts","../src/constants/runnersCommand.ts","../src/services/runnersCommand/index.ts","../src/services/headless/index.ts"],"sourcesContent":["export const MS_PER_SECOND = 1000;\n","export const COMMAND_NAME = 'runners';\nexport const COMMAND_DESCRIPTION =\n 'Open Runner Space: background processes started by bash. `/runners stop <id> [reason]` stops one, `/runners start [name=x] [cwd=y] [interactive=true] -- <command>` starts one.';\nexport const SERVER_COMMAND_DESCRIPTION = 'Start and stop supervised background runners without a terminal UI.';\n","export const RUNNERS_STOP_VERB = 'stop';\nexport const RUNNERS_START_VERB = 'start';\nexport const RUNNERS_COMMAND_SEPARATOR = '--';\nexport const NAME_KEY = 'name';\nexport const CWD_KEY = 'cwd';\nexport const INTERACTIVE_KEY = 'interactive';\n/** `interactive=prefer`: a terminal when rmux or tmux is available, plain output otherwise. */\nexport const INTERACTIVE_PREFER = 'prefer';\n","import {\n CWD_KEY,\n INTERACTIVE_KEY,\n INTERACTIVE_PREFER,\n NAME_KEY,\n RUNNERS_COMMAND_SEPARATOR,\n RUNNERS_START_VERB,\n RUNNERS_STOP_VERB,\n} from '../../constants/runnersCommand';\n/** The `/runners` verb that stops one runner without opening Runner Space. */\n/** The `/runners` verb that starts one, for a client that cannot call the bash tool. */\n/**\n * Ends the options and begins the command.\n *\n * A command is arbitrary shell: it may hold `=`, and it may hold flags of its\n * own (`pnpm test -- --watch`). Only the first of these is the separator, so\n * everything the reader typed survives intact after it.\n */\n\nexport interface RunnersStartRequest {\n kind: 'start';\n /** The shell command to run; empty when the reader gave none. */\n command: string;\n cwd?: string;\n name?: string;\n interactive: boolean;\n /** `interactive=prefer`: a terminal when one is available, plain output otherwise. */\n preferInteractive?: boolean;\n}\n\nexport type RunnersCommandRequest =\n /** No arguments: open Runner Space. */\n | { kind: 'space' }\n /** `stop <id> [reason]`: stop one runner headlessly; id is empty when it was left out. */\n | { kind: 'stop'; id: string; reason?: string }\n /** `start [key=value ...] -- <command>`: start one headlessly. */\n | RunnersStartRequest;\n\n/** Splits on whitespace but keeps a double-quoted run together, so a path with spaces survives. */\nfunction tokenize(text: string): string[] {\n return text.match(/(?:\"[^\"]*\"|\\S)+/gu) ?? [];\n}\n\nfunction unquote(value: string): string {\n return value.length >= 2 && value.startsWith('\"') && value.endsWith('\"') ? value.slice(1, -1) : value;\n}\n\n/**\n * Parses `start`'s tail: `key=value` pairs up to the separator, then the\n * command verbatim.\n *\n * Without a separator the whole tail is the command, so typing\n * `/runners start pnpm build` does what it looks like. Options are read only\n * when a separator says where they end, which is what keeps a command such as\n * `FOO=bar pnpm test` from being mistaken for one.\n */\nfunction parseStart(tail: string): RunnersStartRequest {\n const tokens = tokenize(tail);\n const separator = tokens.indexOf(RUNNERS_COMMAND_SEPARATOR);\n if (separator === -1) return { kind: 'start', command: tail.trim(), interactive: false };\n\n const request: RunnersStartRequest = {\n kind: 'start',\n // Sliced from the raw text rather than rejoined from tokens: rejoining\n // would collapse the spacing the reader wrote.\n command: tail.slice(tail.indexOf(RUNNERS_COMMAND_SEPARATOR) + RUNNERS_COMMAND_SEPARATOR.length).trim(),\n interactive: false,\n };\n for (const token of tokens.slice(0, separator)) {\n const split = token.indexOf('=');\n if (split <= 0) continue;\n const value = unquote(token.slice(split + 1));\n const key = token.slice(0, split);\n if (key === NAME_KEY && value !== '') request.name = value;\n if (key === CWD_KEY && value !== '') request.cwd = value;\n if (key === INTERACTIVE_KEY) {\n request.interactive = value === 'true';\n if (value === INTERACTIVE_PREFER) request.preferInteractive = true;\n }\n }\n return request;\n}\n\n/**\n * Parses `/runners` arguments. Anything other than a known verb opens Runner\n * Space, which is what the bare command has always done.\n */\nexport function parseRunnersCommand(args: string): RunnersCommandRequest {\n const trimmed = args.trim();\n const [verb = '', id = '', ...rest] = trimmed.split(/\\s+/u).filter(Boolean);\n if (verb === RUNNERS_START_VERB) return parseStart(trimmed.slice(verb.length).trim());\n if (verb !== RUNNERS_STOP_VERB) return { kind: 'space' };\n const reason = rest.join(' ').trim();\n return { kind: 'stop', id, ...(reason ? { reason } : {}) };\n}\n","import { resolve } from 'node:path';\n\nimport type {\n PoHeadlessCommand,\n PoHeadlessExecutionContext,\n PoHeadlessTool,\n PoHeadlessToolResult,\n} from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, type FileSystem } from '@agimon-ai/po-core/platform';\n\nimport { BASH_TOOL_LABEL, BASH_TOOL_NAME } from '../../constants/bashTool';\nimport { MS_PER_SECOND } from '../../constants/headless';\nimport { COMMAND_NAME, SERVER_COMMAND_DESCRIPTION } from '../../constants/runners';\nimport { type BashParams, BashParamsSchema } from '../../schemas/bashTool';\nimport { formatRunResult } from '../../services/bashResult';\nimport { stopRunnerProcess } from '../../services/reconcile';\nimport { getBackgroundThresholdMs } from '../../services/runnerConfig';\nimport type { RunnerDependencies } from '../../services/runnerDependencies/type';\nimport { parseRunnersCommand } from '../../services/runnersCommand';\nimport type { RunnerSequenceService } from '../runnerSequence';\nimport { isSequenceId } from '../runnerSequenceStore';\n\nexport function createHeadlessBashTool(\n runSequence: RunnerSequenceService['run'] | undefined,\n environment: Readonly<Record<string, string | undefined>>,\n): PoHeadlessTool<typeof BashParamsSchema> {\n return {\n name: BASH_TOOL_NAME,\n label: BASH_TOOL_LABEL,\n description:\n 'Execute an array of Bash commands in order in the current working directory, stopping on first failure. Each command gets a complete saved log; return all log paths and only the last executed result. Do not pipe or redirect output to a file, head, or tail. Runner saves full logs automatically and bounds returned output.',\n promptSnippet: 'Execute shell commands with bounded foreground output and supervised background runners',\n promptGuidelines: [\n `A command sequence still running after ${Math.round(getBackgroundThresholdMs(environment) / MS_PER_SECOND)} seconds remains active as a background runner with an id and streaming log paths.`,\n 'Pass background: true for commands that should remain active, such as dev servers and watchers.',\n 'Always pass command as an array, including for one command. interactive: true applies only to the last command.',\n 'Do not pipe or redirect command output to a file, head, or tail. Full logs are saved automatically; returned output is already bounded.',\n 'Stop background runners when they are no longer needed.',\n ],\n parameters: BashParamsSchema,\n executionMode: 'serial',\n execute: async (\n _toolCallId: string,\n params: BashParams,\n signal: AbortSignal | undefined,\n onUpdate: ((result: PoHeadlessToolResult) => void) | undefined,\n context: PoHeadlessExecutionContext,\n ) => {\n if (signal?.aborted) throw new Error('Operation aborted');\n if (!runSequence) throw new Error('This host does not support supervised Bash command sequences.');\n return runSequence(params, context, _toolCallId, signal, onUpdate);\n },\n };\n}\n\nexport interface HeadlessRunnersCommandOptions {\n /** Checks a typed folder exists before a runner starts in it. */\n fs?: FileSystem | undefined;\n /** Hears every start: why it did not happen, or undefined when it did, so the runners panel can say. */\n onStart?: (sessionId: string, failure: string | undefined) => void;\n sequences?: Pick<RunnerSequenceService, 'get' | 'stop'>;\n}\n\n/** A folder as typed, relative to the session directory; undefined when it is not one. */\nasync function runnerFolder(\n sessionDirectory: string,\n folder: string | undefined,\n fs: FileSystem | undefined,\n): Promise<string | undefined> {\n if (folder === undefined) return sessionDirectory;\n const path = resolve(sessionDirectory, folder);\n if (!fs) return path;\n const info = await fs.fileInfo(path, BACKGROUND_CONTEXT);\n return info.ok && info.value.kind === 'directory' ? path : undefined;\n}\n\nexport function createHeadlessRunnersCommand(\n dependencies: RunnerDependencies,\n { fs, onStart, sequences }: HeadlessRunnersCommandOptions = {},\n): PoHeadlessCommand {\n return {\n name: COMMAND_NAME,\n description: SERVER_COMMAND_DESCRIPTION,\n async execute(args: string, context: PoHeadlessExecutionContext) {\n const request = parseRunnersCommand(args);\n if (request.kind === 'space') {\n const active = await dependencies.runnerRegistry.listBySession(context.sessionId);\n await context.client.notify({\n body: `${active.length} active runner${active.length === 1 ? '' : 's'}.`,\n level: 'info',\n });\n return;\n }\n if (request.kind === 'start') {\n if (!request.command) throw new Error('Usage: /runners start [name=x] [cwd=y] [interactive=true] -- <command>');\n const cwd = await runnerFolder(context.cwd, request.cwd, fs);\n if (cwd === undefined) {\n const message = `That folder does not exist: ${request.cwd ?? ''}`;\n onStart?.(context.sessionId, message);\n await context.client.notify({ body: message, level: 'error' });\n return;\n }\n const result = await dependencies.bashRunService.run({\n command: request.command,\n background: true,\n sessionId: context.sessionId,\n cwd,\n ...(request.name ? { name: request.name } : {}),\n ...(request.interactive ? { interactive: true } : {}),\n ...(request.preferInteractive ? { preferInteractive: true } : {}),\n });\n onStart?.(context.sessionId, result.kind === 'failed' ? result.error : undefined);\n await context.client.notify({\n body:\n (await formatRunResult(result, {}, undefined, dependencies.settings, dependencies.environment)).content[0]\n ?.text ?? 'Runner started.',\n level: result.kind === 'failed' ? 'error' : 'info',\n });\n return;\n }\n if (!request.id) throw new Error('Usage: /runners stop <runner-id> [reason]');\n if (isSequenceId(request.id)) {\n if (!sequences) throw new Error('Sequence controls are unavailable.');\n await sequences.stop(request.id, request.reason);\n await context.client.notify({ body: `Stop requested for sequence ${request.id}.`, level: 'info' });\n return;\n }\n const record = await dependencies.runnerRegistry.get(request.id, context.sessionId);\n if (!record) {\n await context.client.notify({ body: `No active runner ${request.id} in this session.`, level: 'warning' });\n return;\n }\n const stopped = await stopRunnerProcess(record, dependencies.launcher, dependencies.rmuxBackend, {\n registry: dependencies.runnerRegistry,\n intent: {\n reason: 'stopped',\n terminationReason: 'user_stop',\n ...(request.reason ? { stopReason: request.reason } : {}),\n },\n });\n if (stopped) {\n await dependencies.runnerRegistry.complete(\n request.id,\n {\n reason: 'stopped',\n code: null,\n signal: null,\n ...(request.reason ? { stopReason: request.reason } : {}),\n },\n context.sessionId,\n );\n }\n await context.client.notify({\n body: stopped ? `Stopped runner ${request.id}.` : `Could not stop runner ${request.id}.`,\n level: stopped ? 'info' : 'error',\n });\n },\n };\n}\n"],"mappings":";;;;;;;;AAAA,MAAa,gBAAgB;;;ACA7B,MAAa,eAAe;AAG5B,MAAa,6BAA6B;;;;AEoC1C,SAAS,SAAS,MAAwB;CACxC,OAAO,KAAK,MAAM,mBAAmB,KAAK,CAAC;AAC7C;AAEA,SAAS,QAAQ,OAAuB;CACtC,OAAO,MAAM,UAAU,KAAK,MAAM,WAAW,IAAG,KAAK,MAAM,SAAS,IAAG,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI;AAClG;;;;;;;;;;AAWA,SAAS,WAAW,MAAmC;CACrD,MAAM,SAAS,SAAS,IAAI;CAC5B,MAAM,YAAY,OAAO,QAAA,IAAiC;CAC1D,IAAI,cAAc,IAAI,OAAO;EAAE,MAAM;EAAS,SAAS,KAAK,KAAK;EAAG,aAAa;CAAM;CAEvF,MAAM,UAA+B;EACnC,MAAM;EAGN,SAAS,KAAK,MAAM,KAAK,QAAA,IAAiC,IAAA,CAAoC,CAAC,CAAC,KAAK;EACrG,aAAa;CACf;CACA,KAAK,MAAM,SAAS,OAAO,MAAM,GAAG,SAAS,GAAG;EAC9C,MAAM,QAAQ,MAAM,QAAQ,GAAG;EAC/B,IAAI,SAAS,GAAG;EAChB,MAAM,QAAQ,QAAQ,MAAM,MAAM,QAAQ,CAAC,CAAC;EAC5C,MAAM,MAAM,MAAM,MAAM,GAAG,KAAK;EAChC,IAAI,QAAA,UAAoB,UAAU,IAAI,QAAQ,OAAO;EACrD,IAAI,QAAA,SAAmB,UAAU,IAAI,QAAQ,MAAM;EACnD,IAAI,QAAA,eAAyB;GAC3B,QAAQ,cAAc,UAAU;GAChC,IAAI,UAAA,UAA8B,QAAQ,oBAAoB;EAChE;CACF;CACA,OAAO;AACT;;;;;AAMA,SAAgB,oBAAoB,MAAqC;CACvE,MAAM,UAAU,KAAK,KAAK;CAC1B,MAAM,CAAC,OAAO,IAAI,KAAK,IAAI,GAAG,QAAQ,QAAQ,MAAM,MAAM,CAAC,CAAC,OAAO,OAAO;CAC1E,IAAI,SAAA,SAA6B,OAAO,WAAW,QAAQ,MAAM,KAAK,MAAM,CAAC,CAAC,KAAK,CAAC;CACpF,IAAI,SAAA,QAA4B,OAAO,EAAE,MAAM,QAAQ;CACvD,MAAM,SAAS,KAAK,KAAK,GAAG,CAAC,CAAC,KAAK;CACnC,OAAO;EAAE,MAAM;EAAQ;EAAI,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;CAAG;AAC3D;;;ACxEA,SAAgB,uBACd,aACA,aACyC;CACzC,OAAO;EACL,MAAM;EACN,OAAO;EACP,aACE;EACF,eAAe;EACf,kBAAkB;GAChB,0CAA0C,KAAK,MAAM,yBAAyB,WAAW,IAAI,aAAa,EAAE;GAC5G;GACA;GACA;GACA;EACF;EACA,YAAY;EACZ,eAAe;EACf,SAAS,OACP,aACA,QACA,QACA,UACA,YACG;GACH,IAAI,QAAQ,SAAS,MAAM,IAAI,MAAM,mBAAmB;GACxD,IAAI,CAAC,aAAa,MAAM,IAAI,MAAM,+DAA+D;GACjG,OAAO,YAAY,QAAQ,SAAS,aAAa,QAAQ,QAAQ;EACnE;CACF;AACF;;AAWA,eAAe,aACb,kBACA,QACA,IAC6B;CAC7B,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,MAAM,OAAO,QAAQ,kBAAkB,MAAM;CAC7C,IAAI,CAAC,IAAI,OAAO;CAChB,MAAM,OAAO,MAAM,GAAG,SAAS,MAAM,kBAAkB;CACvD,OAAO,KAAK,MAAM,KAAK,MAAM,SAAS,cAAc,OAAO,KAAA;AAC7D;AAEA,SAAgB,6BACd,cACA,EAAE,IAAI,SAAS,cAA6C,CAAC,GAC1C;CACnB,OAAO;EACL,MAAM;EACN,aAAa;EACb,MAAM,QAAQ,MAAc,SAAqC;GAC/D,MAAM,UAAU,oBAAoB,IAAI;GACxC,IAAI,QAAQ,SAAS,SAAS;IAC5B,MAAM,SAAS,MAAM,aAAa,eAAe,cAAc,QAAQ,SAAS;IAChF,MAAM,QAAQ,OAAO,OAAO;KAC1B,MAAM,GAAG,OAAO,OAAO,gBAAgB,OAAO,WAAW,IAAI,KAAK,IAAI;KACtE,OAAO;IACT,CAAC;IACD;GACF;GACA,IAAI,QAAQ,SAAS,SAAS;IAC5B,IAAI,CAAC,QAAQ,SAAS,MAAM,IAAI,MAAM,wEAAwE;IAC9G,MAAM,MAAM,MAAM,aAAa,QAAQ,KAAK,QAAQ,KAAK,EAAE;IAC3D,IAAI,QAAQ,KAAA,GAAW;KACrB,MAAM,UAAU,+BAA+B,QAAQ,OAAO;KAC9D,UAAU,QAAQ,WAAW,OAAO;KACpC,MAAM,QAAQ,OAAO,OAAO;MAAE,MAAM;MAAS,OAAO;KAAQ,CAAC;KAC7D;IACF;IACA,MAAM,SAAS,MAAM,aAAa,eAAe,IAAI;KACnD,SAAS,QAAQ;KACjB,YAAY;KACZ,WAAW,QAAQ;KACnB;KACA,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;KAC7C,GAAI,QAAQ,cAAc,EAAE,aAAa,KAAK,IAAI,CAAC;KACnD,GAAI,QAAQ,oBAAoB,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACjE,CAAC;IACD,UAAU,QAAQ,WAAW,OAAO,SAAS,WAAW,OAAO,QAAQ,KAAA,CAAS;IAChF,MAAM,QAAQ,OAAO,OAAO;KAC1B,OACG,MAAM,gBAAgB,QAAQ,CAAC,GAAG,KAAA,GAAW,aAAa,UAAU,aAAa,WAAW,EAAA,CAAG,QAAQ,EAAE,EACtG,QAAQ;KACd,OAAO,OAAO,SAAS,WAAW,UAAU;IAC9C,CAAC;IACD;GACF;GACA,IAAI,CAAC,QAAQ,IAAI,MAAM,IAAI,MAAM,2CAA2C;GAC5E,IAAI,aAAa,QAAQ,EAAE,GAAG;IAC5B,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,oCAAoC;IACpE,MAAM,UAAU,KAAK,QAAQ,IAAI,QAAQ,MAAM;IAC/C,MAAM,QAAQ,OAAO,OAAO;KAAE,MAAM,+BAA+B,QAAQ,GAAG;KAAI,OAAO;IAAO,CAAC;IACjG;GACF;GACA,MAAM,SAAS,MAAM,aAAa,eAAe,IAAI,QAAQ,IAAI,QAAQ,SAAS;GAClF,IAAI,CAAC,QAAQ;IACX,MAAM,QAAQ,OAAO,OAAO;KAAE,MAAM,oBAAoB,QAAQ,GAAG;KAAoB,OAAO;IAAU,CAAC;IACzG;GACF;GACA,MAAM,UAAU,MAAM,kBAAkB,QAAQ,aAAa,UAAU,aAAa,aAAa;IAC/F,UAAU,aAAa;IACvB,QAAQ;KACN,QAAQ;KACR,mBAAmB;KACnB,GAAI,QAAQ,SAAS,EAAE,YAAY,QAAQ,OAAO,IAAI,CAAC;IACzD;GACF,CAAC;GACD,IAAI,SACF,MAAM,aAAa,eAAe,SAChC,QAAQ,IACR;IACE,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,GAAI,QAAQ,SAAS,EAAE,YAAY,QAAQ,OAAO,IAAI,CAAC;GACzD,GACA,QAAQ,SACV;GAEF,MAAM,QAAQ,OAAO,OAAO;IAC1B,MAAM,UAAU,kBAAkB,QAAQ,GAAG,KAAK,yBAAyB,QAAQ,GAAG;IACtF,OAAO,UAAU,SAAS;GAC5B,CAAC;EACH;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"headless-CZGASZQW.mjs","names":[],"sources":["../src/constants/headless.ts","../src/constants/runners.ts","../src/constants/runnersCommand.ts","../src/services/runnersCommand/index.ts","../src/services/headless/index.ts"],"sourcesContent":["export const MS_PER_SECOND = 1000;\n","export const COMMAND_NAME = 'runners';\nexport const COMMAND_DESCRIPTION =\n 'Open Runner Space: background processes started by bash. `/runners stop <id> [reason]` stops one, `/runners start [name=x] [cwd=y] [interactive=true] -- <command>` starts one.';\nexport const SERVER_COMMAND_DESCRIPTION = 'Start and stop supervised background runners without a terminal UI.';\nexport const RUNNER_UNAVAILABLE_MESSAGE = 'Subprocess supervision is unavailable on this host.';\n","export const RUNNERS_STOP_VERB = 'stop';\nexport const RUNNERS_START_VERB = 'start';\nexport const RUNNERS_COMMAND_SEPARATOR = '--';\nexport const NAME_KEY = 'name';\nexport const CWD_KEY = 'cwd';\nexport const INTERACTIVE_KEY = 'interactive';\n/** `interactive=prefer`: a terminal when rmux or tmux is available, plain output otherwise. */\nexport const INTERACTIVE_PREFER = 'prefer';\n","import {\n CWD_KEY,\n INTERACTIVE_KEY,\n INTERACTIVE_PREFER,\n NAME_KEY,\n RUNNERS_COMMAND_SEPARATOR,\n RUNNERS_START_VERB,\n RUNNERS_STOP_VERB,\n} from '../../constants/runnersCommand';\n/** The `/runners` verb that stops one runner without opening Runner Space. */\n/** The `/runners` verb that starts one, for a client that cannot call the bash tool. */\n/**\n * Ends the options and begins the command.\n *\n * A command is arbitrary shell: it may hold `=`, and it may hold flags of its\n * own (`pnpm test -- --watch`). Only the first of these is the separator, so\n * everything the reader typed survives intact after it.\n */\n\nexport interface RunnersStartRequest {\n kind: 'start';\n /** The shell command to run; empty when the reader gave none. */\n command: string;\n cwd?: string;\n name?: string;\n interactive: boolean;\n /** `interactive=prefer`: a terminal when one is available, plain output otherwise. */\n preferInteractive?: boolean;\n}\n\nexport type RunnersCommandRequest =\n /** No arguments: open Runner Space. */\n | { kind: 'space' }\n /** `stop <id> [reason]`: stop one runner headlessly; id is empty when it was left out. */\n | { kind: 'stop'; id: string; reason?: string }\n /** `start [key=value ...] -- <command>`: start one headlessly. */\n | RunnersStartRequest;\n\n/** Splits on whitespace but keeps a double-quoted run together, so a path with spaces survives. */\nfunction tokenize(text: string): string[] {\n return text.match(/(?:\"[^\"]*\"|\\S)+/gu) ?? [];\n}\n\nfunction unquote(value: string): string {\n return value.length >= 2 && value.startsWith('\"') && value.endsWith('\"') ? value.slice(1, -1) : value;\n}\n\n/**\n * Parses `start`'s tail: `key=value` pairs up to the separator, then the\n * command verbatim.\n *\n * Without a separator the whole tail is the command, so typing\n * `/runners start pnpm build` does what it looks like. Options are read only\n * when a separator says where they end, which is what keeps a command such as\n * `FOO=bar pnpm test` from being mistaken for one.\n */\nfunction parseStart(tail: string): RunnersStartRequest {\n const tokens = tokenize(tail);\n const separator = tokens.indexOf(RUNNERS_COMMAND_SEPARATOR);\n if (separator === -1) return { kind: 'start', command: tail.trim(), interactive: false };\n\n const request: RunnersStartRequest = {\n kind: 'start',\n // Sliced from the raw text rather than rejoined from tokens: rejoining\n // would collapse the spacing the reader wrote.\n command: tail.slice(tail.indexOf(RUNNERS_COMMAND_SEPARATOR) + RUNNERS_COMMAND_SEPARATOR.length).trim(),\n interactive: false,\n };\n for (const token of tokens.slice(0, separator)) {\n const split = token.indexOf('=');\n if (split <= 0) continue;\n const value = unquote(token.slice(split + 1));\n const key = token.slice(0, split);\n if (key === NAME_KEY && value !== '') request.name = value;\n if (key === CWD_KEY && value !== '') request.cwd = value;\n if (key === INTERACTIVE_KEY) {\n request.interactive = value === 'true';\n if (value === INTERACTIVE_PREFER) request.preferInteractive = true;\n }\n }\n return request;\n}\n\n/**\n * Parses `/runners` arguments. Anything other than a known verb opens Runner\n * Space, which is what the bare command has always done.\n */\nexport function parseRunnersCommand(args: string): RunnersCommandRequest {\n const trimmed = args.trim();\n const [verb = '', id = '', ...rest] = trimmed.split(/\\s+/u).filter(Boolean);\n if (verb === RUNNERS_START_VERB) return parseStart(trimmed.slice(verb.length).trim());\n if (verb !== RUNNERS_STOP_VERB) return { kind: 'space' };\n const reason = rest.join(' ').trim();\n return { kind: 'stop', id, ...(reason ? { reason } : {}) };\n}\n","import { resolve } from 'node:path';\n\nimport type {\n PoHeadlessCommand,\n PoHeadlessExecutionContext,\n PoHeadlessTool,\n PoHeadlessToolResult,\n} from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, type FileSystem } from '@agimon-ai/po-core/platform';\n\nimport { BASH_TOOL_LABEL, BASH_TOOL_NAME } from '../../constants/bashTool';\nimport { MS_PER_SECOND } from '../../constants/headless';\nimport { COMMAND_NAME, SERVER_COMMAND_DESCRIPTION } from '../../constants/runners';\nimport { type BashParams, BashParamsSchema } from '../../schemas/bashTool';\nimport { formatRunResult } from '../../services/bashResult';\nimport { stopRunnerProcess } from '../../services/reconcile';\nimport { getBackgroundThresholdMs } from '../../services/runnerConfig';\nimport type { RunnerDependencies } from '../../services/runnerDependencies/type';\nimport { parseRunnersCommand } from '../../services/runnersCommand';\nimport type { RunnerSequenceService } from '../runnerSequence';\nimport { isSequenceId } from '../runnerSequenceStore';\n\nexport function createHeadlessBashTool(\n runSequence: RunnerSequenceService['run'] | undefined,\n environment: Readonly<Record<string, string | undefined>>,\n): PoHeadlessTool<typeof BashParamsSchema> {\n return {\n name: BASH_TOOL_NAME,\n label: BASH_TOOL_LABEL,\n description:\n 'Execute an array of Bash commands in order in the current working directory, stopping on first failure. Each command gets a complete saved log; return all log paths and only the last executed result. Do not pipe or redirect output to a file, head, or tail. Runner saves full logs automatically and bounds returned output.',\n promptSnippet: 'Execute shell commands with bounded foreground output and supervised background runners',\n promptGuidelines: [\n `A command sequence still running after ${Math.round(getBackgroundThresholdMs(environment) / MS_PER_SECOND)} seconds remains active as a background runner with an id and streaming log paths.`,\n 'Pass background: true for commands that should remain active, such as dev servers and watchers.',\n 'Always pass command as an array, including for one command. interactive: true applies only to the last command.',\n 'Do not pipe or redirect command output to a file, head, or tail. Full logs are saved automatically; returned output is already bounded.',\n 'Stop background runners when they are no longer needed.',\n ],\n parameters: BashParamsSchema,\n executionMode: 'serial',\n execute: async (\n _toolCallId: string,\n params: BashParams,\n signal: AbortSignal | undefined,\n onUpdate: ((result: PoHeadlessToolResult) => void) | undefined,\n context: PoHeadlessExecutionContext,\n ) => {\n if (signal?.aborted) throw new Error('Operation aborted');\n if (!runSequence) throw new Error('This host does not support supervised Bash command sequences.');\n return runSequence(params, context, _toolCallId, signal, onUpdate);\n },\n };\n}\n\nexport interface HeadlessRunnersCommandOptions {\n /** Checks a typed folder exists before a runner starts in it. */\n fs?: FileSystem | undefined;\n /** Hears every start: why it did not happen, or undefined when it did, so the runners panel can say. */\n onStart?: (sessionId: string, failure: string | undefined) => void;\n sequences?: Pick<RunnerSequenceService, 'get' | 'stop'>;\n}\n\n/** A folder as typed, relative to the session directory; undefined when it is not one. */\nasync function runnerFolder(\n sessionDirectory: string,\n folder: string | undefined,\n fs: FileSystem | undefined,\n): Promise<string | undefined> {\n if (folder === undefined) return sessionDirectory;\n const path = resolve(sessionDirectory, folder);\n if (!fs) return path;\n const info = await fs.fileInfo(path, BACKGROUND_CONTEXT);\n return info.ok && info.value.kind === 'directory' ? path : undefined;\n}\n\nexport function createHeadlessRunnersCommand(\n dependencies: RunnerDependencies,\n { fs, onStart, sequences }: HeadlessRunnersCommandOptions = {},\n): PoHeadlessCommand {\n return {\n name: COMMAND_NAME,\n description: SERVER_COMMAND_DESCRIPTION,\n async execute(args: string, context: PoHeadlessExecutionContext) {\n const request = parseRunnersCommand(args);\n if (request.kind === 'space') {\n const active = await dependencies.runnerRegistry.listBySession(context.sessionId);\n await context.client.notify({\n body: `${active.length} active runner${active.length === 1 ? '' : 's'}.`,\n level: 'info',\n });\n return;\n }\n if (request.kind === 'start') {\n if (!request.command) throw new Error('Usage: /runners start [name=x] [cwd=y] [interactive=true] -- <command>');\n const cwd = await runnerFolder(context.cwd, request.cwd, fs);\n if (cwd === undefined) {\n const message = `That folder does not exist: ${request.cwd ?? ''}`;\n onStart?.(context.sessionId, message);\n await context.client.notify({ body: message, level: 'error' });\n return;\n }\n const result = await dependencies.bashRunService.run({\n command: request.command,\n background: true,\n sessionId: context.sessionId,\n cwd,\n ...(request.name ? { name: request.name } : {}),\n ...(request.interactive ? { interactive: true } : {}),\n ...(request.preferInteractive ? { preferInteractive: true } : {}),\n });\n onStart?.(context.sessionId, result.kind === 'failed' ? result.error : undefined);\n await context.client.notify({\n body:\n (await formatRunResult(result, {}, undefined, dependencies.settings, dependencies.environment)).content[0]\n ?.text ?? 'Runner started.',\n level: result.kind === 'failed' ? 'error' : 'info',\n });\n return;\n }\n if (!request.id) throw new Error('Usage: /runners stop <runner-id> [reason]');\n if (isSequenceId(request.id)) {\n if (!sequences) throw new Error('Sequence controls are unavailable.');\n await sequences.stop(request.id, request.reason);\n await context.client.notify({ body: `Stop requested for sequence ${request.id}.`, level: 'info' });\n return;\n }\n const record = await dependencies.runnerRegistry.get(request.id, context.sessionId);\n if (!record) {\n await context.client.notify({ body: `No active runner ${request.id} in this session.`, level: 'warning' });\n return;\n }\n const stopped = await stopRunnerProcess(record, dependencies.launcher, dependencies.rmuxBackend, {\n registry: dependencies.runnerRegistry,\n intent: {\n reason: 'stopped',\n terminationReason: 'user_stop',\n ...(request.reason ? { stopReason: request.reason } : {}),\n },\n });\n if (stopped) {\n await dependencies.runnerRegistry.complete(\n request.id,\n {\n reason: 'stopped',\n code: null,\n signal: null,\n ...(request.reason ? { stopReason: request.reason } : {}),\n },\n context.sessionId,\n );\n }\n await context.client.notify({\n body: stopped ? `Stopped runner ${request.id}.` : `Could not stop runner ${request.id}.`,\n level: stopped ? 'info' : 'error',\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-rEHOO_5S.cjs","names":["BASH_TOOL_NAME","BASH_TOOL_LABEL","getBackgroundThresholdMs","BashParamsSchema","resolve","BACKGROUND_CONTEXT","formatRunResult","isSequenceId","stopRunnerProcess"],"sources":["../src/constants/headless.ts","../src/constants/runners.ts","../src/constants/runnersCommand.ts","../src/services/runnersCommand/index.ts","../src/services/headless/index.ts"],"sourcesContent":["export const MS_PER_SECOND = 1000;\n","export const COMMAND_NAME = 'runners';\nexport const COMMAND_DESCRIPTION =\n 'Open Runner Space: background processes started by bash. `/runners stop <id> [reason]` stops one, `/runners start [name=x] [cwd=y] [interactive=true] -- <command>` starts one.';\nexport const SERVER_COMMAND_DESCRIPTION = 'Start and stop supervised background runners without a terminal UI.';\n","export const RUNNERS_STOP_VERB = 'stop';\nexport const RUNNERS_START_VERB = 'start';\nexport const RUNNERS_COMMAND_SEPARATOR = '--';\nexport const NAME_KEY = 'name';\nexport const CWD_KEY = 'cwd';\nexport const INTERACTIVE_KEY = 'interactive';\n/** `interactive=prefer`: a terminal when rmux or tmux is available, plain output otherwise. */\nexport const INTERACTIVE_PREFER = 'prefer';\n","import {\n CWD_KEY,\n INTERACTIVE_KEY,\n INTERACTIVE_PREFER,\n NAME_KEY,\n RUNNERS_COMMAND_SEPARATOR,\n RUNNERS_START_VERB,\n RUNNERS_STOP_VERB,\n} from '../../constants/runnersCommand';\n/** The `/runners` verb that stops one runner without opening Runner Space. */\n/** The `/runners` verb that starts one, for a client that cannot call the bash tool. */\n/**\n * Ends the options and begins the command.\n *\n * A command is arbitrary shell: it may hold `=`, and it may hold flags of its\n * own (`pnpm test -- --watch`). Only the first of these is the separator, so\n * everything the reader typed survives intact after it.\n */\n\nexport interface RunnersStartRequest {\n kind: 'start';\n /** The shell command to run; empty when the reader gave none. */\n command: string;\n cwd?: string;\n name?: string;\n interactive: boolean;\n /** `interactive=prefer`: a terminal when one is available, plain output otherwise. */\n preferInteractive?: boolean;\n}\n\nexport type RunnersCommandRequest =\n /** No arguments: open Runner Space. */\n | { kind: 'space' }\n /** `stop <id> [reason]`: stop one runner headlessly; id is empty when it was left out. */\n | { kind: 'stop'; id: string; reason?: string }\n /** `start [key=value ...] -- <command>`: start one headlessly. */\n | RunnersStartRequest;\n\n/** Splits on whitespace but keeps a double-quoted run together, so a path with spaces survives. */\nfunction tokenize(text: string): string[] {\n return text.match(/(?:\"[^\"]*\"|\\S)+/gu) ?? [];\n}\n\nfunction unquote(value: string): string {\n return value.length >= 2 && value.startsWith('\"') && value.endsWith('\"') ? value.slice(1, -1) : value;\n}\n\n/**\n * Parses `start`'s tail: `key=value` pairs up to the separator, then the\n * command verbatim.\n *\n * Without a separator the whole tail is the command, so typing\n * `/runners start pnpm build` does what it looks like. Options are read only\n * when a separator says where they end, which is what keeps a command such as\n * `FOO=bar pnpm test` from being mistaken for one.\n */\nfunction parseStart(tail: string): RunnersStartRequest {\n const tokens = tokenize(tail);\n const separator = tokens.indexOf(RUNNERS_COMMAND_SEPARATOR);\n if (separator === -1) return { kind: 'start', command: tail.trim(), interactive: false };\n\n const request: RunnersStartRequest = {\n kind: 'start',\n // Sliced from the raw text rather than rejoined from tokens: rejoining\n // would collapse the spacing the reader wrote.\n command: tail.slice(tail.indexOf(RUNNERS_COMMAND_SEPARATOR) + RUNNERS_COMMAND_SEPARATOR.length).trim(),\n interactive: false,\n };\n for (const token of tokens.slice(0, separator)) {\n const split = token.indexOf('=');\n if (split <= 0) continue;\n const value = unquote(token.slice(split + 1));\n const key = token.slice(0, split);\n if (key === NAME_KEY && value !== '') request.name = value;\n if (key === CWD_KEY && value !== '') request.cwd = value;\n if (key === INTERACTIVE_KEY) {\n request.interactive = value === 'true';\n if (value === INTERACTIVE_PREFER) request.preferInteractive = true;\n }\n }\n return request;\n}\n\n/**\n * Parses `/runners` arguments. Anything other than a known verb opens Runner\n * Space, which is what the bare command has always done.\n */\nexport function parseRunnersCommand(args: string): RunnersCommandRequest {\n const trimmed = args.trim();\n const [verb = '', id = '', ...rest] = trimmed.split(/\\s+/u).filter(Boolean);\n if (verb === RUNNERS_START_VERB) return parseStart(trimmed.slice(verb.length).trim());\n if (verb !== RUNNERS_STOP_VERB) return { kind: 'space' };\n const reason = rest.join(' ').trim();\n return { kind: 'stop', id, ...(reason ? { reason } : {}) };\n}\n","import { resolve } from 'node:path';\n\nimport type {\n PoHeadlessCommand,\n PoHeadlessExecutionContext,\n PoHeadlessTool,\n PoHeadlessToolResult,\n} from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, type FileSystem } from '@agimon-ai/po-core/platform';\n\nimport { BASH_TOOL_LABEL, BASH_TOOL_NAME } from '../../constants/bashTool';\nimport { MS_PER_SECOND } from '../../constants/headless';\nimport { COMMAND_NAME, SERVER_COMMAND_DESCRIPTION } from '../../constants/runners';\nimport { type BashParams, BashParamsSchema } from '../../schemas/bashTool';\nimport { formatRunResult } from '../../services/bashResult';\nimport { stopRunnerProcess } from '../../services/reconcile';\nimport { getBackgroundThresholdMs } from '../../services/runnerConfig';\nimport type { RunnerDependencies } from '../../services/runnerDependencies/type';\nimport { parseRunnersCommand } from '../../services/runnersCommand';\nimport type { RunnerSequenceService } from '../runnerSequence';\nimport { isSequenceId } from '../runnerSequenceStore';\n\nexport function createHeadlessBashTool(\n runSequence: RunnerSequenceService['run'] | undefined,\n environment: Readonly<Record<string, string | undefined>>,\n): PoHeadlessTool<typeof BashParamsSchema> {\n return {\n name: BASH_TOOL_NAME,\n label: BASH_TOOL_LABEL,\n description:\n 'Execute an array of Bash commands in order in the current working directory, stopping on first failure. Each command gets a complete saved log; return all log paths and only the last executed result. Do not pipe or redirect output to a file, head, or tail. Runner saves full logs automatically and bounds returned output.',\n promptSnippet: 'Execute shell commands with bounded foreground output and supervised background runners',\n promptGuidelines: [\n `A command sequence still running after ${Math.round(getBackgroundThresholdMs(environment) / MS_PER_SECOND)} seconds remains active as a background runner with an id and streaming log paths.`,\n 'Pass background: true for commands that should remain active, such as dev servers and watchers.',\n 'Always pass command as an array, including for one command. interactive: true applies only to the last command.',\n 'Do not pipe or redirect command output to a file, head, or tail. Full logs are saved automatically; returned output is already bounded.',\n 'Stop background runners when they are no longer needed.',\n ],\n parameters: BashParamsSchema,\n executionMode: 'serial',\n execute: async (\n _toolCallId: string,\n params: BashParams,\n signal: AbortSignal | undefined,\n onUpdate: ((result: PoHeadlessToolResult) => void) | undefined,\n context: PoHeadlessExecutionContext,\n ) => {\n if (signal?.aborted) throw new Error('Operation aborted');\n if (!runSequence) throw new Error('This host does not support supervised Bash command sequences.');\n return runSequence(params, context, _toolCallId, signal, onUpdate);\n },\n };\n}\n\nexport interface HeadlessRunnersCommandOptions {\n /** Checks a typed folder exists before a runner starts in it. */\n fs?: FileSystem | undefined;\n /** Hears every start: why it did not happen, or undefined when it did, so the runners panel can say. */\n onStart?: (sessionId: string, failure: string | undefined) => void;\n sequences?: Pick<RunnerSequenceService, 'get' | 'stop'>;\n}\n\n/** A folder as typed, relative to the session directory; undefined when it is not one. */\nasync function runnerFolder(\n sessionDirectory: string,\n folder: string | undefined,\n fs: FileSystem | undefined,\n): Promise<string | undefined> {\n if (folder === undefined) return sessionDirectory;\n const path = resolve(sessionDirectory, folder);\n if (!fs) return path;\n const info = await fs.fileInfo(path, BACKGROUND_CONTEXT);\n return info.ok && info.value.kind === 'directory' ? path : undefined;\n}\n\nexport function createHeadlessRunnersCommand(\n dependencies: RunnerDependencies,\n { fs, onStart, sequences }: HeadlessRunnersCommandOptions = {},\n): PoHeadlessCommand {\n return {\n name: COMMAND_NAME,\n description: SERVER_COMMAND_DESCRIPTION,\n async execute(args: string, context: PoHeadlessExecutionContext) {\n const request = parseRunnersCommand(args);\n if (request.kind === 'space') {\n const active = await dependencies.runnerRegistry.listBySession(context.sessionId);\n await context.client.notify({\n body: `${active.length} active runner${active.length === 1 ? '' : 's'}.`,\n level: 'info',\n });\n return;\n }\n if (request.kind === 'start') {\n if (!request.command) throw new Error('Usage: /runners start [name=x] [cwd=y] [interactive=true] -- <command>');\n const cwd = await runnerFolder(context.cwd, request.cwd, fs);\n if (cwd === undefined) {\n const message = `That folder does not exist: ${request.cwd ?? ''}`;\n onStart?.(context.sessionId, message);\n await context.client.notify({ body: message, level: 'error' });\n return;\n }\n const result = await dependencies.bashRunService.run({\n command: request.command,\n background: true,\n sessionId: context.sessionId,\n cwd,\n ...(request.name ? { name: request.name } : {}),\n ...(request.interactive ? { interactive: true } : {}),\n ...(request.preferInteractive ? { preferInteractive: true } : {}),\n });\n onStart?.(context.sessionId, result.kind === 'failed' ? result.error : undefined);\n await context.client.notify({\n body:\n (await formatRunResult(result, {}, undefined, dependencies.settings, dependencies.environment)).content[0]\n ?.text ?? 'Runner started.',\n level: result.kind === 'failed' ? 'error' : 'info',\n });\n return;\n }\n if (!request.id) throw new Error('Usage: /runners stop <runner-id> [reason]');\n if (isSequenceId(request.id)) {\n if (!sequences) throw new Error('Sequence controls are unavailable.');\n await sequences.stop(request.id, request.reason);\n await context.client.notify({ body: `Stop requested for sequence ${request.id}.`, level: 'info' });\n return;\n }\n const record = await dependencies.runnerRegistry.get(request.id, context.sessionId);\n if (!record) {\n await context.client.notify({ body: `No active runner ${request.id} in this session.`, level: 'warning' });\n return;\n }\n const stopped = await stopRunnerProcess(record, dependencies.launcher, dependencies.rmuxBackend, {\n registry: dependencies.runnerRegistry,\n intent: {\n reason: 'stopped',\n terminationReason: 'user_stop',\n ...(request.reason ? { stopReason: request.reason } : {}),\n },\n });\n if (stopped) {\n await dependencies.runnerRegistry.complete(\n request.id,\n {\n reason: 'stopped',\n code: null,\n signal: null,\n ...(request.reason ? { stopReason: request.reason } : {}),\n },\n context.sessionId,\n );\n }\n await context.client.notify({\n body: stopped ? `Stopped runner ${request.id}.` : `Could not stop runner ${request.id}.`,\n level: stopped ? 'info' : 'error',\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"headless-rEHOO_5S.cjs","names":["BASH_TOOL_NAME","BASH_TOOL_LABEL","getBackgroundThresholdMs","BashParamsSchema","resolve","BACKGROUND_CONTEXT","formatRunResult","isSequenceId","stopRunnerProcess"],"sources":["../src/constants/headless.ts","../src/constants/runners.ts","../src/constants/runnersCommand.ts","../src/services/runnersCommand/index.ts","../src/services/headless/index.ts"],"sourcesContent":["export const MS_PER_SECOND = 1000;\n","export const COMMAND_NAME = 'runners';\nexport const COMMAND_DESCRIPTION =\n 'Open Runner Space: background processes started by bash. `/runners stop <id> [reason]` stops one, `/runners start [name=x] [cwd=y] [interactive=true] -- <command>` starts one.';\nexport const SERVER_COMMAND_DESCRIPTION = 'Start and stop supervised background runners without a terminal UI.';\nexport const RUNNER_UNAVAILABLE_MESSAGE = 'Subprocess supervision is unavailable on this host.';\n","export const RUNNERS_STOP_VERB = 'stop';\nexport const RUNNERS_START_VERB = 'start';\nexport const RUNNERS_COMMAND_SEPARATOR = '--';\nexport const NAME_KEY = 'name';\nexport const CWD_KEY = 'cwd';\nexport const INTERACTIVE_KEY = 'interactive';\n/** `interactive=prefer`: a terminal when rmux or tmux is available, plain output otherwise. */\nexport const INTERACTIVE_PREFER = 'prefer';\n","import {\n CWD_KEY,\n INTERACTIVE_KEY,\n INTERACTIVE_PREFER,\n NAME_KEY,\n RUNNERS_COMMAND_SEPARATOR,\n RUNNERS_START_VERB,\n RUNNERS_STOP_VERB,\n} from '../../constants/runnersCommand';\n/** The `/runners` verb that stops one runner without opening Runner Space. */\n/** The `/runners` verb that starts one, for a client that cannot call the bash tool. */\n/**\n * Ends the options and begins the command.\n *\n * A command is arbitrary shell: it may hold `=`, and it may hold flags of its\n * own (`pnpm test -- --watch`). Only the first of these is the separator, so\n * everything the reader typed survives intact after it.\n */\n\nexport interface RunnersStartRequest {\n kind: 'start';\n /** The shell command to run; empty when the reader gave none. */\n command: string;\n cwd?: string;\n name?: string;\n interactive: boolean;\n /** `interactive=prefer`: a terminal when one is available, plain output otherwise. */\n preferInteractive?: boolean;\n}\n\nexport type RunnersCommandRequest =\n /** No arguments: open Runner Space. */\n | { kind: 'space' }\n /** `stop <id> [reason]`: stop one runner headlessly; id is empty when it was left out. */\n | { kind: 'stop'; id: string; reason?: string }\n /** `start [key=value ...] -- <command>`: start one headlessly. */\n | RunnersStartRequest;\n\n/** Splits on whitespace but keeps a double-quoted run together, so a path with spaces survives. */\nfunction tokenize(text: string): string[] {\n return text.match(/(?:\"[^\"]*\"|\\S)+/gu) ?? [];\n}\n\nfunction unquote(value: string): string {\n return value.length >= 2 && value.startsWith('\"') && value.endsWith('\"') ? value.slice(1, -1) : value;\n}\n\n/**\n * Parses `start`'s tail: `key=value` pairs up to the separator, then the\n * command verbatim.\n *\n * Without a separator the whole tail is the command, so typing\n * `/runners start pnpm build` does what it looks like. Options are read only\n * when a separator says where they end, which is what keeps a command such as\n * `FOO=bar pnpm test` from being mistaken for one.\n */\nfunction parseStart(tail: string): RunnersStartRequest {\n const tokens = tokenize(tail);\n const separator = tokens.indexOf(RUNNERS_COMMAND_SEPARATOR);\n if (separator === -1) return { kind: 'start', command: tail.trim(), interactive: false };\n\n const request: RunnersStartRequest = {\n kind: 'start',\n // Sliced from the raw text rather than rejoined from tokens: rejoining\n // would collapse the spacing the reader wrote.\n command: tail.slice(tail.indexOf(RUNNERS_COMMAND_SEPARATOR) + RUNNERS_COMMAND_SEPARATOR.length).trim(),\n interactive: false,\n };\n for (const token of tokens.slice(0, separator)) {\n const split = token.indexOf('=');\n if (split <= 0) continue;\n const value = unquote(token.slice(split + 1));\n const key = token.slice(0, split);\n if (key === NAME_KEY && value !== '') request.name = value;\n if (key === CWD_KEY && value !== '') request.cwd = value;\n if (key === INTERACTIVE_KEY) {\n request.interactive = value === 'true';\n if (value === INTERACTIVE_PREFER) request.preferInteractive = true;\n }\n }\n return request;\n}\n\n/**\n * Parses `/runners` arguments. Anything other than a known verb opens Runner\n * Space, which is what the bare command has always done.\n */\nexport function parseRunnersCommand(args: string): RunnersCommandRequest {\n const trimmed = args.trim();\n const [verb = '', id = '', ...rest] = trimmed.split(/\\s+/u).filter(Boolean);\n if (verb === RUNNERS_START_VERB) return parseStart(trimmed.slice(verb.length).trim());\n if (verb !== RUNNERS_STOP_VERB) return { kind: 'space' };\n const reason = rest.join(' ').trim();\n return { kind: 'stop', id, ...(reason ? { reason } : {}) };\n}\n","import { resolve } from 'node:path';\n\nimport type {\n PoHeadlessCommand,\n PoHeadlessExecutionContext,\n PoHeadlessTool,\n PoHeadlessToolResult,\n} from '@agimon-ai/po-core/headless';\nimport { BACKGROUND_CONTEXT, type FileSystem } from '@agimon-ai/po-core/platform';\n\nimport { BASH_TOOL_LABEL, BASH_TOOL_NAME } from '../../constants/bashTool';\nimport { MS_PER_SECOND } from '../../constants/headless';\nimport { COMMAND_NAME, SERVER_COMMAND_DESCRIPTION } from '../../constants/runners';\nimport { type BashParams, BashParamsSchema } from '../../schemas/bashTool';\nimport { formatRunResult } from '../../services/bashResult';\nimport { stopRunnerProcess } from '../../services/reconcile';\nimport { getBackgroundThresholdMs } from '../../services/runnerConfig';\nimport type { RunnerDependencies } from '../../services/runnerDependencies/type';\nimport { parseRunnersCommand } from '../../services/runnersCommand';\nimport type { RunnerSequenceService } from '../runnerSequence';\nimport { isSequenceId } from '../runnerSequenceStore';\n\nexport function createHeadlessBashTool(\n runSequence: RunnerSequenceService['run'] | undefined,\n environment: Readonly<Record<string, string | undefined>>,\n): PoHeadlessTool<typeof BashParamsSchema> {\n return {\n name: BASH_TOOL_NAME,\n label: BASH_TOOL_LABEL,\n description:\n 'Execute an array of Bash commands in order in the current working directory, stopping on first failure. Each command gets a complete saved log; return all log paths and only the last executed result. Do not pipe or redirect output to a file, head, or tail. Runner saves full logs automatically and bounds returned output.',\n promptSnippet: 'Execute shell commands with bounded foreground output and supervised background runners',\n promptGuidelines: [\n `A command sequence still running after ${Math.round(getBackgroundThresholdMs(environment) / MS_PER_SECOND)} seconds remains active as a background runner with an id and streaming log paths.`,\n 'Pass background: true for commands that should remain active, such as dev servers and watchers.',\n 'Always pass command as an array, including for one command. interactive: true applies only to the last command.',\n 'Do not pipe or redirect command output to a file, head, or tail. Full logs are saved automatically; returned output is already bounded.',\n 'Stop background runners when they are no longer needed.',\n ],\n parameters: BashParamsSchema,\n executionMode: 'serial',\n execute: async (\n _toolCallId: string,\n params: BashParams,\n signal: AbortSignal | undefined,\n onUpdate: ((result: PoHeadlessToolResult) => void) | undefined,\n context: PoHeadlessExecutionContext,\n ) => {\n if (signal?.aborted) throw new Error('Operation aborted');\n if (!runSequence) throw new Error('This host does not support supervised Bash command sequences.');\n return runSequence(params, context, _toolCallId, signal, onUpdate);\n },\n };\n}\n\nexport interface HeadlessRunnersCommandOptions {\n /** Checks a typed folder exists before a runner starts in it. */\n fs?: FileSystem | undefined;\n /** Hears every start: why it did not happen, or undefined when it did, so the runners panel can say. */\n onStart?: (sessionId: string, failure: string | undefined) => void;\n sequences?: Pick<RunnerSequenceService, 'get' | 'stop'>;\n}\n\n/** A folder as typed, relative to the session directory; undefined when it is not one. */\nasync function runnerFolder(\n sessionDirectory: string,\n folder: string | undefined,\n fs: FileSystem | undefined,\n): Promise<string | undefined> {\n if (folder === undefined) return sessionDirectory;\n const path = resolve(sessionDirectory, folder);\n if (!fs) return path;\n const info = await fs.fileInfo(path, BACKGROUND_CONTEXT);\n return info.ok && info.value.kind === 'directory' ? path : undefined;\n}\n\nexport function createHeadlessRunnersCommand(\n dependencies: RunnerDependencies,\n { fs, onStart, sequences }: HeadlessRunnersCommandOptions = {},\n): PoHeadlessCommand {\n return {\n name: COMMAND_NAME,\n description: SERVER_COMMAND_DESCRIPTION,\n async execute(args: string, context: PoHeadlessExecutionContext) {\n const request = parseRunnersCommand(args);\n if (request.kind === 'space') {\n const active = await dependencies.runnerRegistry.listBySession(context.sessionId);\n await context.client.notify({\n body: `${active.length} active runner${active.length === 1 ? '' : 's'}.`,\n level: 'info',\n });\n return;\n }\n if (request.kind === 'start') {\n if (!request.command) throw new Error('Usage: /runners start [name=x] [cwd=y] [interactive=true] -- <command>');\n const cwd = await runnerFolder(context.cwd, request.cwd, fs);\n if (cwd === undefined) {\n const message = `That folder does not exist: ${request.cwd ?? ''}`;\n onStart?.(context.sessionId, message);\n await context.client.notify({ body: message, level: 'error' });\n return;\n }\n const result = await dependencies.bashRunService.run({\n command: request.command,\n background: true,\n sessionId: context.sessionId,\n cwd,\n ...(request.name ? { name: request.name } : {}),\n ...(request.interactive ? { interactive: true } : {}),\n ...(request.preferInteractive ? { preferInteractive: true } : {}),\n });\n onStart?.(context.sessionId, result.kind === 'failed' ? result.error : undefined);\n await context.client.notify({\n body:\n (await formatRunResult(result, {}, undefined, dependencies.settings, dependencies.environment)).content[0]\n ?.text ?? 'Runner started.',\n level: result.kind === 'failed' ? 'error' : 'info',\n });\n return;\n }\n if (!request.id) throw new Error('Usage: /runners stop <runner-id> [reason]');\n if (isSequenceId(request.id)) {\n if (!sequences) throw new Error('Sequence controls are unavailable.');\n await sequences.stop(request.id, request.reason);\n await context.client.notify({ body: `Stop requested for sequence ${request.id}.`, level: 'info' });\n return;\n }\n const record = await dependencies.runnerRegistry.get(request.id, context.sessionId);\n if (!record) {\n await context.client.notify({ body: `No active runner ${request.id} in this session.`, level: 'warning' });\n return;\n }\n const stopped = await stopRunnerProcess(record, dependencies.launcher, dependencies.rmuxBackend, {\n registry: dependencies.runnerRegistry,\n intent: {\n reason: 'stopped',\n terminationReason: 'user_stop',\n ...(request.reason ? { stopReason: request.reason } : {}),\n },\n });\n if (stopped) {\n await dependencies.runnerRegistry.complete(\n request.id,\n {\n reason: 'stopped',\n code: null,\n signal: null,\n ...(request.reason ? { stopReason: request.reason } : {}),\n },\n context.sessionId,\n );\n }\n await context.client.notify({\n body: stopped ? `Stopped runner ${request.id}.` : `Could not stop runner ${request.id}.`,\n level: stopped ? 'info' : 'error',\n });\n },\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/po-runner",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.113",
|
|
4
4
|
"description": "Supervised shell execution, background process control, and run logs for Pi coding agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -214,10 +214,10 @@
|
|
|
214
214
|
},
|
|
215
215
|
"dependencies": {
|
|
216
216
|
"@agimon-ai/foundation-process-registry": "0.30.14",
|
|
217
|
-
"@agimon-ai/po-core": "0.0.0-alpha.
|
|
218
|
-
"@agimon-ai/po-ui": "0.0.0-alpha.
|
|
219
|
-
"@agimon-ai/po-web-components": "0.0.0-alpha.
|
|
220
|
-
"@agimon-ai/po-web-security": "0.0.0-alpha.
|
|
217
|
+
"@agimon-ai/po-core": "0.0.0-alpha.113",
|
|
218
|
+
"@agimon-ai/po-ui": "0.0.0-alpha.113",
|
|
219
|
+
"@agimon-ai/po-web-components": "0.0.0-alpha.113",
|
|
220
|
+
"@agimon-ai/po-web-security": "0.0.0-alpha.113",
|
|
221
221
|
"@deepseek-ai/cordis": "4.0.5",
|
|
222
222
|
"@rmux/sdk": "0.6.5",
|
|
223
223
|
"@xterm/headless": "6.0.0",
|
|
@@ -225,8 +225,8 @@
|
|
|
225
225
|
"typebox": "1.4.2"
|
|
226
226
|
},
|
|
227
227
|
"devDependencies": {
|
|
228
|
-
"@agimon-ai/po-build": "0.0.0-alpha.
|
|
229
|
-
"@agimon-ai/po-native-components": "0.0.0-alpha.
|
|
228
|
+
"@agimon-ai/po-build": "0.0.0-alpha.113",
|
|
229
|
+
"@agimon-ai/po-native-components": "0.0.0-alpha.113",
|
|
230
230
|
"@earendil-works/pi-coding-agent": "1.5.0",
|
|
231
231
|
"@tanstack/react-store": "0.11.2",
|
|
232
232
|
"@tanstack/store": "0.11.2",
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
"vitest": "5.0.4"
|
|
243
243
|
},
|
|
244
244
|
"peerDependencies": {
|
|
245
|
-
"@agimon-ai/po-native-components": "0.0.0-alpha.
|
|
245
|
+
"@agimon-ai/po-native-components": "0.0.0-alpha.113",
|
|
246
246
|
"@earendil-works/pi-coding-agent": "1.5.0",
|
|
247
247
|
"react-native": "0.88.1",
|
|
248
248
|
"react-native-unistyles": "^3.5.3"
|
|
@@ -262,14 +262,14 @@
|
|
|
262
262
|
}
|
|
263
263
|
},
|
|
264
264
|
"optionalDependencies": {
|
|
265
|
-
"@agimon-ai/po-runner-rmux-darwin-arm64": "0.0.0-alpha.
|
|
266
|
-
"@agimon-ai/po-runner-rmux-darwin-x64": "0.0.0-alpha.
|
|
267
|
-
"@agimon-ai/po-runner-rmux-linux-arm64": "0.0.0-alpha.
|
|
268
|
-
"@agimon-ai/po-runner-rmux-linux-x64": "0.0.0-alpha.
|
|
269
|
-
"@agimon-ai/po-runner-rtk-darwin-arm64": "0.0.0-alpha.
|
|
270
|
-
"@agimon-ai/po-runner-rtk-darwin-x64": "0.0.0-alpha.
|
|
271
|
-
"@agimon-ai/po-runner-rtk-linux-arm64": "0.0.0-alpha.
|
|
272
|
-
"@agimon-ai/po-runner-rtk-linux-x64": "0.0.0-alpha.
|
|
265
|
+
"@agimon-ai/po-runner-rmux-darwin-arm64": "0.0.0-alpha.113",
|
|
266
|
+
"@agimon-ai/po-runner-rmux-darwin-x64": "0.0.0-alpha.113",
|
|
267
|
+
"@agimon-ai/po-runner-rmux-linux-arm64": "0.0.0-alpha.113",
|
|
268
|
+
"@agimon-ai/po-runner-rmux-linux-x64": "0.0.0-alpha.113",
|
|
269
|
+
"@agimon-ai/po-runner-rtk-darwin-arm64": "0.0.0-alpha.113",
|
|
270
|
+
"@agimon-ai/po-runner-rtk-darwin-x64": "0.0.0-alpha.113",
|
|
271
|
+
"@agimon-ai/po-runner-rtk-linux-arm64": "0.0.0-alpha.113",
|
|
272
|
+
"@agimon-ai/po-runner-rtk-linux-x64": "0.0.0-alpha.113"
|
|
273
273
|
},
|
|
274
274
|
"engines": {
|
|
275
275
|
"node": ">=24"
|