Skip to main content

codex_wrapper/command/
sandbox.rs

1/// Run commands within a Codex-provided sandbox.
2///
3/// Wraps `codex sandbox <macos|linux|windows> -- <command> [args...]`.
4use crate::Codex;
5use crate::command::CodexCommand;
6use crate::error::Result;
7use crate::exec::{self, CommandOutput};
8
9/// Target sandbox platform.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum SandboxPlatform {
12    /// macOS Seatbelt sandbox.
13    MacOs,
14    /// Linux sandbox (bubblewrap by default).
15    Linux,
16    /// Windows restricted token sandbox.
17    Windows,
18}
19
20impl SandboxPlatform {
21    pub(crate) fn as_arg(self) -> &'static str {
22        match self {
23            Self::MacOs => "macos",
24            Self::Linux => "linux",
25            Self::Windows => "windows",
26        }
27    }
28}
29
30/// Run a command within a Codex-provided sandbox.
31#[derive(Debug, Clone)]
32pub struct SandboxCommand {
33    platform: SandboxPlatform,
34    command: String,
35    command_args: Vec<String>,
36}
37
38impl SandboxCommand {
39    /// Create a sandbox command for the given platform and command.
40    #[must_use]
41    pub fn new(platform: SandboxPlatform, command: impl Into<String>) -> Self {
42        Self {
43            platform,
44            command: command.into(),
45            command_args: Vec::new(),
46        }
47    }
48
49    /// Add an argument to the sandboxed command.
50    #[must_use]
51    pub fn arg(mut self, arg: impl Into<String>) -> Self {
52        self.command_args.push(arg.into());
53        self
54    }
55
56    /// Add multiple arguments to the sandboxed command.
57    #[must_use]
58    pub fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
59        self.command_args.extend(args.into_iter().map(Into::into));
60        self
61    }
62}
63
64impl CodexCommand for SandboxCommand {
65    type Output = CommandOutput;
66
67    fn args(&self) -> Vec<String> {
68        let mut args = vec![
69            "sandbox".into(),
70            self.platform.as_arg().into(),
71            "--".into(),
72            self.command.clone(),
73        ];
74        args.extend(self.command_args.clone());
75        args
76    }
77
78    async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
79        exec::run_codex(codex, self.args()).await
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use crate::command::CodexCommand;
87
88    #[test]
89    fn sandbox_macos_args() {
90        let cmd = SandboxCommand::new(SandboxPlatform::MacOs, "ls").arg("-la");
91        assert_eq!(
92            CodexCommand::args(&cmd),
93            vec!["sandbox", "macos", "--", "ls", "-la"]
94        );
95    }
96
97    #[test]
98    fn sandbox_linux_args() {
99        let cmd = SandboxCommand::new(SandboxPlatform::Linux, "cat").arg("/etc/hosts");
100        assert_eq!(
101            CodexCommand::args(&cmd),
102            vec!["sandbox", "linux", "--", "cat", "/etc/hosts"]
103        );
104    }
105}