Create a sandbox with `--keep` that runs a long-lived background command. The CLI process runs in the background. This method polls its stdout for `ready_marker` (a string the background command prints when it is ready to accept work). Sandbox name is parsed from the output header. # Arguments `command` — The command and arguments to run inside the sandbox (passed after `--`). `ready_marker` —
(
command: &[&str],
ready_marker: &str,
)
| 124 | /// not seen within [`SANDBOX_READY_TIMEOUT`], or the sandbox name cannot |
| 125 | /// be parsed. |
| 126 | pub async fn create_keep( |
| 127 | command: &[&str], |
| 128 | ready_marker: &str, |
| 129 | ) -> Result<Self, String> { |
| 130 | let mut cmd = openshell_cmd(); |
| 131 | cmd.arg("sandbox") |
| 132 | .arg("create") |
| 133 | .arg("--keep") |
| 134 | .arg("--") |
| 135 | .args(command); |
| 136 | cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); |
| 137 | |
| 138 | let mut child = cmd |
| 139 | .spawn() |
| 140 | .map_err(|e| format!("failed to spawn openshell: {e}"))?; |
| 141 | |
| 142 | let stdout = child.stdout.take().expect("stdout must be piped"); |
| 143 | let mut reader = BufReader::new(stdout).lines(); |
| 144 | |
| 145 | let mut accumulated = String::new(); |
| 146 | let mut name: Option<String> = None; |
| 147 | let mut ready = false; |
| 148 | |
| 149 | let poll_result = timeout(SANDBOX_READY_TIMEOUT, async { |
| 150 | while let Ok(Some(line)) = reader.next_line().await { |
| 151 | let clean = strip_ansi(&line); |
| 152 | accumulated.push_str(&clean); |
| 153 | accumulated.push('\n'); |
| 154 | |
| 155 | // Try to extract the sandbox name from the header. |
| 156 | if name.is_none() |
| 157 | && let Some(n) = extract_sandbox_name(&accumulated) |
| 158 | { |
| 159 | name = Some(n); |
| 160 | } |
| 161 | |
| 162 | // Check for the ready marker. |
| 163 | if clean.contains(ready_marker) { |
| 164 | ready = true; |
| 165 | break; |
| 166 | } |
| 167 | } |
| 168 | }) |
| 169 | .await; |
| 170 | |
| 171 | if poll_result.is_err() { |
| 172 | // Timeout — kill the child and report. |
| 173 | let _ = child.kill().await; |
| 174 | return Err(format!( |
| 175 | "sandbox did not become ready within {SANDBOX_READY_TIMEOUT:?}.\n\ |
| 176 | Output so far:\n{accumulated}" |
| 177 | )); |
| 178 | } |
| 179 | |
| 180 | if !ready { |
| 181 | // The line reader ended before seeing the marker (process exited). |
| 182 | let _ = child.kill().await; |
| 183 | return Err(format!( |
nothing calls this directly
no test coverage detected