Skip to main content

hanzo_client/apis/
sandbox_api.rs

1/*
2 * Hanzo Cloud API
3 *
4 * The Hanzo Cloud API as a customer calls it: every operation under /v1/ except the operator's admin product, relay routes, legacy spellings and capabilities still reached by flag. Tagged by product: the first path segment after /v1/.
5 *
6 * The version of the OpenAPI document: v1
7 * 
8 * Generated by: https://openapi-generator.tech
9 */
10
11
12use reqwest;
13use serde::{Deserialize, Serialize, de::Error as _};
14use crate::{apis::ResponseContent, models};
15use super::{Error, configuration, ContentType};
16
17
18/// struct for typed errors of method [`delete_sandbox_by_id`]
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(untagged)]
21pub enum DeleteSandboxByIdError {
22    UnknownValue(serde_json::Value),
23}
24
25/// struct for typed errors of method [`end_sandbox`]
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(untagged)]
28pub enum EndSandboxError {
29    UnknownValue(serde_json::Value),
30}
31
32/// struct for typed errors of method [`get_sandbox`]
33#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(untagged)]
35pub enum GetSandboxError {
36    UnknownValue(serde_json::Value),
37}
38
39/// struct for typed errors of method [`get_sandbox_by_id`]
40#[derive(Debug, Clone, Serialize, Deserialize)]
41#[serde(untagged)]
42pub enum GetSandboxByIdError {
43    UnknownValue(serde_json::Value),
44}
45
46/// struct for typed errors of method [`get_sandbox_by_id_fs`]
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(untagged)]
49pub enum GetSandboxByIdFsError {
50    UnknownValue(serde_json::Value),
51}
52
53/// struct for typed errors of method [`get_sandbox_by_id_screen`]
54#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(untagged)]
56pub enum GetSandboxByIdScreenError {
57    UnknownValue(serde_json::Value),
58}
59
60/// struct for typed errors of method [`get_sandbox_by_id_screen_ws`]
61#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(untagged)]
63pub enum GetSandboxByIdScreenWsError {
64    UnknownValue(serde_json::Value),
65}
66
67/// struct for typed errors of method [`get_sandbox_by_id_terminal`]
68#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(untagged)]
70pub enum GetSandboxByIdTerminalError {
71    UnknownValue(serde_json::Value),
72}
73
74/// struct for typed errors of method [`get_sandbox_by_id_terminal_ws`]
75#[derive(Debug, Clone, Serialize, Deserialize)]
76#[serde(untagged)]
77pub enum GetSandboxByIdTerminalWsError {
78    UnknownValue(serde_json::Value),
79}
80
81/// struct for typed errors of method [`lease_sandbox`]
82#[derive(Debug, Clone, Serialize, Deserialize)]
83#[serde(untagged)]
84pub enum LeaseSandboxError {
85    UnknownValue(serde_json::Value),
86}
87
88/// struct for typed errors of method [`post_sandbox`]
89#[derive(Debug, Clone, Serialize, Deserialize)]
90#[serde(untagged)]
91pub enum PostSandboxError {
92    UnknownValue(serde_json::Value),
93}
94
95/// struct for typed errors of method [`post_sandbox_by_id_exec`]
96#[derive(Debug, Clone, Serialize, Deserialize)]
97#[serde(untagged)]
98pub enum PostSandboxByIdExecError {
99    UnknownValue(serde_json::Value),
100}
101
102/// struct for typed errors of method [`post_sandbox_by_id_fs`]
103#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(untagged)]
105pub enum PostSandboxByIdFsError {
106    UnknownValue(serde_json::Value),
107}
108
109/// struct for typed errors of method [`post_sandbox_by_id_screen_ticket`]
110#[derive(Debug, Clone, Serialize, Deserialize)]
111#[serde(untagged)]
112pub enum PostSandboxByIdScreenTicketError {
113    UnknownValue(serde_json::Value),
114}
115
116/// struct for typed errors of method [`post_sandbox_by_id_terminal_ticket`]
117#[derive(Debug, Clone, Serialize, Deserialize)]
118#[serde(untagged)]
119pub enum PostSandboxByIdTerminalTicketError {
120    UnknownValue(serde_json::Value),
121}
122
123/// struct for typed errors of method [`read_sandbox_file`]
124#[derive(Debug, Clone, Serialize, Deserialize)]
125#[serde(untagged)]
126pub enum ReadSandboxFileError {
127    UnknownValue(serde_json::Value),
128}
129
130/// struct for typed errors of method [`run_in_sandbox`]
131#[derive(Debug, Clone, Serialize, Deserialize)]
132#[serde(untagged)]
133pub enum RunInSandboxError {
134    UnknownValue(serde_json::Value),
135}
136
137/// struct for typed errors of method [`stop_run`]
138#[derive(Debug, Clone, Serialize, Deserialize)]
139#[serde(untagged)]
140pub enum StopRunError {
141    UnknownValue(serde_json::Value),
142}
143
144/// struct for typed errors of method [`write_sandbox_file`]
145#[derive(Debug, Clone, Serialize, Deserialize)]
146#[serde(untagged)]
147pub enum WriteSandboxFileError {
148    UnknownValue(serde_json::Value),
149}
150
151
152/// Ends a sandbox and releases the compute behind it. Answers 204.  ENDING IS NOT STOPPING. This releases the resource: the pod goes and anything only inside it goes with it. To end what a sandbox is RUNNING while keeping the sandbox — the checkout, the logs, the half-written file — the verb is POST /v1/sandbox/stop.  `?purge=1` additionally removes the record, so the sandbox stops being listed at all rather than being listed as ended.
153pub async fn delete_sandbox_by_id(configuration: &configuration::Configuration, id: &str, purge: Option<&str>) -> Result<(), Error<DeleteSandboxByIdError>> {
154    // add a prefix to parameters to efficiently prevent name collisions
155    let p_id = id;
156    let p_purge = purge;
157
158    let uri_str = format!("{}/v1/sandbox/{id}", configuration.base_path, id=crate::apis::urlencode(p_id));
159    let mut req_builder = configuration.client.request(reqwest::Method::DELETE, &uri_str);
160
161    if let Some(ref param_value) = p_purge {
162        req_builder = req_builder.query(&[("purge", &param_value.to_string())]);
163    }
164    if let Some(ref user_agent) = configuration.user_agent {
165        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
166    }
167    if let Some(ref token) = configuration.bearer_access_token {
168        req_builder = req_builder.bearer_auth(token.to_owned());
169    };
170
171    let req = req_builder.build()?;
172    let resp = configuration.client.execute(req).await?;
173
174    let status = resp.status();
175
176    if !status.is_client_error() && !status.is_server_error() {
177        Ok(())
178    } else {
179        let content = resp.text().await?;
180        let entity: Option<DeleteSandboxByIdError> = serde_json::from_str(&content).ok();
181        Err(Error::ResponseError(ResponseContent { status, content, entity }))
182    }
183}
184
185/// Ends the caller's sandbox lease: the pod goes, and the volume goes only when the caller asked for that too.
186pub async fn end_sandbox(configuration: &configuration::Configuration, end_in: models::EndIn) -> Result<(), Error<EndSandboxError>> {
187    // add a prefix to parameters to efficiently prevent name collisions
188    let p_end_in = end_in;
189
190    let uri_str = format!("{}/v1/sandbox/end", configuration.base_path);
191    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
192
193    if let Some(ref user_agent) = configuration.user_agent {
194        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
195    }
196    if let Some(ref token) = configuration.bearer_access_token {
197        req_builder = req_builder.bearer_auth(token.to_owned());
198    };
199    req_builder = req_builder.json(&p_end_in);
200
201    let req = req_builder.build()?;
202    let resp = configuration.client.execute(req).await?;
203
204    let status = resp.status();
205
206    if !status.is_client_error() && !status.is_server_error() {
207        Ok(())
208    } else {
209        let content = resp.text().await?;
210        let entity: Option<EndSandboxError> = serde_json::from_str(&content).ok();
211        Err(Error::ResponseError(ResponseContent { status, content, entity }))
212    }
213}
214
215/// Lists the caller org's sandboxes, newest first.  `?project=` and `?status=` narrow it. Only the caller's org's: the store is keyed on the validated org, so another tenant's sandbox is not something this operation can return.
216pub async fn get_sandbox(configuration: &configuration::Configuration, project: Option<&str>, status: Option<&str>) -> Result<models::SandboxList, Error<GetSandboxError>> {
217    // add a prefix to parameters to efficiently prevent name collisions
218    let p_project = project;
219    let p_status = status;
220
221    let uri_str = format!("{}/v1/sandbox", configuration.base_path);
222    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
223
224    if let Some(ref param_value) = p_project {
225        req_builder = req_builder.query(&[("project", &param_value.to_string())]);
226    }
227    if let Some(ref param_value) = p_status {
228        req_builder = req_builder.query(&[("status", &param_value.to_string())]);
229    }
230    if let Some(ref user_agent) = configuration.user_agent {
231        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
232    }
233    if let Some(ref token) = configuration.bearer_access_token {
234        req_builder = req_builder.bearer_auth(token.to_owned());
235    };
236
237    let req = req_builder.build()?;
238    let resp = configuration.client.execute(req).await?;
239
240    let status = resp.status();
241    let content_type = resp
242        .headers()
243        .get("content-type")
244        .and_then(|v| v.to_str().ok())
245        .unwrap_or("application/octet-stream");
246    let content_type = super::ContentType::from(content_type);
247
248    if !status.is_client_error() && !status.is_server_error() {
249        let content = resp.text().await?;
250        match content_type {
251            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
252            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::SandboxList`"))),
253            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::SandboxList`")))),
254        }
255    } else {
256        let content = resp.text().await?;
257        let entity: Option<GetSandboxError> = serde_json::from_str(&content).ok();
258        Err(Error::ResponseError(ResponseContent { status, content, entity }))
259    }
260}
261
262/// Returns one sandbox: its class, project, image, the runtime it was given, its status and when its lease ends.  An id the caller's org does not hold is the same 404 an unknown id gives — the store is keyed on the org, so a cross-tenant id simply is not there.
263pub async fn get_sandbox_by_id(configuration: &configuration::Configuration, id: &str) -> Result<models::Sandbox, Error<GetSandboxByIdError>> {
264    // add a prefix to parameters to efficiently prevent name collisions
265    let p_id = id;
266
267    let uri_str = format!("{}/v1/sandbox/{id}", configuration.base_path, id=crate::apis::urlencode(p_id));
268    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
269
270    if let Some(ref user_agent) = configuration.user_agent {
271        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
272    }
273    if let Some(ref token) = configuration.bearer_access_token {
274        req_builder = req_builder.bearer_auth(token.to_owned());
275    };
276
277    let req = req_builder.build()?;
278    let resp = configuration.client.execute(req).await?;
279
280    let status = resp.status();
281    let content_type = resp
282        .headers()
283        .get("content-type")
284        .and_then(|v| v.to_str().ok())
285        .unwrap_or("application/octet-stream");
286    let content_type = super::ContentType::from(content_type);
287
288    if !status.is_client_error() && !status.is_server_error() {
289        let content = resp.text().await?;
290        match content_type {
291            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
292            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Sandbox`"))),
293            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Sandbox`")))),
294        }
295    } else {
296        let content = resp.text().await?;
297        let entity: Option<GetSandboxByIdError> = serde_json::from_str(&content).ok();
298        Err(Error::ResponseError(ResponseContent { status, content, entity }))
299    }
300}
301
302/// Reads one file from the sandbox's project directory as text, or lists the entries when the path names a directory. Paths resolve under the project root and a path that climbs out is refused rather than rewritten.
303pub async fn get_sandbox_by_id_fs(configuration: &configuration::Configuration, id: &str) -> Result<(), Error<GetSandboxByIdFsError>> {
304    // add a prefix to parameters to efficiently prevent name collisions
305    let p_id = id;
306
307    let uri_str = format!("{}/v1/sandbox/{id}/fs", configuration.base_path, id=crate::apis::urlencode(p_id));
308    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
309
310    if let Some(ref user_agent) = configuration.user_agent {
311        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
312    }
313    if let Some(ref token) = configuration.bearer_access_token {
314        req_builder = req_builder.bearer_auth(token.to_owned());
315    };
316
317    let req = req_builder.build()?;
318    let resp = configuration.client.execute(req).await?;
319
320    let status = resp.status();
321
322    if !status.is_client_error() && !status.is_server_error() {
323        Ok(())
324    } else {
325        let content = resp.text().await?;
326        let entity: Option<GetSandboxByIdFsError> = serde_json::from_str(&content).ok();
327        Err(Error::ResponseError(ResponseContent { status, content, entity }))
328    }
329}
330
331/// A complete, self-contained desktop — noVNC inline, no other origin — that opens its own socket and draws this sandbox's display. Embed it in an iframe and there is nothing else to build.  `ticket` is the credential from the POST above, carried through to the socket. The page is NOT gated: it is inert markup and does not redeem the ticket, because a ticket is spent once and a page that spent it would hold a credential that no longer opens anything. `frame-ancestors` admits our own brands' hosts and nothing further.  It is served for every class, not only for `desktop`. The class is a fact about the image, and a sandbox with no VNC server already fails exactly — the connection is refused and the page says so — where a check here would be a second opinion about what is running inside a pod, formed from a label rather than from the pod.
332pub async fn get_sandbox_by_id_screen(configuration: &configuration::Configuration, id: &str) -> Result<(), Error<GetSandboxByIdScreenError>> {
333    // add a prefix to parameters to efficiently prevent name collisions
334    let p_id = id;
335
336    let uri_str = format!("{}/v1/sandbox/{id}/screen", configuration.base_path, id=crate::apis::urlencode(p_id));
337    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
338
339    if let Some(ref user_agent) = configuration.user_agent {
340        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
341    }
342    if let Some(ref token) = configuration.bearer_access_token {
343        req_builder = req_builder.bearer_auth(token.to_owned());
344    };
345
346    let req = req_builder.build()?;
347    let resp = configuration.client.execute(req).await?;
348
349    let status = resp.status();
350
351    if !status.is_client_error() && !status.is_server_error() {
352        Ok(())
353    } else {
354        let content = resp.text().await?;
355        let entity: Option<GetSandboxByIdScreenError> = serde_json::from_str(&content).ok();
356        Err(Error::ResponseError(ResponseContent { status, content, entity }))
357    }
358}
359
360/// Upgrades to a WebSocket carrying RFB — the VNC wire protocol — from the sandbox's display, for a host that brings its own client. Requires `ticket`; a missing, expired or already-spent one answers 401 without upgrading.  THE WIRE IS RFB, in BINARY frames both ways, and it is not interpreted here: this is a pipe between the caller's client and the server inside the pod.  THE PIXELS COME OUT THROUGH THE EXEC CHANNEL. The display binds 127.0.0.1 only and deliberately nothing else, so there is no address to dial — `socat` joins the stream to that loopback port over the same Kubernetes exec subresource every other call into a sandbox uses. One way in, one thing to authorize, nothing further exposed.  The window size is ignored. A browser pane is not the X server's geometry, and the client scales what it is given rather than asking a server with no RandR to resize itself.
361pub async fn get_sandbox_by_id_screen_ws(configuration: &configuration::Configuration, id: &str) -> Result<(), Error<GetSandboxByIdScreenWsError>> {
362    // add a prefix to parameters to efficiently prevent name collisions
363    let p_id = id;
364
365    let uri_str = format!("{}/v1/sandbox/{id}/screen/ws", configuration.base_path, id=crate::apis::urlencode(p_id));
366    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
367
368    if let Some(ref user_agent) = configuration.user_agent {
369        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
370    }
371    if let Some(ref token) = configuration.bearer_access_token {
372        req_builder = req_builder.bearer_auth(token.to_owned());
373    };
374
375    let req = req_builder.build()?;
376    let resp = configuration.client.execute(req).await?;
377
378    let status = resp.status();
379
380    if !status.is_client_error() && !status.is_server_error() {
381        Ok(())
382    } else {
383        let content = resp.text().await?;
384        let entity: Option<GetSandboxByIdScreenWsError> = serde_json::from_str(&content).ok();
385        Err(Error::ResponseError(ResponseContent { status, content, entity }))
386    }
387}
388
389/// A complete, self-contained terminal — xterm inline, no other origin — that opens its own socket and runs a shell in this sandbox. Embed it in an iframe and there is nothing else to build.  `ticket` is the credential from the POST above and `arg` names the session (see the socket below); both are simply carried through to the socket. The page is NOT gated — it is inert markup and does not redeem the ticket, because a ticket is spent once and a page that spent it would hold a credential that no longer opens anything.  When the terminal is up it posts `{source:\"hanzo-term\", ready:true}` to its parent frame, so a host can tell a live terminal from a page that failed into something else. `frame-ancestors` admits our own brands' hosts and nothing further.
390pub async fn get_sandbox_by_id_terminal(configuration: &configuration::Configuration, id: &str) -> Result<(), Error<GetSandboxByIdTerminalError>> {
391    // add a prefix to parameters to efficiently prevent name collisions
392    let p_id = id;
393
394    let uri_str = format!("{}/v1/sandbox/{id}/terminal", configuration.base_path, id=crate::apis::urlencode(p_id));
395    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
396
397    if let Some(ref user_agent) = configuration.user_agent {
398        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
399    }
400    if let Some(ref token) = configuration.bearer_access_token {
401        req_builder = req_builder.bearer_auth(token.to_owned());
402    };
403
404    let req = req_builder.build()?;
405    let resp = configuration.client.execute(req).await?;
406
407    let status = resp.status();
408
409    if !status.is_client_error() && !status.is_server_error() {
410        Ok(())
411    } else {
412        let content = resp.text().await?;
413        let entity: Option<GetSandboxByIdTerminalError> = serde_json::from_str(&content).ok();
414        Err(Error::ResponseError(ResponseContent { status, content, entity }))
415    }
416}
417
418/// Upgrades to a WebSocket carrying a login shell on a pseudo-terminal inside the sandbox — for a host that brings its own emulator. Requires `ticket`; a missing, expired or already-spent one answers 401 without upgrading.  THE WIRE. A text frame is stdin, unless it is the one control object `{\"resize\":{\"cols\":N,\"rows\":M}}`; a binary frame is always stdin. Output comes back as BINARY frames, because a pty emits arbitrary bytes cut at arbitrary offsets and a text frame carrying half a rune is one the browser closes the connection over.  `arg` names a SESSION: the shell runs under `tmux new -A -s <arg>`, which attaches to that session if it exists and creates it if it does not — so one sandbox holds as many terminals as a caller has names for. It is 1-64 characters of letters, digits, `-` or `_` and may not begin with `-`; anything else is 400. Without `arg` the shell is unnamed and unmultiplexed.  The shell is `zsh -l`, falling back to `bash -l` and then to `sh -l`, and to the plain shell again when the image has no tmux. Every step is a preference and none is a requirement: whatever else the image carries — the hanzo CLI included — is a command to type, never a condition for getting a prompt.
419pub async fn get_sandbox_by_id_terminal_ws(configuration: &configuration::Configuration, id: &str) -> Result<(), Error<GetSandboxByIdTerminalWsError>> {
420    // add a prefix to parameters to efficiently prevent name collisions
421    let p_id = id;
422
423    let uri_str = format!("{}/v1/sandbox/{id}/terminal/ws", configuration.base_path, id=crate::apis::urlencode(p_id));
424    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
425
426    if let Some(ref user_agent) = configuration.user_agent {
427        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
428    }
429    if let Some(ref token) = configuration.bearer_access_token {
430        req_builder = req_builder.bearer_auth(token.to_owned());
431    };
432
433    let req = req_builder.build()?;
434    let resp = configuration.client.execute(req).await?;
435
436    let status = resp.status();
437
438    if !status.is_client_error() && !status.is_server_error() {
439        Ok(())
440    } else {
441        let content = resp.text().await?;
442        let entity: Option<GetSandboxByIdTerminalWsError> = serde_json::from_str(&content).ok();
443        Err(Error::ResponseError(ResponseContent { status, content, entity }))
444    }
445}
446
447/// Leases the caller's sandbox, or returns the one it named if that lease is still running.  What comes back is a real computer: a pod under a runtime boundary with a toolchain already in it, its own filesystem, and a lease that ends it. Every other op here acts on the one this returns.
448pub async fn lease_sandbox(configuration: &configuration::Configuration, lease_in: models::LeaseIn) -> Result<models::Leased, Error<LeaseSandboxError>> {
449    // add a prefix to parameters to efficiently prevent name collisions
450    let p_lease_in = lease_in;
451
452    let uri_str = format!("{}/v1/sandbox/lease", configuration.base_path);
453    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
454
455    if let Some(ref user_agent) = configuration.user_agent {
456        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
457    }
458    if let Some(ref token) = configuration.bearer_access_token {
459        req_builder = req_builder.bearer_auth(token.to_owned());
460    };
461    req_builder = req_builder.json(&p_lease_in);
462
463    let req = req_builder.build()?;
464    let resp = configuration.client.execute(req).await?;
465
466    let status = resp.status();
467    let content_type = resp
468        .headers()
469        .get("content-type")
470        .and_then(|v| v.to_str().ok())
471        .unwrap_or("application/octet-stream");
472    let content_type = super::ContentType::from(content_type);
473
474    if !status.is_client_error() && !status.is_server_error() {
475        let content = resp.text().await?;
476        match content_type {
477            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
478            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Leased`"))),
479            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Leased`")))),
480        }
481    } else {
482        let content = resp.text().await?;
483        let entity: Option<LeaseSandboxError> = serde_json::from_str(&content).ok();
484        Err(Error::ResponseError(ResponseContent { status, content, entity }))
485    }
486}
487
488/// Leases a sandbox — a real computer — for the caller's org.  The class decides what it is for and therefore its image, working directory and isolation. A dev or desktop sandbox is SINGLE-ATTACH per project, so asking twice for one project resumes the one that exists rather than paying for a second; an exec sandbox carries no project and is bounded per org instead, refused 429 past the ceiling because the caller's correct response is to wait.  Answers 201 with the sandbox as leased, which names the runtime it GOT — not the one that was asked for.
489pub async fn post_sandbox(configuration: &configuration::Configuration, lease_in: models::LeaseIn) -> Result<models::Sandbox, Error<PostSandboxError>> {
490    // add a prefix to parameters to efficiently prevent name collisions
491    let p_lease_in = lease_in;
492
493    let uri_str = format!("{}/v1/sandbox", configuration.base_path);
494    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
495
496    if let Some(ref user_agent) = configuration.user_agent {
497        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
498    }
499    if let Some(ref token) = configuration.bearer_access_token {
500        req_builder = req_builder.bearer_auth(token.to_owned());
501    };
502    req_builder = req_builder.json(&p_lease_in);
503
504    let req = req_builder.build()?;
505    let resp = configuration.client.execute(req).await?;
506
507    let status = resp.status();
508    let content_type = resp
509        .headers()
510        .get("content-type")
511        .and_then(|v| v.to_str().ok())
512        .unwrap_or("application/octet-stream");
513    let content_type = super::ContentType::from(content_type);
514
515    if !status.is_client_error() && !status.is_server_error() {
516        let content = resp.text().await?;
517        match content_type {
518            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
519            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Sandbox`"))),
520            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Sandbox`")))),
521        }
522    } else {
523        let content = resp.text().await?;
524        let entity: Option<PostSandboxError> = serde_json::from_str(&content).ok();
525        Err(Error::ResponseError(ResponseContent { status, content, entity }))
526    }
527}
528
529/// Runs one command in a sandbox the caller holds and answers with its exit code, stdout and stderr.  Send `argv` — an argument vector cannot be word-split by accident — or `command` for a shell line, which is the only input here that ever reaches a shell. A non-zero exit is a SUCCESSFUL call carrying a failed command: the status is 200 and the exit code is in the answer, because \"the command failed\" and \"the call failed\" are different facts.
530pub async fn post_sandbox_by_id_exec(configuration: &configuration::Configuration, id: &str, exec_request: models::ExecRequest) -> Result<models::ExecResult, Error<PostSandboxByIdExecError>> {
531    // add a prefix to parameters to efficiently prevent name collisions
532    let p_id = id;
533    let p_exec_request = exec_request;
534
535    let uri_str = format!("{}/v1/sandbox/{id}/exec", configuration.base_path, id=crate::apis::urlencode(p_id));
536    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
537
538    if let Some(ref user_agent) = configuration.user_agent {
539        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
540    }
541    if let Some(ref token) = configuration.bearer_access_token {
542        req_builder = req_builder.bearer_auth(token.to_owned());
543    };
544    req_builder = req_builder.json(&p_exec_request);
545
546    let req = req_builder.build()?;
547    let resp = configuration.client.execute(req).await?;
548
549    let status = resp.status();
550    let content_type = resp
551        .headers()
552        .get("content-type")
553        .and_then(|v| v.to_str().ok())
554        .unwrap_or("application/octet-stream");
555    let content_type = super::ContentType::from(content_type);
556
557    if !status.is_client_error() && !status.is_server_error() {
558        let content = resp.text().await?;
559        match content_type {
560            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
561            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ExecResult`"))),
562            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ExecResult`")))),
563        }
564    } else {
565        let content = resp.text().await?;
566        let entity: Option<PostSandboxByIdExecError> = serde_json::from_str(&content).ok();
567        Err(Error::ResponseError(ResponseContent { status, content, entity }))
568    }
569}
570
571/// Writes the request body to one file in the sandbox's project directory, creating parent directories. Same confinement as the read above.
572pub async fn post_sandbox_by_id_fs(configuration: &configuration::Configuration, id: &str) -> Result<(), Error<PostSandboxByIdFsError>> {
573    // add a prefix to parameters to efficiently prevent name collisions
574    let p_id = id;
575
576    let uri_str = format!("{}/v1/sandbox/{id}/fs", configuration.base_path, id=crate::apis::urlencode(p_id));
577    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
578
579    if let Some(ref user_agent) = configuration.user_agent {
580        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
581    }
582    if let Some(ref token) = configuration.bearer_access_token {
583        req_builder = req_builder.bearer_auth(token.to_owned());
584    };
585
586    let req = req_builder.build()?;
587    let resp = configuration.client.execute(req).await?;
588
589    let status = resp.status();
590
591    if !status.is_client_error() && !status.is_server_error() {
592        Ok(())
593    } else {
594        let content = resp.text().await?;
595        let entity: Option<PostSandboxByIdFsError> = serde_json::from_str(&content).ok();
596        Err(Error::ResponseError(ResponseContent { status, content, entity }))
597    }
598}
599
600/// Mints a short-lived grant to open the screen of a desktop sandbox. Same properties as the terminal ticket, for the other endpoint.
601pub async fn post_sandbox_by_id_screen_ticket(configuration: &configuration::Configuration, id: &str) -> Result<models::TicketGrant, Error<PostSandboxByIdScreenTicketError>> {
602    // add a prefix to parameters to efficiently prevent name collisions
603    let p_id = id;
604
605    let uri_str = format!("{}/v1/sandbox/{id}/screen/ticket", configuration.base_path, id=crate::apis::urlencode(p_id));
606    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
607
608    if let Some(ref user_agent) = configuration.user_agent {
609        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
610    }
611    if let Some(ref token) = configuration.bearer_access_token {
612        req_builder = req_builder.bearer_auth(token.to_owned());
613    };
614
615    let req = req_builder.build()?;
616    let resp = configuration.client.execute(req).await?;
617
618    let status = resp.status();
619    let content_type = resp
620        .headers()
621        .get("content-type")
622        .and_then(|v| v.to_str().ok())
623        .unwrap_or("application/octet-stream");
624    let content_type = super::ContentType::from(content_type);
625
626    if !status.is_client_error() && !status.is_server_error() {
627        let content = resp.text().await?;
628        match content_type {
629            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
630            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::TicketGrant`"))),
631            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::TicketGrant`")))),
632        }
633    } else {
634        let content = resp.text().await?;
635        let entity: Option<PostSandboxByIdScreenTicketError> = serde_json::from_str(&content).ok();
636        Err(Error::ResponseError(ResponseContent { status, content, entity }))
637    }
638}
639
640/// Mints a short-lived grant to open a terminal on a sandbox.  The ticket travels in the query string of the URL it answers with, because a browser cannot set an Authorization header on a WebSocket handshake. It is single-purpose and short-lived for exactly that reason. A sandbox that is not running is 409 rather than a ticket that cannot be used.
641pub async fn post_sandbox_by_id_terminal_ticket(configuration: &configuration::Configuration, id: &str) -> Result<models::TicketGrant, Error<PostSandboxByIdTerminalTicketError>> {
642    // add a prefix to parameters to efficiently prevent name collisions
643    let p_id = id;
644
645    let uri_str = format!("{}/v1/sandbox/{id}/terminal/ticket", configuration.base_path, id=crate::apis::urlencode(p_id));
646    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
647
648    if let Some(ref user_agent) = configuration.user_agent {
649        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
650    }
651    if let Some(ref token) = configuration.bearer_access_token {
652        req_builder = req_builder.bearer_auth(token.to_owned());
653    };
654
655    let req = req_builder.build()?;
656    let resp = configuration.client.execute(req).await?;
657
658    let status = resp.status();
659    let content_type = resp
660        .headers()
661        .get("content-type")
662        .and_then(|v| v.to_str().ok())
663        .unwrap_or("application/octet-stream");
664    let content_type = super::ContentType::from(content_type);
665
666    if !status.is_client_error() && !status.is_server_error() {
667        let content = resp.text().await?;
668        match content_type {
669            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
670            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::TicketGrant`"))),
671            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::TicketGrant`")))),
672        }
673    } else {
674        let content = resp.text().await?;
675        let entity: Option<PostSandboxByIdTerminalTicketError> = serde_json::from_str(&content).ok();
676        Err(Error::ResponseError(ResponseContent { status, content, entity }))
677    }
678}
679
680/// Reads one path in the caller's sandbox: a file's bytes, or a directory's entries when the path names one.
681pub async fn read_sandbox_file(configuration: &configuration::Configuration, path_in: models::PathIn) -> Result<models::Blob, Error<ReadSandboxFileError>> {
682    // add a prefix to parameters to efficiently prevent name collisions
683    let p_path_in = path_in;
684
685    let uri_str = format!("{}/v1/sandbox/read", configuration.base_path);
686    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
687
688    if let Some(ref user_agent) = configuration.user_agent {
689        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
690    }
691    if let Some(ref token) = configuration.bearer_access_token {
692        req_builder = req_builder.bearer_auth(token.to_owned());
693    };
694    req_builder = req_builder.json(&p_path_in);
695
696    let req = req_builder.build()?;
697    let resp = configuration.client.execute(req).await?;
698
699    let status = resp.status();
700    let content_type = resp
701        .headers()
702        .get("content-type")
703        .and_then(|v| v.to_str().ok())
704        .unwrap_or("application/octet-stream");
705    let content_type = super::ContentType::from(content_type);
706
707    if !status.is_client_error() && !status.is_server_error() {
708        let content = resp.text().await?;
709        match content_type {
710            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
711            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Blob`"))),
712            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Blob`")))),
713        }
714    } else {
715        let content = resp.text().await?;
716        let entity: Option<ReadSandboxFileError> = serde_json::from_str(&content).ok();
717        Err(Error::ResponseError(ResponseContent { status, content, entity }))
718    }
719}
720
721/// Runs one command inside the caller's sandbox and answers its exit code, stdout and stderr. A non-zero exit is a successful call carrying a failed program, so it comes back as data and not as an error.  Name a `session` and the command NARRATES INTO IT: its output is appended to that session's live log as the program produces it, so anything watching the session — GET /v1/agents/sessions/stream, scoped to one run with ?root= — watches the work happen rather than waiting for the verdict. Without it the call is what it always was: silent until it returns, which for an agentic run is twenty-five minutes of blank screen.  The session is named; the TENANT is not. It is the org the caller already proved, so a session belonging to somebody else is absent from the org this call acts for and the append is refused there.
722pub async fn run_in_sandbox(configuration: &configuration::Configuration, run_in: models::RunIn) -> Result<models::Ran, Error<RunInSandboxError>> {
723    // add a prefix to parameters to efficiently prevent name collisions
724    let p_run_in = run_in;
725
726    let uri_str = format!("{}/v1/sandbox/run", configuration.base_path);
727    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
728
729    if let Some(ref user_agent) = configuration.user_agent {
730        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
731    }
732    if let Some(ref token) = configuration.bearer_access_token {
733        req_builder = req_builder.bearer_auth(token.to_owned());
734    };
735    req_builder = req_builder.json(&p_run_in);
736
737    let req = req_builder.build()?;
738    let resp = configuration.client.execute(req).await?;
739
740    let status = resp.status();
741    let content_type = resp
742        .headers()
743        .get("content-type")
744        .and_then(|v| v.to_str().ok())
745        .unwrap_or("application/octet-stream");
746    let content_type = super::ContentType::from(content_type);
747
748    if !status.is_client_error() && !status.is_server_error() {
749        let content = resp.text().await?;
750        match content_type {
751            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
752            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Ran`"))),
753            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Ran`")))),
754        }
755    } else {
756        let content = resp.text().await?;
757        let entity: Option<RunInSandboxError> = serde_json::from_str(&content).ok();
758        Err(Error::ResponseError(ResponseContent { status, content, entity }))
759    }
760}
761
762/// Interrupts whatever the caller's sandbox is running and answers how many commands it ended. The sandbox stays leased — stop ends the WORK, end ends the RESOURCE — so whoever stopped a run can still read what it left behind.
763pub async fn stop_run(configuration: &configuration::Configuration, stop_in: models::StopIn) -> Result<models::Stopped, Error<StopRunError>> {
764    // add a prefix to parameters to efficiently prevent name collisions
765    let p_stop_in = stop_in;
766
767    let uri_str = format!("{}/v1/sandbox/stop", configuration.base_path);
768    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
769
770    if let Some(ref user_agent) = configuration.user_agent {
771        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
772    }
773    if let Some(ref token) = configuration.bearer_access_token {
774        req_builder = req_builder.bearer_auth(token.to_owned());
775    };
776    req_builder = req_builder.json(&p_stop_in);
777
778    let req = req_builder.build()?;
779    let resp = configuration.client.execute(req).await?;
780
781    let status = resp.status();
782    let content_type = resp
783        .headers()
784        .get("content-type")
785        .and_then(|v| v.to_str().ok())
786        .unwrap_or("application/octet-stream");
787    let content_type = super::ContentType::from(content_type);
788
789    if !status.is_client_error() && !status.is_server_error() {
790        let content = resp.text().await?;
791        match content_type {
792            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
793            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Stopped`"))),
794            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Stopped`")))),
795        }
796    } else {
797        let content = resp.text().await?;
798        let entity: Option<StopRunError> = serde_json::from_str(&content).ok();
799        Err(Error::ResponseError(ResponseContent { status, content, entity }))
800    }
801}
802
803/// Writes bytes to one path in the caller's sandbox, creating parents, and answers the resolved path.
804pub async fn write_sandbox_file(configuration: &configuration::Configuration, write_in: models::WriteIn) -> Result<models::Wrote, Error<WriteSandboxFileError>> {
805    // add a prefix to parameters to efficiently prevent name collisions
806    let p_write_in = write_in;
807
808    let uri_str = format!("{}/v1/sandbox/write", configuration.base_path);
809    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
810
811    if let Some(ref user_agent) = configuration.user_agent {
812        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
813    }
814    if let Some(ref token) = configuration.bearer_access_token {
815        req_builder = req_builder.bearer_auth(token.to_owned());
816    };
817    req_builder = req_builder.json(&p_write_in);
818
819    let req = req_builder.build()?;
820    let resp = configuration.client.execute(req).await?;
821
822    let status = resp.status();
823    let content_type = resp
824        .headers()
825        .get("content-type")
826        .and_then(|v| v.to_str().ok())
827        .unwrap_or("application/octet-stream");
828    let content_type = super::ContentType::from(content_type);
829
830    if !status.is_client_error() && !status.is_server_error() {
831        let content = resp.text().await?;
832        match content_type {
833            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
834            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Wrote`"))),
835            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Wrote`")))),
836        }
837    } else {
838        let content = resp.text().await?;
839        let entity: Option<WriteSandboxFileError> = serde_json::from_str(&content).ok();
840        Err(Error::ResponseError(ResponseContent { status, content, entity }))
841    }
842}
843