codex_wrapper/command/
sandbox.rs1use crate::Codex;
8use crate::command::CodexCommand;
9use crate::error::Result;
10use crate::exec::{self, CommandOutput};
11
12#[derive(Debug, Clone)]
16pub struct SandboxCommand {
17 command: String,
18 command_args: Vec<String>,
19 config_overrides: Vec<String>,
20 enabled_features: Vec<String>,
21 disabled_features: Vec<String>,
22 permission_profile: Option<String>,
23 profile: Option<String>,
24 cd: Option<String>,
25}
26
27impl SandboxCommand {
28 #[must_use]
30 pub fn new(command: impl Into<String>) -> Self {
31 Self {
32 command: command.into(),
33 command_args: Vec::new(),
34 config_overrides: Vec::new(),
35 enabled_features: Vec::new(),
36 disabled_features: Vec::new(),
37 permission_profile: None,
38 profile: None,
39 cd: None,
40 }
41 }
42
43 #[must_use]
45 pub fn arg(mut self, arg: impl Into<String>) -> Self {
46 self.command_args.push(arg.into());
47 self
48 }
49
50 #[must_use]
52 pub fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
53 self.command_args.extend(args.into_iter().map(Into::into));
54 self
55 }
56
57 #[must_use]
59 pub fn config(mut self, key_value: impl Into<String>) -> Self {
60 self.config_overrides.push(key_value.into());
61 self
62 }
63
64 #[must_use]
66 pub fn enable(mut self, feature: impl Into<String>) -> Self {
67 self.enabled_features.push(feature.into());
68 self
69 }
70
71 #[must_use]
73 pub fn disable(mut self, feature: impl Into<String>) -> Self {
74 self.disabled_features.push(feature.into());
75 self
76 }
77
78 #[must_use]
80 pub fn permission_profile(mut self, name: impl Into<String>) -> Self {
81 self.permission_profile = Some(name.into());
82 self
83 }
84
85 #[must_use]
88 pub fn profile(mut self, name: impl Into<String>) -> Self {
89 self.profile = Some(name.into());
90 self
91 }
92
93 #[must_use]
96 pub fn cd(mut self, dir: impl Into<String>) -> Self {
97 self.cd = Some(dir.into());
98 self
99 }
100}
101
102impl CodexCommand for SandboxCommand {
103 type Output = CommandOutput;
104
105 fn args(&self) -> Vec<String> {
106 let mut args = vec!["sandbox".to_string()];
107 for value in &self.config_overrides {
108 args.push("-c".into());
109 args.push(value.clone());
110 }
111 for value in &self.enabled_features {
112 args.push("--enable".into());
113 args.push(value.clone());
114 }
115 for value in &self.disabled_features {
116 args.push("--disable".into());
117 args.push(value.clone());
118 }
119 if let Some(name) = &self.permission_profile {
120 args.push("--permission-profile".into());
121 args.push(name.clone());
122 }
123 if let Some(name) = &self.profile {
124 args.push("--profile".into());
125 args.push(name.clone());
126 }
127 if let Some(dir) = &self.cd {
128 args.push("--cd".into());
129 args.push(dir.clone());
130 }
131 args.push("--".into());
132 args.push(self.command.clone());
133 args.extend(self.command_args.clone());
134 args
135 }
136
137 async fn execute(&self, codex: &Codex) -> Result<CommandOutput> {
138 exec::run_codex(codex, self.args()).await
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use crate::command::CodexCommand;
146
147 #[test]
148 fn sandbox_basic_args() {
149 let cmd = SandboxCommand::new("ls").arg("-la");
150 assert_eq!(CodexCommand::args(&cmd), vec!["sandbox", "--", "ls", "-la"]);
151 }
152
153 #[test]
154 fn sandbox_args_with_options() {
155 let cmd = SandboxCommand::new("cat")
156 .permission_profile("readonly")
157 .cd("/tmp")
158 .args(["/etc/hosts"]);
159 assert_eq!(
160 CodexCommand::args(&cmd),
161 vec![
162 "sandbox",
163 "--permission-profile",
164 "readonly",
165 "--cd",
166 "/tmp",
167 "--",
168 "cat",
169 "/etc/hosts",
170 ]
171 );
172 }
173}