Skip to main content

codex/cli/
sandbox.rs

1use crate::{ConfigOverride, FeatureToggles};
2use std::{ffi::OsString, path::PathBuf, process::ExitStatus};
3
4/// Sandbox platform variant; maps to platform subcommands of `codex sandbox`.
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub enum SandboxPlatform {
7    Macos,
8    Linux,
9    Windows,
10}
11
12impl SandboxPlatform {
13    pub(crate) fn subcommand(self) -> &'static str {
14        match self {
15            SandboxPlatform::Macos => "macos",
16            SandboxPlatform::Linux => "linux",
17            SandboxPlatform::Windows => "windows",
18        }
19    }
20}
21
22/// Request to run an arbitrary command inside a Codex-provided sandbox.
23#[derive(Clone, Debug, Eq, PartialEq)]
24pub struct SandboxCommandRequest {
25    /// Target platform subcommand; maps to `macos` (alias `seatbelt`), `linux` (alias `landlock`), or `windows`.
26    pub platform: SandboxPlatform,
27    /// Trailing command arguments to execute. Must be non-empty to avoid the upstream CLI panic.
28    pub command: Vec<OsString>,
29    /// Request the workspace-write sandbox preset (`--full-auto`).
30    pub full_auto: bool,
31    /// Stream macOS sandbox denials after the child process exits (no-op on other platforms).
32    pub log_denials: bool,
33    /// Allow Unix sockets on macOS (`--allow-unix-socket`).
34    pub allow_unix_socket: bool,
35    /// Additional `--config key=value` overrides to pass through.
36    pub config_overrides: Vec<ConfigOverride>,
37    /// Feature toggles forwarded to `--enable`/`--disable`.
38    pub feature_toggles: FeatureToggles,
39    /// Working directory for the spawned command; falls back to the builder value, then the current process directory.
40    pub working_dir: Option<PathBuf>,
41}
42
43impl SandboxCommandRequest {
44    pub fn new<I, S>(platform: SandboxPlatform, command: I) -> Self
45    where
46        I: IntoIterator<Item = S>,
47        S: Into<OsString>,
48    {
49        Self {
50            platform,
51            command: command.into_iter().map(Into::into).collect(),
52            full_auto: false,
53            log_denials: false,
54            allow_unix_socket: false,
55            config_overrides: Vec::new(),
56            feature_toggles: FeatureToggles::default(),
57            working_dir: None,
58        }
59    }
60
61    pub fn full_auto(mut self, enable: bool) -> Self {
62        self.full_auto = enable;
63        self
64    }
65
66    pub fn log_denials(mut self, enable: bool) -> Self {
67        self.log_denials = enable;
68        self
69    }
70
71    pub fn allow_unix_socket(mut self, enable: bool) -> Self {
72        self.allow_unix_socket = enable;
73        self
74    }
75
76    pub fn config_override(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
77        self.config_overrides.push(ConfigOverride::new(key, value));
78        self
79    }
80
81    pub fn config_override_raw(mut self, raw: impl Into<String>) -> Self {
82        self.config_overrides.push(ConfigOverride::from_raw(raw));
83        self
84    }
85
86    pub fn enable_feature(mut self, name: impl Into<String>) -> Self {
87        self.feature_toggles.enable.push(name.into());
88        self
89    }
90
91    pub fn disable_feature(mut self, name: impl Into<String>) -> Self {
92        self.feature_toggles.disable.push(name.into());
93        self
94    }
95
96    pub fn working_dir(mut self, dir: impl Into<PathBuf>) -> Self {
97        self.working_dir = Some(dir.into());
98        self
99    }
100}
101
102/// Captured output from `codex sandbox <platform>`.
103#[derive(Clone, Debug)]
104pub struct SandboxRun {
105    /// Exit status returned by the inner command (mirrors the sandbox helper).
106    pub status: ExitStatus,
107    /// Captured stdout (mirrored to the console when `mirror_stdout` is true).
108    pub stdout: String,
109    /// Captured stderr (mirrored unless `quiet` is set).
110    pub stderr: String,
111}