pub struct PtyShell {
pub last_command: String,
pub output_buffer: String,
pub command_running: bool,
pub last_exit_code: Option<i32>,
pub output_truncated: bool,
pub last_returned_hash: Option<u64>,
pub attach_hint: Option<String>,
/* private fields */
}Expand description
Real PTY-based interactive shell
Uses portable-pty for true pseudo-terminal functionality, enabling proper handling of interactive programs like sudo, vim, etc.
Fields§
§last_command: StringLast command executed
output_buffer: StringAccumulated output buffer
command_running: boolWhether a command is currently running
last_exit_code: Option<i32>Exit code of the last completed foreground command, parsed from the prompt
marker (──➤<nonce>:<code>). None until a command finishes, and reset
to None while one is running.
output_truncated: boolFlag for output truncation
last_returned_hash: Option<u64>Hash of the last rendered output we shipped to the caller. Used by the
delta path in status_check to elide repeats when the screen is idle.
attach_hint: Option<String>Optional command a human can run to attach to the same terminal session.
Implementations§
Source§impl PtyShell
impl PtyShell
Sourcepub fn send_command(&mut self, command: &str) -> Result<()>
pub fn send_command(&mut self, command: &str) -> Result<()>
Send a command to the shell and start reading output
Sourcepub fn mark_command_started(&mut self)
pub fn mark_command_started(&mut self)
Start the monotonic runtime clock for a newly submitted command.
Sourcepub fn completion_report_pending(&self) -> bool
pub fn completion_report_pending(&self) -> bool
Whether the most recent foreground command has finished without any response having reported its exit yet.
Sourcepub fn mark_completion_reported(&mut self)
pub fn mark_completion_reported(&mut self)
Record that a response reported the current command as finished.
pub fn command_generation(&self) -> u64
pub fn incarnation(&self) -> u64
Sourcepub fn command_elapsed(&self) -> Option<Duration>
pub fn command_elapsed(&self) -> Option<Duration>
Elapsed runtime since the current command was submitted.
Sourcepub fn current_cwd(&self) -> &Path
pub fn current_cwd(&self) -> &Path
Current working directory last reported by this shell’s prompt.
Sourcepub fn delivered_output(&self) -> String
pub fn delivered_output(&self) -> String
Snapshot used as the left-hand side of incremental output rendering.
Sourcepub fn mark_output_delivered(&mut self, output: &str)
pub fn mark_output_delivered(&mut self, output: &str)
Mark the cumulative output snapshot as delivered after rendering it.
Sourcepub fn collect_scrollback(&self, lines: usize) -> String
pub fn collect_scrollback(&self, lines: usize) -> String
Return up to lines recent lines from the scrollback ring, oldest
first. When older lines were evicted or a long unterminated line was
clipped, a [winx: …] notice is prepended so the caller knows the
scrollback is incomplete rather than silently receiving less than asked.
Sourcepub fn fingerprint(text: &str) -> u64
pub fn fingerprint(text: &str) -> u64
Hash arbitrary rendered output into a u64 dedup key.
Sourcepub fn read_output(&mut self, timeout_secs: f32) -> Result<(String, bool)>
pub fn read_output(&mut self, timeout_secs: f32) -> Result<(String, bool)>
Read output from the PTY with timeout
Returns (output, is_complete) tuple where is_complete indicates
whether the command has finished (prompt detected)
Sourcepub fn poll_output_nonblocking(&mut self) -> bool
pub fn poll_output_nonblocking(&mut self) -> bool
Non-blocking drain of whatever the reader thread has already queued.
Unlike read_output, this never sleeps: it consumes
every chunk currently available, updates prompt/completion state, and
returns immediately. prune_finished_shells uses it so the global
background-shell lock is never held across a blocking 100ms read_output.
Returns true if the command has completed (prompt seen or PTY closed).
Sourcepub fn output_snapshot(&self) -> String
pub fn output_snapshot(&self) -> String
Snapshot of the accumulated output buffer — the same text read_output
returns. Lets callers poll non-blocking (poll_output_nonblocking) and
grab the buffer once at the end instead of cloning it on every slice.
Sourcepub fn set_scratch_root(&mut self, root: &Path)
pub fn set_scratch_root(&mut self, root: &Path)
Point output-offload scratch files at root (the workspace). Idempotent;
callers set it per command so it always tracks the current workspace.
Sourcepub fn scratch_path(&self) -> Option<&Path>
pub fn scratch_path(&self) -> Option<&Path>
The scratch file holding the current command’s dropped output head, if any was offloaded this command.
Sourcepub fn reset_scratch(&mut self)
pub fn reset_scratch(&mut self)
Forget the current command’s scratch file so the next command starts a fresh one. Does not delete the file (pruned later by age).
Sourcepub fn send_interrupt(&mut self) -> Result<()>
pub fn send_interrupt(&mut self) -> Result<()>
Send Ctrl+C (interrupt) to the PTY
Sourcepub fn send_suspend(&mut self) -> Result<()>
pub fn send_suspend(&mut self) -> Result<()>
Send Ctrl+Z (suspend) to the PTY
Sourcepub fn send_text(&mut self, text: &str) -> Result<()>
pub fn send_text(&mut self, text: &str) -> Result<()>
Send text directly to the PTY (for interactive input)
Sourcepub fn send_bytes(&mut self, bytes: &[u8]) -> Result<()>
pub fn send_bytes(&mut self, bytes: &[u8]) -> Result<()>
Send raw bytes directly to the PTY.
Sourcepub fn send_special_key(&mut self, key: &str) -> Result<()>
pub fn send_special_key(&mut self, key: &str) -> Result<()>
Send a special key sequence
Sourcepub fn live_snapshot(&self, max_lines: usize) -> Vec<String>
pub fn live_snapshot(&self, max_lines: usize) -> Vec<String>
Snapshot the live terminal screen — a stable, consolidated view of what a
human would currently see, with cursor moves/redraws applied and ANSI
stripped. max_lines of 0 returns the full screen buffer. This is the
foundation for piloting interactive TUIs (the claude CLI, vim, …).
Sourcepub fn live_in_alt_screen(&self) -> bool
pub fn live_in_alt_screen(&self) -> bool
Whether the live terminal is currently on the alternate screen buffer (a full-screen app like vim/htop/less is running).
Sourcepub fn live_osc_title(&self) -> String
pub fn live_osc_title(&self) -> String
The latest OSC 0/2 window title emitted by the foreground program (""
when none) — an out-of-band turn signal for the manifest recognizers
(claude/codex render a spinner glyph in the title while generating).
Sourcepub fn live_osc_progress(&self) -> String
pub fn live_osc_progress(&self) -> String
The latest OSC 9 payload after 9; ("" when none) — ConEmu-style
progress reads 4;<state>;<percent>.
Sourcepub fn live_cursor_position(&self) -> (u16, u16)
pub fn live_cursor_position(&self) -> (u16, u16)
Cursor position (row, col) on the live screen (0-based), so a piloting
agent knows where focus is in a menu/form. (0, 0) if the lock is poisoned.
Sourcepub fn live_snapshot_diff(
&self,
max_lines: usize,
threshold: usize,
) -> ScreenUpdate
pub fn live_snapshot_diff( &self, max_lines: usize, threshold: usize, ) -> ScreenUpdate
Diff the live screen against what the client last saw, returning only the
changed lines when the change is small (huge token savings over many
polls). Updates the baseline as a side effect. See ScreenUpdate.
Sourcepub fn process_id(&self) -> Option<u32>
pub fn process_id(&self) -> Option<u32>
OS process id of the shell that owns this PTY.
Trait Implementations§
Source§impl Drop for PtyShell
impl Drop for PtyShell
Source§fn drop(&mut self)
fn drop(&mut self)
Kill and reap the shell child and its whole process group so neither the shell nor anything it spawned leaks.
std::process::Child::drop neither kills nor waits, and even
Child::kill signals only the shell itself: a background job it started
(npm run dev &, a nohup’d server) would be reparented to init and live
on. Because portable-pty runs the shell as a session/group leader, we can
signal the whole group with a negative pid - SIGTERM first so well-behaved
children can clean up, then Child::kill (SIGKILL on the leader, which
closes the PTY slave so the reader thread’s read() returns EOF and the
thread exits), then SIGKILL the group to sweep anything still alive, then
reap. The pgid is captured while the leader is alive and reused: it stays
valid until we wait(), even after the leader dies. All best-effort.
Auto Trait Implementations§
impl !RefUnwindSafe for PtyShell
impl !Sync for PtyShell
impl !UnwindSafe for PtyShell
impl Freeze for PtyShell
impl Send for PtyShell
impl Unpin for PtyShell
impl UnsafeUnpin for PtyShell
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.impl<T> ErasedDestructor for Twhere
T: 'static,
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more