claude_code_acp/terminal/
client.rs1use std::path::PathBuf;
6use std::sync::Arc;
7
8use sacp::link::AgentToClient;
9use sacp::schema::{
10 CreateTerminalRequest, CreateTerminalResponse, EnvVariable, KillTerminalCommandRequest,
11 KillTerminalCommandResponse, ReleaseTerminalRequest, ReleaseTerminalResponse, SessionId,
12 TerminalId, TerminalOutputRequest, TerminalOutputResponse, WaitForTerminalExitRequest,
13 WaitForTerminalExitResponse,
14};
15use sacp::JrConnectionCx;
16
17use crate::types::AgentError;
18
19#[derive(Debug, Clone)]
25pub struct TerminalClient {
26 connection_cx: JrConnectionCx<AgentToClient>,
28 session_id: SessionId,
30}
31
32impl TerminalClient {
33 pub fn new(connection_cx: JrConnectionCx<AgentToClient>, session_id: impl Into<SessionId>) -> Self {
35 Self {
36 connection_cx,
37 session_id: session_id.into(),
38 }
39 }
40
41 pub async fn create(
53 &self,
54 command: impl Into<String>,
55 args: Vec<String>,
56 cwd: Option<PathBuf>,
57 output_byte_limit: Option<u64>,
58 ) -> Result<TerminalId, AgentError> {
59 let mut request = CreateTerminalRequest::new(self.session_id.clone(), command);
60 request = request.args(args);
61
62 request = request.env(vec![EnvVariable::new("CLAUDECODE", "1")]);
64
65 if let Some(cwd_path) = cwd {
66 request = request.cwd(cwd_path);
67 }
68
69 if let Some(limit) = output_byte_limit {
70 request = request.output_byte_limit(limit);
71 }
72
73 tracing::debug!(?request, "Sending terminal/create request");
74
75 let response: CreateTerminalResponse = self
76 .connection_cx
77 .send_request(request)
78 .block_task()
79 .await
80 .map_err(|e| AgentError::Internal(format!("Terminal create failed: {}", e)))?;
81
82 tracing::debug!(?response, "Received terminal/create response");
83
84 Ok(response.terminal_id)
85 }
86
87 pub async fn output(
91 &self,
92 terminal_id: impl Into<TerminalId>,
93 ) -> Result<TerminalOutputResponse, AgentError> {
94 let request = TerminalOutputRequest::new(self.session_id.clone(), terminal_id);
95
96 self.connection_cx
97 .send_request(request)
98 .block_task()
99 .await
100 .map_err(|e| AgentError::Internal(format!("Terminal output failed: {}", e)))
101 }
102
103 pub async fn wait_for_exit(
107 &self,
108 terminal_id: impl Into<TerminalId>,
109 ) -> Result<WaitForTerminalExitResponse, AgentError> {
110 let request = WaitForTerminalExitRequest::new(self.session_id.clone(), terminal_id);
111
112 self.connection_cx
113 .send_request(request)
114 .block_task()
115 .await
116 .map_err(|e| AgentError::Internal(format!("Terminal wait_for_exit failed: {}", e)))
117 }
118
119 pub async fn kill(
124 &self,
125 terminal_id: impl Into<TerminalId>,
126 ) -> Result<KillTerminalCommandResponse, AgentError> {
127 let request = KillTerminalCommandRequest::new(self.session_id.clone(), terminal_id);
128
129 self.connection_cx
130 .send_request(request)
131 .block_task()
132 .await
133 .map_err(|e| AgentError::Internal(format!("Terminal kill failed: {}", e)))
134 }
135
136 pub async fn release(
141 &self,
142 terminal_id: impl Into<TerminalId>,
143 ) -> Result<ReleaseTerminalResponse, AgentError> {
144 let request = ReleaseTerminalRequest::new(self.session_id.clone(), terminal_id);
145
146 self.connection_cx
147 .send_request(request)
148 .block_task()
149 .await
150 .map_err(|e| AgentError::Internal(format!("Terminal release failed: {}", e)))
151 }
152
153 pub fn session_id(&self) -> &SessionId {
155 &self.session_id
156 }
157
158 pub fn into_arc(self) -> Arc<Self> {
160 Arc::new(self)
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 #[test]
167 fn test_terminal_client_session_id() {
168 }
171}