1use anyhow::{Context, Result};
2use log::debug;
3use std::process::Command;
4
5#[derive(Debug, Clone)]
7pub struct SandboxConfig {
8 pub name: String,
9 pub template: String,
10 pub workspace: String,
11}
12
13pub 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
25pub 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
36pub 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
61pub 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;