tiny_agent/sandbox/
host.rs1use super::{ExecOutput, Sandbox, SandboxError};
2use async_trait::async_trait;
3use std::process::Stdio;
4use std::sync::Arc;
5use tokio::process::Command;
6
7#[derive(Default)]
11pub struct HostSandbox;
12
13impl HostSandbox {
14 pub fn new() -> Self {
15 Self
16 }
17}
18
19#[async_trait]
20impl Sandbox for HostSandbox {
21 async fn open(self: Arc<Self>, _session_id: &str) -> Result<Arc<dyn Sandbox>, SandboxError> {
22 Ok(self)
23 }
24
25 async fn execute(&self, command: &str) -> Result<ExecOutput, SandboxError> {
26 let output = Command::new("bash")
27 .arg("-c")
28 .arg(command)
29 .stdin(Stdio::null())
30 .stdout(Stdio::piped())
31 .stderr(Stdio::piped())
32 .output()
33 .await?;
34 Ok(ExecOutput {
35 stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
36 stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
37 exit_code: output.status.code(),
38 })
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[tokio::test]
47 async fn execute_captures_stdout_and_exit_code() {
48 let sb = HostSandbox::new();
49 let out = sb.execute("echo hello").await.unwrap();
50 assert_eq!(out.stdout, "hello\n");
51 assert!(out.is_success());
52 }
53
54 #[tokio::test]
55 async fn execute_reports_nonzero_exit() {
56 let sb = HostSandbox::new();
57 let out = sb.execute("exit 3").await.unwrap();
58 assert_eq!(out.exit_code, Some(3));
59 assert!(!out.is_success());
60 }
61}