Capsa is experimental software. APIs may change without notice.
Skip to content

Running Commands

The agent provides structured command execution inside sandboxes. Unlike console-based approaches, you get proper exit codes, separated stdout/stderr, and no terminal parsing.

Basic Execution

Connect to the agent and run commands:

rust,no_run
let vm = capsa::sandbox()
    .build()
    .await?;

let agent = vm.agent().await?;

let result = agent.exec("echo").arg("hello").run().await?;
println!("stdout: {}", result.stdout);
println!("stderr: {}", result.stderr);
println!("exit_code: {}", result.exit_code);

Direct vs Shell Execution

Commands are executed directly without a shell. For shell features (pipes, redirects, variable expansion), use /bin/sh -c:

rust,no_run
// Direct execution (safer, faster)
agent.exec("ls").args(["-la", "/mnt"]).run().await?;

// Shell execution (when you need shell features)
agent.exec("/bin/sh").args(["-c", "ls *.txt | grep foo"]).run().await?;

Environment Variables

Pass environment variables with .env():

rust,no_run
let result = agent.exec("/bin/sh")
    .args(["-c", "echo $API_KEY"])
    .env("API_KEY", "secret123")
    .env("DEBUG", "1")
    .run()
    .await?;
assert_eq!(result.stdout.trim(), "secret123");

Exit Codes

Check the exit code to determine success:

rust,no_run
let result = agent.exec("test").args(["-f", "/etc/passwd"]).run().await?;

if result.exit_code == 0 {
    println!("File exists");
} else {
    println!("File not found");
}

Error Handling

Commands that fail to execute return an error. Commands that execute but return non-zero are not errors:

rust,no_run
// This succeeds (command runs, even though it exits with error)
let result = agent.exec("false").run().await?;
assert_eq!(result.exit_code, 1);

// This fails (command doesn't exist)
let result = agent.exec("/nonexistent/binary").run().await;
assert!(result.is_err());

Capturing Output

Stdout and stderr are captured separately:

rust,no_run
let result = agent.exec("/bin/sh")
    .args(["-c", "echo stdout_data && echo stderr_data >&2"])
    .run()
    .await?;

println!("stdout: {}", result.stdout); // "stdout_data\n"
println!("stderr: {}", result.stderr); // "stderr_data\n"

Long-Running Commands

For background processes, use spawn():

rust,no_run
// Spawn a background process
agent.spawn("sleep").arg("60").run().await?;

// Check if a process is running
let result = agent.exec("pgrep").arg("sleep").run().await?;
if result.exit_code == 0 {
    println!("Sleep is running: {}", result.stdout.trim());
}

Complex Commands

Use shell for complex operations:

rust,no_run
// Pipelines
let result = agent.exec("/bin/sh")
    .args(["-c", "find /tmp -type f | wc -l"])
    .run()
    .await?;

// Conditionals
let result = agent.exec("/bin/sh")
    .args(["-c", "test -f /tmp/config.json && cat /tmp/config.json || echo '{}'"])
    .run()
    .await?;

// Multiple commands
let result = agent.exec("/bin/sh")
    .args(["-c", "cd /project && cargo build && cargo test"])
    .run()
    .await?;

System Information

Query system details with agent.info():

rust,no_run
let info = agent.info().await?;

println!("Kernel: {}", info.kernel_version);
println!("Hostname: {}", info.hostname);
println!("CPUs: {}", info.cpus);
println!("Memory: {} bytes", info.memory_bytes);

Clean Shutdown

Always shut down cleanly when done:

rust,no_run
// Run your commands...

// Then shut down
vm.shutdown().await?;

This signals the sandbox to terminate gracefully, waiting up to 30 seconds for clean shutdown before force-killing.

Complete Example

rust,no_run
use capsa::AccessMode;

async fn run_tests_in_sandbox() -> capsa::Result<()> {
    // Create sandbox with project directory
    let vm = capsa::sandbox()
        .cpus(4)
        .memory_mb(2048)
        .share("./my-project", "/project", AccessMode::ReadOnly)
        .build()
        .await?;

    // Connect to agent
    let agent = vm.agent().await?;

    // Run tests
    let result = agent.exec("/bin/sh")
        .args(["-c", "cd /project && cargo test 2>&1"])
        .run()
        .await?;

    // Check results
    if result.exit_code == 0 {
        println!("All tests passed!");
    } else {
        eprintln!("Tests failed:\n{}", result.stdout);
    }

    // Clean up
    vm.shutdown().await?;

    Ok(())
}

Next Steps

Released under the MIT License.