1use std::future::Future;
9use std::pin::Pin;
10
11use futures_util::Stream;
12
13use crate::audit::{AuditLogEntry, AuditLogResponse, GetLogOptions};
14use crate::client::Client;
15use crate::desktop::DesktopStream;
16use crate::error::BoxxkiteError;
17use crate::files::{
18 ExecOptions, ExecResult, FileCreateResult, FileOptions, GlobOptions, GlobResult, GrepOptions,
19 GrepResult, HttpRequestOptions, HttpRequestResult, LsOptions, LsResult, StrReplaceOptions,
20 StrReplaceResult, ViewOptions, ViewResult,
21};
22use crate::lsp::{LspCompletionResult, LspOpenResult, LspStartResult, LspStopResult};
23use crate::preview::{PreviewRevokeResult, PreviewUrl};
24use crate::processes::{
25 ProcessInputResult, ProcessListResult, ProcessOutputResult, ProcessStartResult,
26 ProcessStopResult, StartProcessOptions,
27};
28use crate::sandboxes::CreateSandboxOptions;
29use crate::takeover::TakeoverStream;
30
31#[derive(Clone, Debug)]
36pub struct SandboxSession {
37 client: Client,
38 id: String,
39}
40
41impl SandboxSession {
42 pub fn id(&self) -> &str {
44 &self.id
45 }
46
47 pub fn client(&self) -> &Client {
50 &self.client
51 }
52
53 pub async fn exec(
54 &self,
55 command: &str,
56 options: ExecOptions,
57 ) -> Result<ExecResult, BoxxkiteError> {
58 self.client.exec(&self.id, command, options).await
59 }
60
61 pub async fn http_request(
62 &self,
63 method: &str,
64 url: &str,
65 options: HttpRequestOptions,
66 ) -> Result<HttpRequestResult, BoxxkiteError> {
67 self.client
68 .http_request(&self.id, method, url, options)
69 .await
70 }
71
72 pub async fn file_create(
73 &self,
74 path: &str,
75 content: &str,
76 options: FileOptions,
77 ) -> Result<FileCreateResult, BoxxkiteError> {
78 self.client
79 .file_create(&self.id, path, content, options)
80 .await
81 }
82
83 pub async fn view(&self, path: &str, options: ViewOptions) -> Result<ViewResult, BoxxkiteError> {
84 self.client.view(&self.id, path, options).await
85 }
86
87 pub async fn str_replace(
88 &self,
89 path: &str,
90 old_str: &str,
91 new_str: &str,
92 options: StrReplaceOptions,
93 ) -> Result<StrReplaceResult, BoxxkiteError> {
94 self.client
95 .str_replace(&self.id, path, old_str, new_str, options)
96 .await
97 }
98
99 pub async fn ls(&self, options: LsOptions) -> Result<LsResult, BoxxkiteError> {
100 self.client.ls(&self.id, options).await
101 }
102
103 pub async fn glob(
104 &self,
105 pattern: &str,
106 options: GlobOptions,
107 ) -> Result<GlobResult, BoxxkiteError> {
108 self.client.glob(&self.id, pattern, options).await
109 }
110
111 pub async fn grep(
112 &self,
113 pattern: &str,
114 options: GrepOptions,
115 ) -> Result<GrepResult, BoxxkiteError> {
116 self.client.grep(&self.id, pattern, options).await
117 }
118
119 pub async fn get_log(&self, options: GetLogOptions) -> Result<AuditLogResponse, BoxxkiteError> {
120 self.client.get_log(&self.id, options).await
121 }
122
123 pub fn watch(
124 &self,
125 ) -> Pin<Box<dyn Stream<Item = Result<AuditLogEntry, BoxxkiteError>> + Send + 'static>> {
126 self.client.watch(&self.id)
127 }
128
129 pub async fn start_process(
130 &self,
131 command: &str,
132 options: StartProcessOptions,
133 ) -> Result<ProcessStartResult, BoxxkiteError> {
134 self.client.start_process(&self.id, command, options).await
135 }
136
137 pub async fn list_processes(&self) -> Result<ProcessListResult, BoxxkiteError> {
138 self.client.list_processes(&self.id).await
139 }
140
141 pub async fn get_process_output(
142 &self,
143 process_id: &str,
144 since_offset: i64,
145 ) -> Result<ProcessOutputResult, BoxxkiteError> {
146 self.client
147 .get_process_output(&self.id, process_id, since_offset)
148 .await
149 }
150
151 pub async fn send_process_input(
152 &self,
153 process_id: &str,
154 data: &str,
155 ) -> Result<ProcessInputResult, BoxxkiteError> {
156 self.client
157 .send_process_input(&self.id, process_id, data)
158 .await
159 }
160
161 pub async fn stop_process(&self, process_id: &str) -> Result<ProcessStopResult, BoxxkiteError> {
162 self.client.stop_process(&self.id, process_id).await
163 }
164
165 pub async fn takeover(&self) -> Result<TakeoverStream, BoxxkiteError> {
166 self.client.takeover(&self.id).await
167 }
168
169 pub async fn desktop_takeover(&self) -> Result<DesktopStream, BoxxkiteError> {
170 self.client.desktop_takeover(&self.id).await
171 }
172
173 pub async fn create_preview_url(
174 &self,
175 port: u16,
176 ttl_seconds: Option<u32>,
177 ) -> Result<PreviewUrl, BoxxkiteError> {
178 self.client
179 .create_preview_url(&self.id, port, ttl_seconds)
180 .await
181 }
182
183 pub async fn revoke_preview_url(
184 &self,
185 port: u16,
186 token_id: &str,
187 ) -> Result<PreviewRevokeResult, BoxxkiteError> {
188 self.client
189 .revoke_preview_url(&self.id, port, token_id)
190 .await
191 }
192
193 pub async fn lsp_start(&self, language: &str) -> Result<LspStartResult, BoxxkiteError> {
194 self.client.lsp_start(&self.id, language).await
195 }
196
197 pub async fn lsp_open(
198 &self,
199 lsp_id: &str,
200 path: &str,
201 content: &str,
202 ) -> Result<LspOpenResult, BoxxkiteError> {
203 self.client.lsp_open(&self.id, lsp_id, path, content).await
204 }
205
206 pub async fn lsp_completion(
207 &self,
208 lsp_id: &str,
209 path: &str,
210 line: u32,
211 character: u32,
212 ) -> Result<LspCompletionResult, BoxxkiteError> {
213 self.client
214 .lsp_completion(&self.id, lsp_id, path, line, character)
215 .await
216 }
217
218 pub async fn lsp_stop(&self, lsp_id: &str) -> Result<LspStopResult, BoxxkiteError> {
219 self.client.lsp_stop(&self.id, lsp_id).await
220 }
221}
222
223impl Client {
224 pub async fn with_sandbox<F, Fut, T>(
245 &self,
246 options: CreateSandboxOptions,
247 f: F,
248 ) -> Result<T, BoxxkiteError>
249 where
250 F: FnOnce(SandboxSession) -> Fut,
251 Fut: Future<Output = Result<T, BoxxkiteError>>,
252 {
253 let sandbox = self.create_sandbox(options).await?;
254 let session = SandboxSession {
255 client: self.clone(),
256 id: sandbox.id.clone(),
257 };
258 let result = f(session).await;
259 let _ = self.destroy_sandbox(&sandbox.id).await;
261 result
262 }
263}