arcadiasuite/server/autonomous/tools/command/RunCommandTool.ts

77 lines
2.1 KiB
TypeScript

/**
* Arcadia Suite - Run Command Tool
*/
import { BaseTool, type ToolParameter, type ToolResult } from "../BaseTool";
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
const ALLOWED_COMMANDS = [
'npm run',
'npx',
'tsc',
'node',
'git status',
'git diff',
'git log',
'git add',
'git commit',
'ls',
'cat',
'head',
'tail',
'grep',
'find',
'wc',
];
const BLOCKED_COMMANDS = ['rm', 'rmdir', 'mv', 'cp', 'chmod', 'chown', 'sudo', 'curl', 'wget', 'ssh'];
export class RunCommandTool extends BaseTool {
name = "run_command";
description = "Executa um comando shell seguro no projeto";
category = "Command";
parameters: ToolParameter[] = [
{ name: "command", type: "string", description: "Comando a executar", required: true },
{ name: "timeout", type: "number", description: "Timeout em ms (máx 60000)", required: false, default: 30000 },
];
async execute(params: Record<string, any>): Promise<ToolResult> {
const command = params.command as string;
const timeout = Math.min(params.timeout as number || 30000, 60000);
const isAllowed = ALLOWED_COMMANDS.some(allowed => command.startsWith(allowed));
if (!isAllowed) {
return this.formatError(`Comando não permitido. Comandos permitidos: ${ALLOWED_COMMANDS.join(", ")}`);
}
const isBlocked = BLOCKED_COMMANDS.some(blocked => command.includes(blocked));
if (isBlocked) {
return this.formatError("Comando bloqueado por segurança");
}
try {
const { stdout, stderr } = await execAsync(command, {
cwd: process.cwd(),
timeout,
maxBuffer: 1024 * 1024,
});
const output = stdout + (stderr ? `\nSTDERR: ${stderr}` : "");
return this.formatSuccess(`Comando executado: ${command}`, {
command,
output: output.slice(0, 10000),
truncated: output.length > 10000,
});
} catch (error: any) {
if (error.killed) {
return this.formatError(`Comando excedeu o timeout de ${timeout}ms`);
}
return this.formatError(`Erro: ${error.message}\n${error.stderr || ""}`);
}
}
}