Skip to main content

zag_agent/
sandbox.rs

1use anyhow::{Context, Result};
2use log::debug;
3use std::process::Command;
4
5/// Configuration for running an agent inside a Docker sandbox.
6#[derive(Debug, Clone)]
7pub struct SandboxConfig {
8    pub name: String,
9    pub template: String,
10    pub workspace: String,
11}
12
13/// Return the Docker sandbox template image for a given provider.
14pub fn template_for_provider(provider: &str) -> &str {
15    match provider {
16        "claude" => "docker/sandbox-templates:claude-code",
17        "codex" => "docker/sandbox-templates:codex",
18        "gemini" => "docker/sandbox-templates:gemini",
19        "copilot" => "docker/sandbox-templates:copilot",
20        "ollama" => "shell",
21        _ => "docker/sandbox-templates:claude-code",
22    }
23}
24
25/// Generate a random sandbox name like `sandbox-a1b2c3d4`.
26pub fn generate_name() -> String {
27    use std::time::{SystemTime, UNIX_EPOCH};
28    let seed = SystemTime::now()
29        .duration_since(UNIX_EPOCH)
30        .unwrap_or_default()
31        .as_nanos();
32    let hash = seed ^ (std::process::id() as u128);
33    format!("sandbox-{:08x}", (hash & 0xFFFF_FFFF) as u32)
34}
35
36/// Build a `Command` that runs agent args inside a Docker sandbox.
37///
38/// Produces: `docker sandbox run --name <name> <template> <workspace> -- <agent_args...>`
39pub fn build_sandbox_command(config: &SandboxConfig, agent_args: Vec<String>) -> Command {
40    let mut cmd = Command::new("docker");
41    cmd.args([
42        "sandbox",
43        "run",
44        "--name",
45        &config.name,
46        &config.template,
47        &config.workspace,
48        "--",
49    ]);
50    cmd.args(&agent_args);
51    debug!(
52        "Sandbox command: docker sandbox run --name {} {} {} -- {}",
53        config.name,
54        config.template,
55        config.workspace,
56        agent_args.join(" ")
57    );
58    cmd
59}
60
61/// Remove a Docker sandbox by name.
62pub fn remove_sandbox(name: &str) -> Result<()> {
63    debug!("Removing sandbox: {}", name);
64    let output = Command::new("docker")
65        .args(["sandbox", "rm", name])
66        .output()
67        .context("Failed to run docker sandbox rm")?;
68    if !output.status.success() {
69        let stderr = String::from_utf8_lossy(&output.stderr);
70        anyhow::bail!("Failed to remove sandbox: {}", stderr.trim());
71    }
72    debug!("Sandbox removed: {}", name);
73    Ok(())
74}
75
76#[cfg(test)]
77#[path = "sandbox_tests.rs"]
78mod tests;