Files
smartagent/ts_tools/tool.shell.ts
T

97 lines
3.1 KiB
TypeScript

import * as plugins from './plugins.js';
import {
createLocalToolExecutionContext,
formatShellResult,
type IToolExecutionContext,
} from './tool.context.js';
export interface IShellToolOptions {
/** Allowed commands whitelist. If empty, all commands are allowed. */
allowedCommands?: string[];
/** Working directory for shell execution */
cwd?: string;
/** Execution context. Defaults to a local Node.js context. */
context?: IToolExecutionContext;
/** Maximum output lines before truncating. */
maxLines?: number;
/** Maximum output bytes before truncating. */
maxBytes?: number;
}
export interface ICreateShellToolsOptions {
/** Allowed commands whitelist. If empty, all commands are allowed. */
allowedCommands?: string[];
/** Maximum output lines before truncating. */
maxLines?: number;
/** Maximum output bytes before truncating. */
maxBytes?: number;
}
export function createShellTools(context: IToolExecutionContext, options: ICreateShellToolsOptions = {}): plugins.ToolSet {
return {
run_command: plugins.tool({
description:
'Execute a shell command in the active workspace. Provide the full command string. stdout and stderr are returned.',
inputSchema: plugins.z.object({
command: plugins.z.string().describe('The shell command to execute'),
cwd: plugins.z
.string()
.optional()
.describe('Working directory for the command'),
timeout: plugins.z
.number()
.optional()
.describe('Timeout in milliseconds'),
timeoutMs: plugins.z
.number()
.optional()
.describe('Timeout in milliseconds'),
}),
execute: async ({
command,
cwd,
timeout,
timeoutMs,
}: {
command: string;
cwd?: string;
timeout?: number;
timeoutMs?: number;
}) => {
if (!context.shell) {
throw new Error('Shell tool is not available in this execution context.');
}
if (options.allowedCommands?.length) {
const baseCommand = command.split(/\s+/)[0];
if (!options.allowedCommands.includes(baseCommand)) {
return `Command "${baseCommand}" is not in the allowed commands list: ${options.allowedCommands.join(', ')}`;
}
}
await context.requestPermission?.({
type: 'shell',
title: 'Run shell command',
metadata: { command, cwd: cwd ?? context.cwd },
});
const execResult = await context.shell.run(command, {
cwd: cwd ?? context.cwd,
timeoutMs: timeoutMs ?? timeout,
abortSignal: context.abortSignal,
});
return plugins.truncateOutput(formatShellResult(execResult), {
maxLines: options.maxLines,
maxBytes: options.maxBytes,
}).content;
},
}),
};
}
export function shellTool(options?: IShellToolOptions): plugins.ToolSet {
const context = options?.context ?? createLocalToolExecutionContext({ cwd: options?.cwd });
return createShellTools(context, options);
}