Skip to main content

termesh_core/
terminal.rs

1//! Protocol-neutral terminal vocabulary shared across the application boundary.
2//!
3//! OS handles stay in `terminal` and ACP wire identifiers stay in `agent`; these types
4//! are the typed effects joining those services to the single-owner model (ADR-0008).
5
6use std::path::PathBuf;
7
8use crate::{SessionId, TerminalGeneration, TerminalId};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct TerminalSpec {
12    pub program: String,
13    pub args: Vec<String>,
14    pub cwd: PathBuf,
15    pub env: Vec<(String, String)>,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct TerminalSize {
20    pub rows: u16,
21    pub cols: u16,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum TerminalOwner {
26    HumanShell,
27    HumanCommand,
28    Agent { session: SessionId },
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum TerminalStatus {
33    Starting,
34    Running { process_id: Option<u32> },
35    Exited(TerminalExit),
36    Failed(String),
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct TerminalExit {
41    pub code: Option<u32>,
42    pub signal: Option<String>,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub enum PtyRequest {
47    Spawn {
48        terminal: TerminalId,
49        generation: TerminalGeneration,
50        spec: TerminalSpec,
51        size: TerminalSize,
52    },
53    Write {
54        terminal: TerminalId,
55        generation: TerminalGeneration,
56        bytes: Vec<u8>,
57    },
58    Resize {
59        terminal: TerminalId,
60        generation: TerminalGeneration,
61        size: TerminalSize,
62    },
63    Kill {
64        terminal: TerminalId,
65        generation: TerminalGeneration,
66    },
67    Release {
68        terminal: TerminalId,
69        generation: TerminalGeneration,
70    },
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub enum PtyEvent {
75    Spawned { terminal: TerminalId, generation: TerminalGeneration, process_id: Option<u32> },
76    Output { terminal: TerminalId, generation: TerminalGeneration, bytes: Vec<u8> },
77    Exited { terminal: TerminalId, generation: TerminalGeneration, exit: TerminalExit },
78    Failed { terminal: TerminalId, generation: TerminalGeneration, message: String },
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
82pub enum AgentTerminalOperation {
83    Create { spec: TerminalSpec, output_byte_limit: usize, preauthorized: bool },
84    Output { terminal: TerminalId },
85    WaitForExit { terminal: TerminalId },
86    Kill { terminal: TerminalId },
87    Release { terminal: TerminalId },
88}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub enum AgentTerminalResponse {
92    Created { terminal: TerminalId },
93    Output { output: String, truncated: bool, exit: Option<TerminalExit> },
94    Exited(TerminalExit),
95    Acknowledged,
96    Error(String),
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use std::path::PathBuf;
103
104    #[test]
105    fn terminal_spec_keeps_program_and_arguments_separate() {
106        let spec = TerminalSpec {
107            program: "cargo".into(),
108            args: vec!["test".into(), "--workspace".into()],
109            cwd: PathBuf::from("/proj"),
110            env: vec![("RUST_BACKTRACE".into(), "1".into())],
111        };
112
113        assert_eq!(spec.program, "cargo");
114        assert_eq!(spec.args, ["test", "--workspace"]);
115    }
116}