diff options
| author | Adam Malczewski <[email protected]> | 2026-06-27 01:09:39 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-27 01:09:39 +0900 |
| commit | 61e45e60d699ed1ca46f94a8f181c92a940317c6 (patch) | |
| tree | 2892d9773c5a8e367e1e58cdb1e88d9c6ad3fe6d /packages/tool-shell/src/shell.ts | |
| parent | 63c7e64532e85e0bbdd6d9ac6825d8f86be98e7a (diff) | |
| parent | 727c98c9dae516a2070eb950410314380a20c974 (diff) | |
| download | dispatch-61e45e60d699ed1ca46f94a8f181c92a940317c6.tar.gz dispatch-61e45e60d699ed1ca46f94a8f181c92a940317c6.zip | |
Merge branch 'feature/indent-change' into dev
Diffstat (limited to 'packages/tool-shell/src/shell.ts')
| -rw-r--r-- | packages/tool-shell/src/shell.ts | 298 |
1 files changed, 149 insertions, 149 deletions
diff --git a/packages/tool-shell/src/shell.ts b/packages/tool-shell/src/shell.ts index dac7fab..e9b676a 100644 --- a/packages/tool-shell/src/shell.ts +++ b/packages/tool-shell/src/shell.ts @@ -6,170 +6,170 @@ const DEFAULT_TIMEOUT = 120_000; const OUTPUT_CAP = 50_000; export interface ValidatedArgs { - readonly command: string; - readonly timeout: number; + readonly command: string; + readonly timeout: number; } export interface SpawnResult { - readonly exitCode: number | null; - readonly timedOut: boolean; - readonly aborted: boolean; + readonly exitCode: number | null; + readonly timedOut: boolean; + readonly aborted: boolean; } export function validateArgs(args: unknown): ValidatedArgs | { readonly error: string } { - if (args === null || args === undefined || typeof args !== "object") { - return { error: "Error: Arguments must be an object." }; - } - const obj = args as Record<string, unknown>; - - const rawCommand = obj.command; - if (typeof rawCommand !== "string" || rawCommand.trim().length === 0) { - return { error: 'Error: Missing or empty "command" parameter (must be a non-empty string).' }; - } - - let timeout = DEFAULT_TIMEOUT; - if (obj.timeout !== undefined) { - const n = Number(obj.timeout); - if (!Number.isFinite(n) || n < 1) { - return { error: 'Error: Invalid "timeout" parameter (must be a positive number).' }; - } - timeout = Math.floor(n); - } - - return { command: rawCommand, timeout }; + if (args === null || args === undefined || typeof args !== "object") { + return { error: "Error: Arguments must be an object." }; + } + const obj = args as Record<string, unknown>; + + const rawCommand = obj.command; + if (typeof rawCommand !== "string" || rawCommand.trim().length === 0) { + return { error: 'Error: Missing or empty "command" parameter (must be a non-empty string).' }; + } + + let timeout = DEFAULT_TIMEOUT; + if (obj.timeout !== undefined) { + const n = Number(obj.timeout); + if (!Number.isFinite(n) || n < 1) { + return { error: 'Error: Invalid "timeout" parameter (must be a positive number).' }; + } + timeout = Math.floor(n); + } + + return { command: rawCommand, timeout }; } export function truncateOutput(output: string, cap: number): string { - if (output.length <= cap) { - return output; - } - const truncated = output.slice(0, cap); - return `${truncated}\n\n[Output truncated: exceeded ${cap} characters]`; + if (output.length <= cap) { + return output; + } + const truncated = output.slice(0, cap); + return `${truncated}\n\n[Output truncated: exceeded ${cap} characters]`; } export function buildResult(params: { - readonly exitCode: number | null; - readonly timedOut: boolean; - readonly aborted: boolean; - readonly output: string; - readonly cap: number; + readonly exitCode: number | null; + readonly timedOut: boolean; + readonly aborted: boolean; + readonly output: string; + readonly cap: number; }): ToolResult { - if (params.aborted) { - return { - content: truncateOutput(params.output, params.cap), - isError: true, - }; - } - if (params.timedOut) { - const content = truncateOutput(params.output, params.cap); - return { - content: `${content}\n\n[Command timed out]`, - isError: true, - }; - } - const exitCode = params.exitCode; - if (exitCode !== null && exitCode !== 0) { - return { - content: truncateOutput(params.output, params.cap), - isError: true, - }; - } - return { - content: truncateOutput(params.output, params.cap), - }; + if (params.aborted) { + return { + content: truncateOutput(params.output, params.cap), + isError: true, + }; + } + if (params.timedOut) { + const content = truncateOutput(params.output, params.cap); + return { + content: `${content}\n\n[Command timed out]`, + isError: true, + }; + } + const exitCode = params.exitCode; + if (exitCode !== null && exitCode !== 0) { + return { + content: truncateOutput(params.output, params.cap), + isError: true, + }; + } + return { + content: truncateOutput(params.output, params.cap), + }; } export function createRunShellTool(deps: { - readonly workdir: string; - readonly resolveBackend: ExecBackendResolver; - readonly outputCap?: number; + readonly workdir: string; + readonly resolveBackend: ExecBackendResolver; + readonly outputCap?: number; }): ToolContract { - const workdir = resolve(deps.workdir); - const cap = deps.outputCap ?? OUTPUT_CAP; - - return { - name: "run_shell", - description: - "Execute a shell command and return its output. " + - "Use for running CLI tools, scripts, or system commands.", - parameters: { - type: "object", - properties: { - command: { - type: "string", - description: "The shell command to execute.", - }, - timeout: { - type: "number", - description: `Timeout in milliseconds (default: ${DEFAULT_TIMEOUT}).`, - default: DEFAULT_TIMEOUT, - }, - }, - required: ["command"], - }, - concurrencySafe: false, - async execute(args: unknown, ctx: ToolExecuteContext): Promise<ToolResult> { - const validated = validateArgs(args); - if ("error" in validated) { - return { content: validated.error, isError: true }; - } - - const { command, timeout } = validated; - const effectiveCwd = ctx.cwd ? resolve(ctx.cwd) : workdir; - - if (ctx.signal.aborted) { - return buildResult({ - exitCode: null, - timedOut: false, - aborted: true, - output: "", - cap, - }); - } - - let output = ""; - const appendOutput = (data: string, _stream: "stdout" | "stderr") => { - output += data; - }; - - const backend = deps.resolveBackend(ctx.computerId); - - let spawnResult: SpawnResult; - - try { - spawnResult = await backend.spawn({ - command, - cwd: effectiveCwd, - signal: ctx.signal, - timeout, - onOutput: (data, stream) => { - ctx.onOutput(data, stream); - appendOutput(data, stream); - }, - }); - } catch (err: unknown) { - if (ctx.signal.aborted) { - return buildResult({ - exitCode: null, - timedOut: false, - aborted: true, - output, - cap, - }); - } - return { - content: `Error spawning command: ${err instanceof Error ? err.message : String(err)}`, - isError: true, - }; - } - - return buildResult({ - exitCode: spawnResult.exitCode, - timedOut: spawnResult.timedOut, - aborted: spawnResult.aborted, - output, - cap, - }); - }, - }; + const workdir = resolve(deps.workdir); + const cap = deps.outputCap ?? OUTPUT_CAP; + + return { + name: "run_shell", + description: + "Execute a shell command and return its output. " + + "Use for running CLI tools, scripts, or system commands.", + parameters: { + type: "object", + properties: { + command: { + type: "string", + description: "The shell command to execute.", + }, + timeout: { + type: "number", + description: `Timeout in milliseconds (default: ${DEFAULT_TIMEOUT}).`, + default: DEFAULT_TIMEOUT, + }, + }, + required: ["command"], + }, + concurrencySafe: false, + async execute(args: unknown, ctx: ToolExecuteContext): Promise<ToolResult> { + const validated = validateArgs(args); + if ("error" in validated) { + return { content: validated.error, isError: true }; + } + + const { command, timeout } = validated; + const effectiveCwd = ctx.cwd ? resolve(ctx.cwd) : workdir; + + if (ctx.signal.aborted) { + return buildResult({ + exitCode: null, + timedOut: false, + aborted: true, + output: "", + cap, + }); + } + + let output = ""; + const appendOutput = (data: string, _stream: "stdout" | "stderr") => { + output += data; + }; + + const backend = deps.resolveBackend(ctx.computerId); + + let spawnResult: SpawnResult; + + try { + spawnResult = await backend.spawn({ + command, + cwd: effectiveCwd, + signal: ctx.signal, + timeout, + onOutput: (data, stream) => { + ctx.onOutput(data, stream); + appendOutput(data, stream); + }, + }); + } catch (err: unknown) { + if (ctx.signal.aborted) { + return buildResult({ + exitCode: null, + timedOut: false, + aborted: true, + output, + cap, + }); + } + return { + content: `Error spawning command: ${err instanceof Error ? err.message : String(err)}`, + isError: true, + }; + } + + return buildResult({ + exitCode: spawnResult.exitCode, + timedOut: spawnResult.timedOut, + aborted: spawnResult.aborted, + output, + cap, + }); + }, + }; } |
