Skip to main content
The Sandbox SDK provides flexible command execution through the exec() method. You can run any shell command and get immediate results, or stream output in real-time for long-running operations.

Basic command execution

The simplest way to execute a command is with exec(), which waits for the command to complete and returns the full result:
const sandbox = getSandbox(env.Sandbox, 'my-sandbox');

const result = await sandbox.exec('echo "Hello, World!"');

console.log(result.stdout);  // "Hello, World!"
console.log(result.exitCode); // 0
console.log(result.success);  // true

Understanding the result

Every exec() call returns an ExecResult object with complete execution details:
interface ExecResult {
  success: boolean;      // true if exitCode === 0
  exitCode: number;      // Process exit code
  stdout: string;        // Standard output content
  stderr: string;        // Standard error content
  command: string;       // Command that was executed
  duration: number;      // Execution time in milliseconds
  timestamp: string;     // ISO timestamp when command started
  sessionId?: string;    // Session ID if provided
}
const result = await sandbox.exec('python3 -c "print(2 + 2)"');
// {
//   success: true,
//   exitCode: 0,
//   stdout: "4\n",
//   stderr: "",
//   duration: 45
// }

Streaming output

For long-running commands, stream output in real-time instead of waiting for completion:
const result = await sandbox.exec('npm install', {
  stream: true,
  onOutput: (stream, data) => {
    if (stream === 'stdout') {
      console.log('Output:', data);
    } else {
      console.error('Error:', data);
    }
  },
  onComplete: (result) => {
    console.log('Command finished with exit code:', result.exitCode);
  }
});
Streaming is perfect for commands that produce incremental output like package installations, builds, or log tailing.

Command options

Customize command execution with various options:

Timeout

Set a maximum execution time to prevent commands from running indefinitely:
try {
  await sandbox.exec('sleep 10', {
    timeout: 3000  // Timeout after 3 seconds
  });
} catch (error) {
  console.error('Command timed out');
}

Environment variables

Provide environment variables for a single command without affecting the session:
const result = await sandbox.exec('echo $API_KEY', {
  env: {
    API_KEY: 'secret-key',
    DEBUG: 'true'
  }
});
Environment variables passed to exec() only apply to that specific command. They don’t persist in the session. Use setEnvVars() for persistent environment variables.

Working directory

Change the working directory for a specific command:
const result = await sandbox.exec('ls -la', {
  cwd: '/workspace/project'
});

All options combined

const result = await sandbox.exec('python script.py', {
  timeout: 60000,  // 60 second timeout
  env: {
    PYTHONPATH: '/workspace/lib',
    DEBUG: '1'
  },
  cwd: '/workspace/scripts',
  stream: true,
  onOutput: (stream, data) => {
    console.log(`[${stream}]`, data);
  },
  onError: (error) => {
    console.error('Execution failed:', error.message);
  }
});

Chaining commands

Use shell operators to chain multiple commands:
// Run commands sequentially
await sandbox.exec('cd /workspace && npm install && npm run build');

// Run with conditional execution
await sandbox.exec('test -f config.json || cp config.example.json config.json');

// Pipe output between commands
const result = await sandbox.exec('cat data.txt | grep "error" | wc -l');

Running Python and Node.js

The sandbox comes with Python 3.11 and Node.js 20 pre-installed:
const result = await sandbox.exec(
  'python3 -c "import sys; print(sys.version)"'
);
console.log(result.stdout);
// "3.11.6 (main, ...)\n"

Error handling

Always check command results for errors:
const result = await sandbox.exec('make build');

if (!result.success) {
  console.error('Build failed!');
  console.error('Exit code:', result.exitCode);
  console.error('Error output:', result.stderr);
  throw new Error(`Build failed with exit code ${result.exitCode}`);
}

console.log('Build succeeded:', result.stdout);
The exec() method doesn’t throw errors for non-zero exit codes. Check result.success or result.exitCode to determine if the command succeeded.

Cancelling commands

Use AbortSignal to cancel long-running commands:
const controller = new AbortController();

// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);

try {
  await sandbox.exec('long-running-process', {
    signal: controller.signal
  });
} catch (error) {
  if (error.message.includes('aborted')) {
    console.log('Command was cancelled');
  }
}

Best practices

1
Use streaming for long operations
2
Stream output for commands that take more than a few seconds. This provides user feedback and helps debug issues.
3
Set appropriate timeouts
4
Always set timeouts for operations that might hang. This prevents resource exhaustion and provides better error messages.
5
Handle errors explicitly
6
Check result.success before using command output. Non-zero exit codes indicate failures that need handling.
7
Escape special characters
8
When constructing commands with user input, properly escape special characters or use command arrays:
9
// Bad - vulnerable to injection
await sandbox.exec(`cat ${userFilename}`);

// Good - escape shell special characters
import { shellEscape } from '@repo/shared';
await sandbox.exec(`cat ${shellEscape(userFilename)}`);

Next steps

Managing files

Read, write, and organize files in your sandbox

Running processes

Start long-running background processes

Code interpreter

Execute Python and JavaScript with rich outputs

Streaming output

Advanced streaming patterns and SSE events

Build docs developers (and LLMs) love