Skip to main content

atomr_agents_coding_cli_isolator/
handle.rs

1//! `ProcessHandle` — the uniform handle every isolator returns.
2
3use std::time::Duration;
4
5use async_trait::async_trait;
6use tokio::sync::mpsc;
7
8use crate::error::IsolatorError;
9
10/// Per-spawn options the harness can tweak independently of the
11/// command itself.
12#[derive(Debug, Clone, Default)]
13pub struct IsolationOpts {
14    /// Capture stdout (NDJSON for headless). Always `true` in v1.
15    pub capture_stdout: bool,
16    /// Capture stderr.
17    pub capture_stderr: bool,
18    /// Wait this long after `kill` before forcing termination. None =
19    /// platform default.
20    pub grace: Option<Duration>,
21}
22
23/// Final exit status from a spawned process.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub struct ExitStatus {
26    pub code: Option<i32>,
27    pub success: bool,
28}
29
30impl ExitStatus {
31    pub fn ok() -> Self {
32        Self { code: Some(0), success: true }
33    }
34    pub fn from_code(code: i32) -> Self {
35        Self { code: Some(code), success: code == 0 }
36    }
37}
38
39/// Uniform handle to a running CLI process — host or container.
40///
41/// The handle owns one set of byte channels:
42///
43/// * `stdout_rx` / `stderr_rx`: lines (or chunks) emitted by the
44///   process. For PTY-allocated processes, all output flows through
45///   `stdout_rx` (stderr is multiplexed by the kernel).
46/// * `stdin_tx`: writes from the harness back into the process.
47///   `None` for processes spawned without an input channel.
48///
49/// All channels are tokio mpsc; the isolator background tasks
50/// translate platform-specific I/O to these channels.
51#[async_trait]
52pub trait ProcessHandle: Send {
53    /// Take ownership of the stdout byte stream. Returns `None` once
54    /// already taken.
55    fn take_stdout(&mut self) -> Option<mpsc::Receiver<Vec<u8>>>;
56
57    /// Take ownership of the stderr byte stream.
58    fn take_stderr(&mut self) -> Option<mpsc::Receiver<Vec<u8>>>;
59
60    /// Take ownership of the stdin sender.
61    fn take_stdin(&mut self) -> Option<mpsc::Sender<Vec<u8>>>;
62
63    /// `true` if the process was spawned with a PTY (interactive).
64    fn is_pty(&self) -> bool;
65
66    /// Resize the PTY window. Errors with `Unsupported` if not a PTY
67    /// process.
68    async fn resize_pty(&mut self, cols: u16, rows: u16) -> Result<(), IsolatorError>;
69
70    /// Send SIGTERM (and SIGKILL after grace).
71    async fn kill(&mut self) -> Result<(), IsolatorError>;
72
73    /// Wait for the process to exit. Idempotent — repeated calls
74    /// after exit return the cached status.
75    async fn wait(&mut self) -> Result<ExitStatus, IsolatorError>;
76}