Skip to main content
Glama
output.rs4.84 kB
//! Terminal output reading and buffering. use std::sync::{Arc, Mutex}; use terminal_mcp_core::Result; use crate::session::Session; /// Output buffer for tracking raw terminal output. #[derive(Debug)] pub struct OutputBuffer { /// Raw bytes received from PTY (with ANSI codes) raw_buffer: Vec<u8>, /// Position of last read last_read_pos: usize, } impl OutputBuffer { /// Create a new output buffer. pub fn new() -> Self { Self { raw_buffer: Vec::new(), last_read_pos: 0, } } /// Append new output to the buffer. pub fn append(&mut self, bytes: &[u8]) { self.raw_buffer.extend_from_slice(bytes); } /// Get all output (with ANSI codes). pub fn read_all(&mut self) -> Vec<u8> { let output = self.raw_buffer.clone(); self.last_read_pos = self.raw_buffer.len(); output } /// Get output since last read (with ANSI codes). pub fn read_since_last(&mut self) -> Vec<u8> { let output = self.raw_buffer[self.last_read_pos..].to_vec(); self.last_read_pos = self.raw_buffer.len(); output } /// Get current buffer size. pub fn size(&self) -> usize { self.raw_buffer.len() } /// Get unread bytes count. pub fn unread_count(&self) -> usize { self.raw_buffer.len() - self.last_read_pos } /// Clear the buffer. pub fn clear(&mut self) { self.raw_buffer.clear(); self.last_read_pos = 0; } } impl Default for OutputBuffer { fn default() -> Self { Self::new() } } /// Output read result. #[derive(Debug, Clone)] pub struct OutputRead { /// The output text pub output: String, /// Number of bytes pub bytes: usize, /// Whether there's more unread output pub has_more: bool, } impl Session { /// Read raw terminal output. /// /// # Arguments /// * `since_last_read` - If true, only return new output since last read /// * `include_ansi` - If true, include ANSI escape codes; otherwise strip them pub fn read_output(&self, since_last_read: bool, include_ansi: bool) -> Result<OutputRead> { // Process any pending PTY output first self.process_output()?; // Get the raw bytes let output_buf_arc = self.output_buffer(); let mut output_buffer = output_buf_arc.lock().unwrap(); let raw_bytes = if since_last_read { output_buffer.read_since_last() } else { output_buffer.read_all() }; let bytes = raw_bytes.len(); let has_more = output_buffer.unread_count() > 0; drop(output_buffer); // Convert to string let output = if include_ansi { // Include ANSI codes String::from_utf8_lossy(&raw_bytes).to_string() } else { // Strip ANSI codes - use the grid's plain text let parser_arc = self.parser(); let parser = parser_arc.lock().unwrap(); parser.grid().to_plain_text() }; Ok(OutputRead { output, bytes, has_more, }) } /// Get the output buffer. pub(crate) fn output_buffer(&self) -> Arc<Mutex<OutputBuffer>> { // This will be added to Session struct Arc::clone(&self.output_buf) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_output_buffer_new() { let buffer = OutputBuffer::new(); assert_eq!(buffer.size(), 0); assert_eq!(buffer.unread_count(), 0); } #[test] fn test_output_buffer_append() { let mut buffer = OutputBuffer::new(); buffer.append(b"Hello"); buffer.append(b" World"); assert_eq!(buffer.size(), 11); assert_eq!(buffer.unread_count(), 11); } #[test] fn test_output_buffer_read_all() { let mut buffer = OutputBuffer::new(); buffer.append(b"Test output"); let output = buffer.read_all(); assert_eq!(output, b"Test output"); assert_eq!(buffer.unread_count(), 0); // Reading again returns empty let output2 = buffer.read_since_last(); assert_eq!(output2, b""); } #[test] fn test_output_buffer_read_since_last() { let mut buffer = OutputBuffer::new(); buffer.append(b"First"); let output1 = buffer.read_since_last(); assert_eq!(output1, b"First"); buffer.append(b" Second"); let output2 = buffer.read_since_last(); assert_eq!(output2, b" Second"); } #[test] fn test_output_buffer_clear() { let mut buffer = OutputBuffer::new(); buffer.append(b"Some data"); buffer.clear(); assert_eq!(buffer.size(), 0); assert_eq!(buffer.unread_count(), 0); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aybelatchane/mcp-server-terminal'

If you have feedback or need assistance with the MCP directory API, please join our Discord server