feat(tools): propagate timeout abort signals to tool execution
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { execFile } from 'child_process';
|
||||
import type { Tool, ToolResult } from '../types.js';
|
||||
import type { Tool, ToolResult, ToolExecutionContext } from '../types.js';
|
||||
|
||||
interface ShellExecArgs {
|
||||
command: string;
|
||||
@@ -19,16 +19,19 @@ export const shellExecTool: Tool = {
|
||||
},
|
||||
required: ['command'],
|
||||
},
|
||||
execute: async (rawArgs: unknown): Promise<ToolResult> => {
|
||||
execute: async (rawArgs: unknown, context?: ToolExecutionContext): Promise<ToolResult> => {
|
||||
const args = rawArgs as ShellExecArgs;
|
||||
const timeout = args.timeout ?? 30_000;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
execFile('bash', ['-c', args.command], {
|
||||
const child = execFile('bash', ['-c', args.command], {
|
||||
cwd: args.cwd,
|
||||
timeout,
|
||||
maxBuffer: 1024 * 1024,
|
||||
}, (error, stdout, stderr) => {
|
||||
if (context?.signal) {
|
||||
context.signal.removeEventListener('abort', onAbort);
|
||||
}
|
||||
if (error) {
|
||||
if (error.killed || error.signal === 'SIGTERM') {
|
||||
resolve({ success: false, output: stdout, error: `Command timed out after ${timeout}ms` });
|
||||
@@ -43,6 +46,17 @@ export const shellExecTool: Tool = {
|
||||
}
|
||||
resolve({ success: true, output: stdout + (stderr ? `\nstderr: ${stderr}` : '') });
|
||||
});
|
||||
|
||||
const onAbort = () => {
|
||||
child.kill('SIGTERM');
|
||||
};
|
||||
if (context?.signal) {
|
||||
if (context.signal.aborted) {
|
||||
onAbort();
|
||||
} else {
|
||||
context.signal.addEventListener('abort', onAbort, { once: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user