Skip to main content

solari/
types.rs

1//! Wire types for the Solari SDK core surface. serde field names match the JSON
2//! wire EXACTLY (camelCase where the gateway/control-WS is camelCase).
3
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use std::collections::HashMap;
7
8// ---------------------------------------------------------------------------
9// REST — session lifecycle
10// ---------------------------------------------------------------------------
11
12/// Idle lifecycle policy (pause/kill on timeout; optional auto-resume).
13#[derive(Debug, Clone, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct Lifecycle {
16    pub on_timeout: String,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub auto_resume: Option<bool>,
19}
20
21/// One attach-at-create instruction: a persistent volume and the absolute
22/// in-guest path to mount it at. The mount happens host-side before the guest
23/// starts, and survives pause/resume and recreate.
24#[derive(Debug, Clone, Serialize, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct VolumeAttachment {
27    /// A `vol_…` id belonging to your org.
28    pub volume_id: String,
29    /// Absolute in-guest mount point, e.g. `/data`.
30    pub path: String,
31}
32
33/// Wire body for `POST /sandboxes`. Null/unset fields are omitted.
34#[derive(Debug, Clone, Default, Serialize)]
35#[serde(rename_all = "camelCase")]
36pub struct CreateSandboxRequest {
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub template: Option<String>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub kind: Option<String>,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub cpu: Option<u32>,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub mem_mb: Option<u32>,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub disk_gb: Option<u32>,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub envs: Option<HashMap<String, String>>,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub metadata: Option<HashMap<String, String>>,
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub timeout_ms: Option<u64>,
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub from_snapshot: Option<String>,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub lifecycle: Option<Lifecycle>,
57    /// Initial display resolution, e.g. `"1280x720"`. Desktops only.
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub resolution: Option<String>,
60    /// Record the session server-side; the response carries a presigned playback
61    /// URL. Desktops only — `record` on a headless sandbox is rejected (400
62    /// `RecordingRequiresDesktop`).
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub record: Option<bool>,
65    /// Persistent volumes to mount before the session starts.
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub volumes: Option<Vec<VolumeAttachment>>,
68}
69
70/// `POST /sandboxes/:id/resume` reply. The gateway may hand back a fresh
71/// `controlUrl` (the session lands on a new slot, so the old one is stale);
72/// `Client::resume` derives one when it does not.
73#[derive(Debug, Clone, Default, Deserialize)]
74#[serde(rename_all = "camelCase")]
75pub struct ResumeResponse {
76    #[serde(default)]
77    pub state: String,
78    #[serde(default)]
79    pub control_url: Option<String>,
80}
81
82/// `201` response from `POST /sandboxes`.
83#[derive(Debug, Clone, Deserialize)]
84#[serde(rename_all = "camelCase")]
85pub struct CreateSandboxResponse {
86    pub sandbox_id: String,
87    #[serde(default)]
88    pub kind: String,
89    pub control_url: String,
90    #[serde(default)]
91    pub expires_at: String,
92    /// Present for desktops only; ignored on the sandbox core surface.
93    #[serde(default)]
94    pub stream_url: Option<String>,
95}
96
97/// `GET /sandboxes/:id` response (subset used by `connect`).
98#[derive(Debug, Clone, Deserialize)]
99#[serde(rename_all = "camelCase")]
100pub struct SandboxView {
101    pub sandbox_id: String,
102    #[serde(default)]
103    pub kind: String,
104    #[serde(default)]
105    pub state: String,
106    #[serde(default)]
107    pub expires_at: String,
108    /// Some gateways include a control URL directly; derived otherwise.
109    #[serde(default)]
110    pub control_url: Option<String>,
111}
112
113/// Shape gateway error bodies may take.
114#[derive(Debug, Clone, Default, Deserialize)]
115pub struct GatewayErrorBody {
116    #[serde(default)]
117    pub code: Option<String>,
118    #[serde(default)]
119    pub error: Option<String>,
120    #[serde(default)]
121    pub message: Option<String>,
122    #[serde(default)]
123    pub retryable: Option<bool>,
124}
125
126// ---------------------------------------------------------------------------
127// commands
128// ---------------------------------------------------------------------------
129
130/// Terminal result of `commands.run` (also the `/exec` fast-path response).
131#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
132#[serde(rename_all = "camelCase")]
133pub struct CommandResult {
134    #[serde(default)]
135    pub exit_code: i64,
136    #[serde(default)]
137    pub stdout: String,
138    #[serde(default)]
139    pub stderr: String,
140}
141
142// ---------------------------------------------------------------------------
143// files
144// ---------------------------------------------------------------------------
145
146/// One directory entry (`fs.list`). Matches the reference `types.ts` FsEntry:
147/// the wire field is `dir` (bool), NOT `isDir`; there is no path/mode/modTime.
148#[derive(Debug, Clone, Deserialize)]
149pub struct FsEntry {
150    #[serde(default)]
151    pub name: String,
152    #[serde(default, rename = "dir")]
153    pub dir: bool,
154    #[serde(default)]
155    pub size: i64,
156}
157
158/// File metadata (`fs.stat`). Matches the reference `types.ts` FsStat: fields
159/// `dir` (bool), `mode` (int), and `modTimeMs` (unix-millis).
160#[derive(Debug, Clone, Deserialize)]
161pub struct FsStat {
162    #[serde(default)]
163    pub name: String,
164    #[serde(default, rename = "dir")]
165    pub dir: bool,
166    #[serde(default)]
167    pub size: i64,
168    #[serde(default)]
169    pub mode: i64,
170    #[serde(default, rename = "modTimeMs")]
171    pub mod_time_ms: i64,
172}
173
174// ---------------------------------------------------------------------------
175// code.run
176// ---------------------------------------------------------------------------
177
178/// Kind of a structured chart extracted from a matplotlib figure.
179#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
180#[serde(rename_all = "snake_case")]
181pub enum ChartType {
182    Line,
183    Scatter,
184    Bar,
185    Pie,
186    BoxAndWhisker,
187    Composite,
188    #[serde(other)]
189    Unknown,
190}
191
192impl Default for ChartType {
193    fn default() -> Self {
194        ChartType::Unknown
195    }
196}
197
198/// One axis of a 2D chart.
199#[derive(Debug, Clone, Default, Serialize, Deserialize)]
200pub struct ChartAxis {
201    #[serde(default, skip_serializing_if = "Option::is_none")]
202    pub label: Option<String>,
203    #[serde(default, skip_serializing_if = "Option::is_none")]
204    pub ticks: Option<Vec<Value>>,
205    #[serde(default, skip_serializing_if = "Option::is_none")]
206    pub scale: Option<String>,
207}
208
209/// Structured representation of a matplotlib figure. `elements` is kept as raw
210/// JSON so new chart types don't require an SDK bump.
211#[derive(Debug, Clone, Default, Serialize, Deserialize)]
212#[serde(rename_all = "camelCase")]
213pub struct Chart {
214    #[serde(default, rename = "type")]
215    pub chart_type: ChartType,
216    #[serde(default, skip_serializing_if = "Option::is_none")]
217    pub title: Option<String>,
218    #[serde(default, skip_serializing_if = "Option::is_none")]
219    pub x_label: Option<String>,
220    #[serde(default, skip_serializing_if = "Option::is_none")]
221    pub y_label: Option<String>,
222    #[serde(default, skip_serializing_if = "Option::is_none")]
223    pub x: Option<ChartAxis>,
224    #[serde(default, skip_serializing_if = "Option::is_none")]
225    pub y: Option<ChartAxis>,
226    /// Loose per-type data (points/bars/slices). Raw decoded JSON.
227    #[serde(default, skip_serializing_if = "Option::is_none")]
228    pub elements: Option<Value>,
229}
230
231/// One rich result object from `code.run`.
232#[derive(Debug, Clone, Default, Deserialize)]
233pub struct CodeResultItem {
234    #[serde(default, rename = "type")]
235    pub result_type: String,
236    #[serde(default)]
237    pub text: Option<String>,
238    #[serde(default)]
239    pub png: Option<String>,
240    #[serde(default)]
241    pub jpeg: Option<String>,
242    #[serde(default)]
243    pub svg: Option<String>,
244    #[serde(default)]
245    pub html: Option<String>,
246    #[serde(default)]
247    pub latex: Option<String>,
248    #[serde(default)]
249    pub json: Option<Value>,
250    #[serde(default)]
251    pub markdown: Option<String>,
252    #[serde(default)]
253    pub chart: Option<Chart>,
254}
255
256/// Result of `code.run`, with the client-side `charts` convenience view.
257#[derive(Debug, Clone, Default)]
258pub struct RunCodeResult {
259    pub results: Vec<CodeResultItem>,
260    pub error: Option<Value>,
261    /// All structured charts across `results` (flattened convenience view).
262    pub charts: Vec<Chart>,
263}
264
265// ---------------------------------------------------------------------------
266// git
267// ---------------------------------------------------------------------------
268
269/// Working-tree status (`git.status`).
270#[derive(Debug, Clone, Default, PartialEq, Eq)]
271pub struct GitStatus {
272    pub branch: String,
273    pub detached: bool,
274    pub ahead: i64,
275    pub behind: i64,
276    pub staged: Vec<String>,
277    pub modified: Vec<String>,
278    pub untracked: Vec<String>,
279    pub clean: bool,
280}
281
282/// One branch entry (`git.branches`).
283#[derive(Debug, Clone, Default, PartialEq, Eq)]
284pub struct GitBranch {
285    pub name: String,
286    pub commit: String,
287    pub current: bool,
288}
289
290/// One commit record (`git.log`).
291#[derive(Debug, Clone, Default, PartialEq, Eq)]
292pub struct GitCommit {
293    pub hash: String,
294    pub author: String,
295    pub email: String,
296    pub date: String,
297    pub message: String,
298}