claude_code_acp/terminal/
handle.rs1use std::sync::Arc;
7
8use sacp::schema::{TerminalExitStatus, TerminalId, TerminalOutputResponse};
9
10use super::TerminalClient;
11use crate::types::AgentError;
12
13#[derive(Debug)]
21pub struct TerminalHandle {
22 terminal_id: TerminalId,
24 client: Arc<TerminalClient>,
26 released: bool,
28}
29
30impl TerminalHandle {
31 pub fn new(terminal_id: TerminalId, client: Arc<TerminalClient>) -> Self {
33 Self {
34 terminal_id,
35 client,
36 released: false,
37 }
38 }
39
40 pub fn id(&self) -> &TerminalId {
42 &self.terminal_id
43 }
44
45 pub fn id_str(&self) -> &str {
47 self.terminal_id.0.as_ref()
48 }
49
50 pub async fn output(&self) -> Result<TerminalOutputResponse, AgentError> {
52 self.client.output(self.terminal_id.clone()).await
53 }
54
55 pub async fn wait_for_exit(&self) -> Result<TerminalExitStatus, AgentError> {
59 let response = self.client.wait_for_exit(self.terminal_id.clone()).await?;
60 Ok(response.exit_status)
61 }
62
63 pub async fn kill(&self) -> Result<(), AgentError> {
68 self.client.kill(self.terminal_id.clone()).await?;
69 Ok(())
70 }
71
72 pub async fn release(mut self) -> Result<(), AgentError> {
76 self.released = true;
77 self.client.release(self.terminal_id.clone()).await?;
78 Ok(())
79 }
80
81 pub async fn execute_and_wait(&self) -> Result<(String, TerminalExitStatus), AgentError> {
86 let exit_status = self.wait_for_exit().await?;
88
89 let output_response = self.output().await?;
91
92 Ok((output_response.output, exit_status))
93 }
94
95 pub fn is_released(&self) -> bool {
97 self.released
98 }
99}
100
101impl Drop for TerminalHandle {
106 fn drop(&mut self) {
107 if !self.released {
108 tracing::warn!(
109 terminal_id = %self.id_str(),
110 "TerminalHandle dropped without explicit release, \
111 terminal will be cleaned up by client timeout"
112 );
113 }
114 }
115}
116
117#[derive(Debug)]
119#[allow(dead_code)] pub struct TerminalBuilder {
121 client: Arc<TerminalClient>,
122 command: String,
123 args: Vec<String>,
124 cwd: Option<std::path::PathBuf>,
125 output_byte_limit: Option<u64>,
126}
127
128#[allow(dead_code)] impl TerminalBuilder {
130 pub fn new(client: Arc<TerminalClient>, command: impl Into<String>) -> Self {
132 Self {
133 client,
134 command: command.into(),
135 args: Vec::new(),
136 cwd: None,
137 output_byte_limit: None,
138 }
139 }
140
141 pub fn args(mut self, args: Vec<String>) -> Self {
143 self.args = args;
144 self
145 }
146
147 pub fn arg(mut self, arg: impl Into<String>) -> Self {
149 self.args.push(arg.into());
150 self
151 }
152
153 pub fn cwd(mut self, cwd: impl Into<std::path::PathBuf>) -> Self {
155 self.cwd = Some(cwd.into());
156 self
157 }
158
159 pub fn output_byte_limit(mut self, limit: u64) -> Self {
161 self.output_byte_limit = Some(limit);
162 self
163 }
164
165 pub async fn create(self) -> Result<TerminalHandle, AgentError> {
167 let terminal_id = self
168 .client
169 .create(self.command, self.args, self.cwd, self.output_byte_limit)
170 .await?;
171
172 Ok(TerminalHandle::new(terminal_id, self.client))
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 }