executeCommand function
Future<BashToolOutput>
executeCommand(
- BashToolInput input, {
- ShellExecOptions options = const ShellExecOptions(),
- SandboxConfig sandbox = const SandboxConfig(),
Execute a shell command with full BashTool semantics.
Implementation
Future<BashToolOutput> executeCommand(
BashToolInput input, {
ShellExecOptions options = const ShellExecOptions(),
SandboxConfig sandbox = const SandboxConfig(),
}) async {
// 1. Security validation
final securityResult = validateCommandSecurity(input.command);
if (!securityResult.passed) {
return BashToolOutput(
stdout: '',
stderr: 'Security violation: ${securityResult.violations.join('; ')}',
exitCode: -1,
);
}
// 2. Path safety check
final pathCheck = checkPathSafety(input.command);
if (pathCheck.isDangerous) {
return BashToolOutput(
stdout: '',
stderr: pathCheck.reason ?? 'Dangerous path operation',
exitCode: -1,
);
}
// 3. Determine timeout
final timeout = input.timeoutMs != null
? Duration(milliseconds: input.timeoutMs!)
: options.timeout;
// 4. Build environment
final env = <String, String>{
...?options.environment,
'NEOMAGECODE': '1', // Side-channel hint
'TERM': 'dumb', // Disable terminal features
};
// 5. Execute
final shell = Platform.isWindows
? 'cmd.exe'
: Platform.environment['SHELL'] ?? '/bin/sh';
final shellArgs = Platform.isWindows
? ['/c', input.command]
: ['-c', input.command];
try {
final process = await Process.start(
shell,
shellArgs,
workingDirectory: options.workingDirectory,
environment: env,
);
final stdoutBuf = StringBuffer();
final stderrBuf = StringBuffer();
var totalBytes = 0;
var interrupted = false;
// Capture output with size limiting
final stdoutSub = process.stdout.transform(utf8.decoder).listen((chunk) {
totalBytes += chunk.length;
if (totalBytes <= options.maxOutputBytes) {
stdoutBuf.write(chunk);
options.onProgress?.call(chunk);
}
});
final stderrSub = process.stderr.transform(utf8.decoder).listen((chunk) {
if (options.mergeStderr) {
totalBytes += chunk.length;
if (totalBytes <= options.maxOutputBytes) {
stdoutBuf.write(chunk);
}
} else {
stderrBuf.write(chunk);
}
});
// Wait with timeout
int exitCode;
try {
exitCode = await process.exitCode.timeout(timeout);
} on TimeoutException {
interrupted = true;
process.kill(ProcessSignal.sigterm);
await Future.delayed(const Duration(seconds: 2));
process.kill(ProcessSignal.sigkill);
exitCode = await process.exitCode.timeout(
const Duration(seconds: 3),
onTimeout: () => -1,
);
}
await stdoutSub.cancel();
await stderrSub.cancel();
var stdout = stdoutBuf.toString();
final stderr = stderrBuf.toString();
// Process output
stdout = stripEmptyLines(stdout);
if (totalBytes > options.maxOutputBytes) {
stdout = truncateOutput(stdout, maxLength: options.maxOutputBytes);
}
// Check for image output
final isImage = isImageOutput(stdout);
// Semantic exit code
final interpretation = interpretExitCode(input.command, exitCode);
// Silent command check
final noOutput = stdout.trim().isEmpty && isSilentCommand(input.command);
return BashToolOutput(
stdout: stdout,
stderr: stderr,
exitCode: exitCode,
interrupted: interrupted,
isImage: isImage,
returnCodeInterpretation: interpretation,
noOutputExpected: noOutput,
);
} catch (e) {
return BashToolOutput(stdout: '', stderr: e.toString(), exitCode: -1);
}
}