agent_tui/daemon/
pty_session.rs

1//! PTY session wrapper - intentional partial boundary.
2//!
3//! This module is an intentional partial boundary that wraps `PtyHandle` from
4//! `agent_tui_terminal`. It exists at the root level because:
5//!
6//! 1. It's a thin wrapper with no business logic - just delegation and error mapping
7//! 2. It adapts the terminal crate's PTY interface to the daemon's error types
8//! 3. It provides a clean separation between PTY lifecycle and terminal emulation
9//!
10//! This follows Clean Architecture's guidance on partial boundaries: when the
11//! cost of a full boundary isn't justified, a simpler separation can be used.
12
13use crate::terminal::PtyHandle;
14
15use crate::daemon::error::SessionError;
16
17/// Wraps PTY lifecycle operations.
18///
19/// Provides a clean interface for PTY I/O and lifecycle management,
20/// separate from terminal emulation concerns.
21pub struct PtySession {
22    handle: PtyHandle,
23}
24
25impl PtySession {
26    pub fn new(handle: PtyHandle) -> Self {
27        Self { handle }
28    }
29
30    pub fn pid(&self) -> Option<u32> {
31        self.handle.pid()
32    }
33
34    pub fn is_running(&mut self) -> bool {
35        self.handle.is_running()
36    }
37
38    pub fn write(&self, data: &[u8]) -> Result<(), SessionError> {
39        self.handle.write(data).map_err(SessionError::Pty)
40    }
41
42    pub fn write_str(&self, s: &str) -> Result<(), SessionError> {
43        self.handle.write_str(s).map_err(SessionError::Pty)
44    }
45
46    pub fn try_read(&self, buf: &mut [u8], timeout_ms: i32) -> Result<usize, SessionError> {
47        self.handle
48            .try_read(buf, timeout_ms)
49            .map_err(SessionError::Pty)
50    }
51
52    pub fn resize(&mut self, cols: u16, rows: u16) -> Result<(), SessionError> {
53        self.handle.resize(cols, rows).map_err(SessionError::Pty)
54    }
55
56    pub fn kill(&mut self) -> Result<(), SessionError> {
57        self.handle.kill().map_err(SessionError::Pty)
58    }
59}