What Is the `run_terminal_command` Tool in Freebuff? Core Responsibilities and Implementation
The run_terminal_command tool is Freebuff's core terminal execution engine that runs arbitrary shell commands in a safe, bounded, and abort-aware manner, with built-in process group management, output limits, and cross-platform compatibility.
The run_terminal_command tool (exported as runTerminalCommand in the SDK) serves as the foundational abstraction layer that enables Freebuff agents to execute shell commands safely. Located in sdk/src/tools/run-terminal-command.ts, this tool handles everything from process spawning to resource cleanup, making it the cornerstone of all terminal-based functionality in the Freebuff ecosystem.
Core Responsibilities of run_terminal_command
The tool manages nine critical responsibilities that ensure reliable command execution across POSIX and Windows environments.
Process Spawning with Detached Process Groups
run_terminal_command launches commands using child_process.spawn with detached: true, creating independent process groups. This design prevents child processes from inheriting the parent's signal handlers and enables clean whole-tree termination.
In sdk/src/tools/run-terminal-command.ts, the spawnDirectTerminalCommand function (lines 45-55) implements this spawning logic. On POSIX systems, this creates a new process group; on Windows, it leverages Git Bash with specific environment tweaks.
Process Group Ownership and Termination
The tool guarantees that all descendant processes belong to a single owner, enabling reliable termination of entire process trees. The killProcessGroup function sends signals to -pid (the whole group) on POSIX, while falling back to taskkill.exe on Windows.
This implementation spans lines 29-73 in the source file and includes isProcessGroupAlive checks to verify termination success.
Bounded Output Buffering
To prevent unbounded memory growth from verbose commands, run_terminal_command uses a BoundedOutputBuffer that maintains head and tail portions of output up to COMMAND_OUTPUT_LIMIT (50 KB). When this limit is exceeded, the buffer inserts a truncation marker rather than consuming unlimited memory.
The buffer implementation occupies lines 63-77 and ensures agents receive useful output without resource exhaustion.
Configurable Timeout Handling
Commands automatically abort after a configurable timeout. The timeout logic (lines 61-73) implements a two-stage escalation:
- SIGTERM – graceful termination request
- SIGKILL after
KILL_ESCALATION_MS(1.5 s) – forceful termination
This approach balances cleanup opportunities with guaranteed termination.
AbortSignal Integration
run_terminal_command responds to standard AbortSignal instances for cooperative cancellation. When a signal aborts, the tool:
- Resolves with a "Command cancelled" message
- Kills the entire process tree
- Returns whatever output was captured before cancellation
The abort listener setup appears in lines 29-38, enabling integration with user-initiated cancellations in CLI applications.
Windows-Specific Compatibility Fixes
Cross-platform execution requires special handling for Windows. The tool applies two key fixes:
rewriteWindowsNulRedirects– Rewrites> nulredirections to/dev/nullequivalentsMSYS=disable_pcon– Forces non-ConPTY Bash environment to avoid pseudo-terminal issues
These adjustments (lines 38-44) ensure consistent behavior between Windows and Unix-like systems.
Pluggable Broker Architecture
For hosts requiring custom execution environments, run_terminal_command accepts an optional TerminalCommandBroker. When provided, the tool delegates to broker.start(request) instead of direct spawning.
This pattern, shown in lines 77-80, allows the CLI UI to run commands in pseudo-terminals or sandboxed containers while maintaining the same interface.
Process Exit Sweeping
A global liveChildren set tracks all active processes. On process.exit, each child receives SIGKILL to prevent zombie processes. This cleanup logic (lines 90-99) ensures resource hygiene even during unclean shutdowns.
Active Process Diagnostics
The getActiveTerminalCommandProcesses function (lines 101-118) returns process metadata (pid, processGroupId) for currently running commands. This enables diagnostics and monitoring without exposing sensitive command text.
Using run_terminal_command in Practice
Basic SDK Usage
Import and invoke the tool directly for synchronous command execution:
import { runTerminalCommand } from '@codebuff/sdk'
const result = await runTerminalCommand({
command: 'git status',
process_type: 'SYNC',
cwd: '/path/to/repo',
timeout_seconds: 30,
})
// Returns: [{ type: 'json', value: { command, stdout, exitCode, ... } }]
Cancellation with AbortController
Integrate with user-driven cancellation using standard web APIs:
const controller = new AbortController()
setTimeout(() => controller.abort(), 5000) // 5-second timeout
const output = await runTerminalCommand({
command: 'sleep 60',
process_type: 'SYNC',
cwd: '.',
timeout_seconds: 120,
signal: controller.signal,
})
// Output contains "Command cancelled" message and captured stdout
Custom Broker Implementation
Supply a custom broker for sandboxed or PTY-based execution:
import { runTerminalCommand, type TerminalCommandBroker } from '@codebuff/sdk'
const myBroker: TerminalCommandBroker = {
start(request) {
// Custom sandbox or PTY logic here
return spawnDirectTerminalCommand(request) // Fallback available
},
}
await runTerminalCommand({
command: 'npm test',
process_type: 'SYNC',
cwd: '/my/project',
timeout_seconds: 60,
terminalCommandBroker: myBroker,
})
Integration Points in the Freebuff Codebase
| File | Role |
|---|---|
sdk/src/tools/run-terminal-command.ts |
Full tool implementation with all safety mechanisms |
sdk/src/run.ts |
SDK entry point invoking runTerminalCommand for agent run operations |
sdk/src/tools/index.ts |
Public SDK export surface |
cli/src/utils/__tests__/terminal-command-broker.test.ts |
Test suite validating broker and abort behavior |
cli/src/smoke/terminal-command-broker.ts |
Real-world smoke tests for CLI integration |
According to the Freebuff source code, higher-level SDK functions and CLI commands delegate all terminal execution to this single tool, ensuring consistent behavior across the platform.
Summary
run_terminal_commandis Freebuff's unified shell execution abstraction, implemented insdk/src/tools/run-terminal-command.ts- Safety mechanisms include process group ownership, bounded 50 KB output buffers, and two-stage timeout escalation (SIGTERM → SIGKILL)
- Cross-platform support handles POSIX and Windows differences through environment tweaks and
taskkill.exefallback - Cancellation works via standard
AbortSignalor built-in timeouts, with guaranteed process tree cleanup - Extensibility through
TerminalCommandBrokerenables custom execution environments without changing the interface - Resource hygiene via global exit sweeping prevents zombie processes during unexpected shutdowns
Frequently Asked Questions
What happens when a command exceeds the output limit?
The BoundedOutputBuffer captures the beginning and end of output up to 50 KB, inserting a truncation marker in the middle. This preserves diagnostically useful content (startup messages and final results) while preventing memory exhaustion from verbose commands.
How does run_terminal_command handle Windows compatibility?
On Windows, the tool forces Git Bash with MSYS=disable_pcon to disable problematic ConPTY behavior, rewrites > nul redirects to Unix-style equivalents, and uses taskkill.exe for process group termination instead of POSIX signals.
Can I run commands without the built-in timeout?
Yes—set timeout_seconds to a sufficiently high value or omit it entirely. However, the tool always respects an externally provided AbortSignal, so callers maintain full cancellation control regardless of timeout configuration.
What is the difference between runTerminalCommand and spawnDirectTerminalCommand?
runTerminalCommand is the public SDK function with full safety wrapping (brokers, buffering, timeouts). spawnDirectTerminalCommand is the lower-level internal function that performs the actual child_process.spawn call, used either directly or as a fallback within custom brokers.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →