1use anyhow::Result;
4use async_trait::async_trait;
5use bytes::Bytes;
6use futures_util::Stream;
7use serde::{Deserialize, Serialize};
8use std::collections::{BTreeMap, BTreeSet};
9use std::fmt::Debug;
10use std::pin::Pin;
11use std::sync::Arc;
12
13mod base64_bytes {
14 use base64::{Engine as _, engine::general_purpose::STANDARD};
15 use serde::{Deserialize, Deserializer, Serializer};
16
17 pub fn serialize<S>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error>
18 where
19 S: Serializer,
20 {
21 serializer.serialize_str(&STANDARD.encode(value))
22 }
23
24 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
25 where
26 D: Deserializer<'de>,
27 {
28 let value = String::deserialize(deserializer)?;
29 STANDARD.decode(value).map_err(serde::de::Error::custom)
30 }
31}
32
33mod catalog;
34pub use catalog::*;
35
36mod ports;
37pub use ports::*;
38
39mod owners;
40pub use owners::*;
41
42mod command_context;
43pub use command_context::*;
44
45mod application_execution;
46pub use application_execution::*;
47
48mod commands;
49pub use commands::*;
50
51mod resources;
52pub use resources::*;
53
54mod execution_operation;
55pub use execution_operation::*;
56
57mod skills;
58pub use skills::*;
59
60mod managed_workloads;
61pub use managed_workloads::*;
62
63pub const COMPUTERS_PATH: &str = "/internal/v1/workspace/computers";
64pub const ADMIN_COMPUTERS_PATH: &str = "/internal/v1/workspace/admin/computers";
67pub const COMPUTER_PATH: &str = "/internal/v1/workspace/computers/{computerId}";
68pub const SPACES_PATH: &str = "/internal/v1/workspace/spaces";
69pub const ADMIN_SPACES_PATH: &str = "/internal/v1/workspace/admin/spaces";
70pub const SPACE_PATH: &str = "/internal/v1/workspace/spaces/{spaceId}";
71pub const SPACE_GRANTS_PATH: &str = "/internal/v1/workspace/spaces/{spaceId}/grants";
72pub const SPACE_GRANT_REVOKE_PATH: &str =
73 "/internal/v1/workspace/spaces/{spaceId}/grants/{grantId}/revoke";
74pub const SPACE_SESSIONS_PATH: &str = "/internal/v1/workspace/spaces/{spaceId}/sessions";
75pub const SPACE_SESSION_HEARTBEAT_PATH: &str =
76 "/internal/v1/workspace/space-sessions/{sessionId}/heartbeat";
77pub const SPACE_SESSION_CLOSE_PATH: &str =
78 "/internal/v1/workspace/space-sessions/{sessionId}/close";
79pub const SPACE_TICKETS_PATH: &str = "/internal/v1/workspace/space-access-tickets";
80pub const SPACE_LEASES_PATH: &str = "/internal/v1/workspace/spaces/{spaceId}/leases";
81pub const SPACE_LEASE_PATH: &str = "/internal/v1/workspace/spaces/{spaceId}/leases/{leaseId}";
82pub const SPACE_FILES_PATH: &str = "/internal/v1/workspace/spaces/{spaceId}/files/{*filePath}";
83pub const SPACE_FILES_READ_PATH: &str = "/internal/v1/workspace/spaces/{spaceId}/files/read";
84pub const SPACE_FILES_DOWNLOAD_PATH: &str =
85 "/internal/v1/workspace/spaces/{spaceId}/files/download";
86pub const SPACE_FILES_LIST_PATH: &str = "/internal/v1/workspace/spaces/{spaceId}/files/list";
87pub const SPACE_SOURCE_ARCHIVE_PATH: &str =
88 "/internal/v1/workspace/spaces/{spaceId}/source-archive";
89pub const SPACE_CHANGE_SETS_PATH: &str = "/internal/v1/workspace/spaces/{spaceId}/change-sets";
90pub const SPACE_CHANGE_SET_APPLY_PATH: &str =
91 "/internal/v1/workspace/spaces/{spaceId}/change-sets/{changeSetId}/apply";
92pub const SANDBOXES_PATH: &str = "/internal/v1/workspace/sandboxes";
93pub const SANDBOX_PATH: &str = "/internal/v1/workspace/sandboxes/{sandboxId}";
94pub const SANDBOX_STOP_PATH: &str = "/internal/v1/workspace/sandboxes/{sandboxId}/stop";
95pub const COMPUTER_SNAPSHOTS_PATH: &str = "/internal/v1/workspace/computers/{computerId}/snapshots";
96pub const SNAPSHOT_ARTIFACT_PATH: &str = "/internal/v1/workspace/snapshots/{snapshotId}/artifact";
97pub const SNAPSHOTS_PATH: &str = "/internal/v1/workspace/snapshots";
98pub const SNAPSHOT_PATH: &str = "/internal/v1/workspace/snapshots/{snapshotId}";
99pub const BUILD_BUILTIN_SNAPSHOTS_PATH: &str = "/internal/v1/workspace/snapshots:build-builtin";
100pub const CATALOG_SNAPSHOTS_PATH: &str = "/internal/v1/workspace/snapshots:catalog";
101pub const TEMPLATES_PATH: &str = "/internal/v1/workspace/templates";
102pub const TEMPLATE_VERSIONS_PATH: &str = "/internal/v1/workspace/templates/{templateId}/versions";
103pub const TEMPLATE_VERSION_PATH: &str =
104 "/internal/v1/workspace/templates/{templateId}/versions/{version}";
105pub const TEMPLATE_VERSION_PUBLISH_PATH: &str =
106 "/internal/v1/workspace/templates/{templateId}/versions/{version}/publish";
107pub const TEMPLATE_VERSION_DEPRECATE_PATH: &str =
108 "/internal/v1/workspace/templates/{templateId}/versions/{version}/deprecate";
109pub const DELEGATIONS_PATH: &str = "/internal/v1/workspace/delegations";
110pub const DELEGATION_REVOKE_PATH: &str = "/internal/v1/workspace/delegations/{delegationId}/revoke";
111pub const ENVIRONMENT_MAX_FILE_BYTES: usize = 8 * 1024 * 1024;
112pub const ENVIRONMENT_MAX_PATH_BYTES: usize = 4096;
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
117#[serde(rename_all = "camelCase")]
118pub struct SandboxResources {
119 cpu_count: u32,
120 memory_mb: u32,
121 disk_mb: u32,
122}
123
124impl SandboxResources {
125 pub fn new(cpu_count: u32, memory_mb: u32, disk_mb: u32) -> std::result::Result<Self, String> {
126 if !(1..=64).contains(&cpu_count) {
127 return Err("cpuCount must be between 1 and 64".into());
128 }
129 if !(128..=131_072).contains(&memory_mb) {
130 return Err("memoryMb must be between 128 and 131072".into());
131 }
132 if !(512..=1_048_576).contains(&disk_mb) {
133 return Err("diskMb must be between 512 and 1048576".into());
134 }
135 Ok(Self {
136 cpu_count,
137 memory_mb,
138 disk_mb,
139 })
140 }
141
142 pub const fn cpu_count(self) -> u32 {
143 self.cpu_count
144 }
145
146 pub const fn memory_mb(self) -> u32 {
147 self.memory_mb
148 }
149
150 pub const fn disk_mb(self) -> u32 {
151 self.disk_mb
152 }
153}
154
155impl Default for SandboxResources {
156 fn default() -> Self {
157 Self::new(1, 1_024, 2_048).expect("system resource defaults are valid")
158 }
159}
160
161impl<'de> Deserialize<'de> for SandboxResources {
162 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
163 where
164 D: serde::Deserializer<'de>,
165 {
166 #[derive(Deserialize)]
167 #[serde(rename_all = "camelCase", deny_unknown_fields)]
168 struct WireResources {
169 cpu_count: u32,
170 memory_mb: u32,
171 disk_mb: u32,
172 }
173
174 let value = WireResources::deserialize(deserializer)?;
175 Self::new(value.cpu_count, value.memory_mb, value.disk_mb).map_err(serde::de::Error::custom)
176 }
177}
178
179#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
180#[serde(deny_unknown_fields, rename_all = "camelCase")]
181pub struct ExportSpaceSourceArchiveQuery {
182 #[serde(default, skip_serializing_if = "Option::is_none")]
183 pub expected_revision: Option<u64>,
184}
185
186pub fn space_directory_path(space_id: &str) -> String {
188 format!("spaces/{space_id}")
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
192#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
193pub enum EnvironmentErrorCode {
194 NotFound,
195 Forbidden,
196 Conflict,
197 InvalidState,
198 ArtifactUnavailable,
199 CapabilityUnsupported,
200 TicketExpired,
201 LeaseRequired,
202 StaleFencingToken,
203 RevisionConflict,
204}
205
206#[derive(Debug, Clone)]
207pub struct EnvironmentDomainError {
208 pub code: EnvironmentErrorCode,
209 pub message: String,
210}
211
212impl EnvironmentDomainError {
213 pub fn new(code: EnvironmentErrorCode, message: impl Into<String>) -> Self {
214 Self {
215 code,
216 message: message.into(),
217 }
218 }
219}
220
221impl std::fmt::Display for EnvironmentDomainError {
222 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223 formatter.write_str(&self.message)
224 }
225}
226
227impl std::error::Error for EnvironmentDomainError {}
228
229#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
230#[serde(rename_all = "snake_case")]
231pub enum PrincipalKind {
232 User,
233 Agent,
234}
235
236#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
237#[serde(deny_unknown_fields, rename_all = "camelCase")]
238pub struct PrincipalRef {
239 pub kind: PrincipalKind,
240 pub id: String,
241}
242
243impl PrincipalRef {
244 pub fn user(id: impl Into<String>) -> Self {
245 Self {
246 kind: PrincipalKind::User,
247 id: id.into(),
248 }
249 }
250
251 pub fn agent(id: impl Into<String>) -> Self {
252 Self {
253 kind: PrincipalKind::Agent,
254 id: id.into(),
255 }
256 }
257}
258
259#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
260#[serde(deny_unknown_fields, rename_all = "camelCase")]
261pub struct ResourceScope {
262 pub tenant_id: String,
263 pub project_id: String,
264}
265
266#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
267#[serde(deny_unknown_fields, rename_all = "camelCase")]
268pub struct ProviderResourceRef {
269 pub provider: String,
270 pub external_id: String,
271}
272
273#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
274#[serde(deny_unknown_fields, rename_all = "camelCase")]
275pub struct ProviderCapabilities {
276 #[serde(default)]
277 pub capabilities: BTreeSet<String>,
278}
279
280impl ProviderCapabilities {
281 pub fn supports(&self, capability: &str) -> bool {
282 self.capabilities.contains(capability)
283 }
284}
285
286#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
287#[serde(rename_all = "snake_case")]
288pub enum ComputerLifecycle {
289 Ready,
290 Deleted,
291}
292
293#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
294#[serde(deny_unknown_fields, rename_all = "camelCase")]
295pub struct ComputerRecord {
296 pub id: String,
297 #[serde(default, skip_serializing_if = "Option::is_none")]
298 pub alias: Option<String>,
299 pub scope: ResourceScope,
300 pub provider_ref: ProviderResourceRef,
301 #[serde(default)]
304 pub resources: SandboxResources,
305 pub lifecycle: ComputerLifecycle,
306 pub capabilities: ProviderCapabilities,
307 pub owners: Vec<OwnerRef>,
308 pub version: u64,
309 pub created_at_ms: u64,
310 pub updated_at_ms: u64,
311}
312
313#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
314#[serde(deny_unknown_fields, rename_all = "camelCase")]
315pub struct ComputerBinding {
316 pub computer_id: String,
317 pub principal: PrincipalRef,
318 pub role: String,
319 pub active: bool,
320 pub bound_at_ms: u64,
321 #[serde(default, skip_serializing_if = "Option::is_none")]
322 pub released_at_ms: Option<u64>,
323 pub version: u64,
324}
325
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ts_rs::TS)]
327#[serde(rename_all = "snake_case")]
328pub enum SpacePersistence {
329 Persistent,
330 CopyOnWrite,
331}
332
333#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
334#[serde(rename_all = "snake_case")]
335pub enum SpaceLifecycle {
336 Ready,
337 Deleted,
338}
339
340#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
341#[serde(deny_unknown_fields, rename_all = "camelCase")]
342pub struct SpaceRecord {
343 pub id: String,
344 #[serde(default, skip_serializing_if = "Option::is_none")]
345 pub alias: Option<String>,
346 pub computer_id: String,
347 pub scope: ResourceScope,
348 pub persistence: SpacePersistence,
349 pub lifecycle: SpaceLifecycle,
350 pub capabilities: ProviderCapabilities,
351 pub owners: Vec<OwnerRef>,
352 #[serde(default, skip_serializing_if = "Option::is_none")]
353 pub source_snapshot_id: Option<String>,
354 pub revision: u64,
355 pub version: u64,
356 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
358 pub installed_skills: BTreeMap<String, SpaceSkillInstallation>,
359 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
361 pub skill_env: BTreeMap<String, String>,
362 pub created_at_ms: u64,
363 pub updated_at_ms: u64,
364}
365
366#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
367#[serde(rename_all = "snake_case")]
368pub enum SandboxPlacement {
369 Computer(String),
370 ProviderPool(String),
371}
372
373#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
374#[serde(rename_all = "snake_case")]
375pub enum SandboxLifecycle {
376 Ready,
377 Stopped,
378 Expired,
379}
380
381#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
382#[serde(deny_unknown_fields, rename_all = "camelCase")]
383pub struct SandboxMount {
384 pub alias: String,
385 pub space_id: String,
386 pub mode: SpaceAccessMode,
387 #[serde(default, skip_serializing_if = "Option::is_none")]
388 pub path_prefix: Option<String>,
389 pub access_ticket_token: String,
390}
391
392#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
395#[serde(deny_unknown_fields, rename_all = "camelCase")]
396pub struct SandboxMountRecord {
397 pub alias: String,
398 pub space_id: String,
399 pub mode: SpaceAccessMode,
400 #[serde(default, skip_serializing_if = "Option::is_none")]
401 pub path_prefix: Option<String>,
402 pub access_ticket_id: String,
403}
404
405#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
406#[serde(deny_unknown_fields, rename_all = "camelCase")]
407pub struct SandboxRecord {
408 pub id: String,
409 #[serde(default, skip_serializing_if = "Option::is_none")]
410 pub alias: Option<String>,
411 pub scope: ResourceScope,
412 pub provider_ref: ProviderResourceRef,
413 #[serde(default)]
416 pub resources: SandboxResources,
417 pub placement: SandboxPlacement,
418 pub lifecycle: SandboxLifecycle,
419 pub owners: Vec<OwnerRef>,
420 #[serde(default, skip_serializing_if = "Option::is_none")]
421 pub access_delegation_id: Option<String>,
422 pub capabilities: ProviderCapabilities,
423 #[serde(default)]
424 pub mounts: Vec<SandboxMountRecord>,
425 pub expires_at_ms: u64,
426 pub version: u64,
427 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
429 pub skill_env: BTreeMap<String, String>,
430 pub created_at_ms: u64,
431 pub updated_at_ms: u64,
432}
433
434#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
435#[serde(rename_all = "snake_case")]
436pub enum SnapshotLifecycle {
437 Building,
438 Ready,
439 Failed,
440 Deleted,
441}
442
443#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
444#[serde(tag = "kind", rename_all = "snake_case")]
445pub enum SnapshotSourceRecord {
446 Build { spec: SnapshotBuildSpec },
447 Computer { computer_id: String },
448}
449
450#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
451#[serde(deny_unknown_fields, rename_all = "camelCase")]
452pub struct SnapshotRecord {
453 pub id: String,
454 #[serde(default, skip_serializing_if = "Option::is_none")]
455 pub alias: Option<String>,
456 pub scope: ResourceScope,
457 pub provider: String,
458 pub provider_ref: ProviderResourceRef,
459 pub source: SnapshotSourceRecord,
460 pub visibility: SnapshotVisibility,
461 pub owners: Vec<OwnerRef>,
462 #[serde(default)]
463 pub builtin: bool,
464 pub created_by: PrincipalRef,
465 pub lifecycle: SnapshotLifecycle,
466 #[serde(default, skip_serializing_if = "Option::is_none")]
467 pub digest: Option<String>,
468 #[serde(default, skip_serializing_if = "Option::is_none")]
469 pub size_bytes: Option<u64>,
470 #[serde(default, skip_serializing_if = "Option::is_none")]
471 pub error: Option<String>,
472 #[serde(default, skip_serializing_if = "Option::is_none")]
473 pub provider_build_id: Option<String>,
474 #[serde(default, skip_serializing_if = "Option::is_none")]
475 pub build_stage: Option<String>,
476 #[serde(default, skip_serializing_if = "Vec::is_empty")]
477 pub build_logs: Vec<String>,
478 #[serde(default, skip_serializing_if = "String::is_empty")]
479 pub build_key: String,
480 pub created_at_ms: u64,
481 pub updated_at_ms: u64,
482}
483
484#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
485#[serde(rename_all = "snake_case")]
486pub enum TemplateTarget {
487 Computer,
488 Sandbox,
489}
490
491#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
492#[serde(rename_all = "snake_case")]
493pub enum TemplateVersionState {
494 Draft,
495 Published,
496 Deprecated,
497}
498
499#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
500#[serde(deny_unknown_fields, rename_all = "camelCase")]
501pub struct TemplateVersionRecord {
502 pub template_id: String,
503 pub version: String,
504 pub scope: ResourceScope,
505 pub provider: String,
506 pub created_by: PrincipalRef,
507 #[serde(default, skip_serializing_if = "Option::is_none")]
508 pub provider_template_id: Option<String>,
509 pub target: TemplateTarget,
510 #[serde(default, skip_serializing_if = "Option::is_none")]
511 pub base_snapshot_id: Option<String>,
512 #[serde(default)]
513 pub resource_profile: BTreeMap<String, String>,
514 #[serde(default)]
515 pub parameter_schema: BTreeMap<String, String>,
516 pub state: TemplateVersionState,
517 pub created_at_ms: u64,
518}
519
520#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
521#[serde(rename_all = "snake_case")]
522pub enum SpaceCapability {
523 Read,
524 Execute,
525 Write,
526 Share,
527 Manage,
528 Mount,
529 Snapshot,
530 Restore,
531 Delete,
532}
533
534#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
535#[serde(deny_unknown_fields, rename_all = "camelCase")]
536pub struct SpaceGrant {
537 pub id: String,
538 pub scope: ResourceScope,
539 pub space_id: String,
540 pub principal: PrincipalRef,
541 pub capabilities: BTreeSet<SpaceCapability>,
542 #[serde(default)]
543 pub path_prefixes: Vec<String>,
544 #[serde(default, skip_serializing_if = "Option::is_none")]
545 pub expires_at_ms: Option<u64>,
546 pub active: bool,
547 pub version: u64,
548}
549
550#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ts_rs::TS)]
551#[serde(rename_all = "snake_case")]
552pub enum SpaceAccessMode {
553 ReadOnly,
554 DirectWrite,
555 CopyOnWrite,
556}
557
558#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
559#[serde(rename_all = "snake_case")]
560pub enum SpaceSessionLifecycle {
561 Opening,
562 Active,
563 Closing,
564 Closed,
565 Expired,
566 Revoked,
567}
568
569#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
570#[serde(deny_unknown_fields, rename_all = "camelCase")]
571pub struct SpaceCollaborationSession {
572 pub id: String,
573 pub scope: ResourceScope,
574 pub space_id: String,
575 pub principal: PrincipalRef,
576 #[serde(default, skip_serializing_if = "Option::is_none")]
577 pub delegation_id: Option<String>,
578 pub requested_mode: SpaceAccessMode,
579 pub effective_mode: SpaceAccessMode,
580 pub lifecycle: SpaceSessionLifecycle,
581 pub last_heartbeat_at_ms: u64,
582 pub expires_at_ms: u64,
583 pub version: u64,
584}
585
586#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
587#[serde(deny_unknown_fields, rename_all = "camelCase")]
588pub struct SpaceAccessTicketRecord {
589 pub id: String,
590 pub token_hash: String,
591 pub scope: ResourceScope,
592 pub space_id: String,
593 pub session_id: String,
594 pub mode: SpaceAccessMode,
595 #[serde(default)]
598 pub capability_paths: BTreeMap<SpaceCapability, Vec<String>>,
599 #[serde(default, skip_serializing_if = "Option::is_none")]
600 pub fencing_token: Option<u64>,
601 pub expires_at_ms: u64,
602 pub revoked: bool,
603}
604
605#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
606#[serde(deny_unknown_fields, rename_all = "camelCase")]
607pub struct IssuedSpaceAccessTicket {
608 pub ticket: SpaceAccessTicketRecord,
609 pub token: String,
612}
613
614#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
615#[serde(deny_unknown_fields, rename_all = "camelCase")]
616pub struct SpaceWriteLease {
617 pub id: String,
618 pub scope: ResourceScope,
619 pub space_id: String,
620 pub session_id: String,
621 pub fencing_token: u64,
622 pub version: u64,
623 pub acquired_at_ms: u64,
624 pub expires_at_ms: u64,
625 pub active: bool,
626}
627
628#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
629#[serde(deny_unknown_fields, rename_all = "camelCase")]
630pub struct DelegationRecord {
631 pub id: String,
632 pub scope: ResourceScope,
633 pub grantor_user_id: String,
634 pub delegate_agent_id: String,
635 #[serde(default)]
636 pub computer_ids: BTreeSet<String>,
637 #[serde(default)]
638 pub space_ids: BTreeSet<String>,
639 pub capabilities: BTreeSet<SpaceCapability>,
640 #[serde(default)]
641 pub path_prefixes: Vec<String>,
642 pub purpose: String,
643 pub expires_at_ms: u64,
644 #[serde(default, skip_serializing_if = "Option::is_none")]
645 pub revoked_at_ms: Option<u64>,
646}
647
648#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
649#[serde(rename_all = "snake_case")]
650pub enum ChangeSetLifecycle {
651 Ready,
652 Applied,
653}
654
655#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
656#[serde(deny_unknown_fields, rename_all = "camelCase")]
657pub struct SpaceChangeSetRecord {
658 pub id: String,
659 pub scope: ResourceScope,
660 pub source_space_id: String,
661 pub target_space_id: String,
662 pub base_revision: u64,
663 pub created_by_session_id: String,
664 #[serde(default, skip_serializing_if = "Option::is_none")]
665 pub provider_change_ref: Option<String>,
666 pub lifecycle: ChangeSetLifecycle,
667 pub created_at_ms: u64,
668}
669
670pub fn default_workspace_provider() -> String {
672 "e2b".into()
673}
674
675#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
676#[serde(deny_unknown_fields, rename_all = "camelCase")]
677pub struct CreateComputerRequest {
678 #[serde(default, skip_serializing_if = "Option::is_none")]
679 pub alias: Option<String>,
680 #[serde(default = "default_workspace_provider")]
681 pub provider: String,
682 #[serde(default, skip_serializing_if = "Option::is_none")]
683 pub snapshot_id: Option<String>,
684 #[serde(default, skip_serializing_if = "Option::is_none")]
685 pub template_id: Option<String>,
686 #[serde(default, skip_serializing_if = "Option::is_none")]
687 pub template_version: Option<String>,
688 #[serde(default)]
689 pub profile: BTreeMap<String, String>,
690 #[serde(default, skip_serializing_if = "Option::is_none")]
692 pub resources: Option<SandboxResources>,
693 #[serde(default, skip_serializing_if = "Option::is_none")]
696 pub egress_rules: Option<Vec<crate::MoulinEgressRule>>,
697 #[serde(default)]
699 pub owners: Vec<OwnerRef>,
700}
701
702#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
703#[serde(deny_unknown_fields, rename_all = "camelCase")]
704pub struct CreateSpaceRequest {
705 #[serde(default, skip_serializing_if = "Option::is_none")]
706 pub alias: Option<String>,
707 pub computer_id: String,
708 pub persistence: SpacePersistence,
709 #[serde(default, skip_serializing_if = "Option::is_none")]
710 pub source_snapshot_id: Option<String>,
711 #[serde(default)]
713 pub owners: Vec<OwnerRef>,
714}
715
716#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
718#[serde(deny_unknown_fields, rename_all = "camelCase")]
719pub struct PatchResourceAliasRequest {
720 pub alias: String,
721}
722
723#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
724#[serde(deny_unknown_fields, rename_all = "camelCase")]
725pub struct CreateSandboxRequest {
726 #[serde(default, skip_serializing_if = "Option::is_none")]
727 pub alias: Option<String>,
728 #[serde(default = "default_workspace_provider")]
729 pub provider: String,
730 pub placement: SandboxPlacement,
731 #[serde(default, skip_serializing_if = "Option::is_none")]
732 pub delegation_id: Option<String>,
733 pub ttl_ms: u64,
734 #[serde(default)]
735 pub mounts: Vec<SandboxMount>,
736 #[serde(default, skip_serializing_if = "Option::is_none")]
737 pub snapshot_id: Option<String>,
738 #[serde(default, skip_serializing_if = "Option::is_none")]
739 pub template_id: Option<String>,
740 #[serde(default, skip_serializing_if = "Option::is_none")]
741 pub template_version: Option<String>,
742 #[serde(default)]
743 pub profile: BTreeMap<String, String>,
744 #[serde(default, skip_serializing_if = "Option::is_none")]
746 pub resources: Option<SandboxResources>,
747 #[serde(default, skip_serializing_if = "Option::is_none")]
750 pub egress_rules: Option<Vec<crate::MoulinEgressRule>>,
751}
752
753#[derive(Debug, Clone, PartialEq, Eq)]
756pub struct ProviderComputerRequest {
757 pub template_external_id: Option<String>,
758 pub profile: BTreeMap<String, String>,
759 pub resources: SandboxResources,
760 pub egress_rules: Option<Vec<crate::MoulinEgressRule>>,
761}
762
763#[derive(Debug, Clone, PartialEq, Eq)]
764pub struct ProviderSpaceRequest {
765 pub persistence: SpacePersistence,
766 pub source_snapshot_external_id: Option<String>,
767}
768
769#[derive(Debug, Clone, PartialEq, Eq)]
770pub struct ProviderSandboxMount {
771 pub alias: String,
772 pub space_directory: String,
773 pub mode: SpaceAccessMode,
774 pub path_prefix: Option<String>,
775}
776
777#[derive(Debug, Clone, PartialEq, Eq)]
778pub enum ProviderSandboxPlacement {
779 Computer(String),
780 ProviderPool(String),
781}
782
783#[derive(Debug, Clone, PartialEq, Eq)]
784pub struct ProviderSandboxRequest {
785 pub placement: ProviderSandboxPlacement,
786 pub ttl_ms: u64,
787 pub mounts: Vec<ProviderSandboxMount>,
788 pub template_external_id: Option<String>,
789 pub source_snapshot_external_id: Option<String>,
790 pub profile: BTreeMap<String, String>,
791 pub resources: SandboxResources,
792 pub egress_rules: Option<Vec<crate::MoulinEgressRule>>,
793}
794
795#[derive(Debug, Clone, PartialEq, Eq)]
796pub enum ProviderSnapshotSource {
797 Computer(String),
798 Sandbox(String),
799 Space(String),
800}
801
802#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
803#[serde(deny_unknown_fields, rename_all = "camelCase")]
804pub struct ListEnvironmentResourcesQuery {
805 #[serde(default, skip_serializing_if = "Option::is_none")]
806 pub owner_kind: Option<OwnerKind>,
807 #[serde(default, skip_serializing_if = "Option::is_none")]
808 pub owner_id: Option<String>,
809 #[serde(default, skip_serializing_if = "Vec::is_empty")]
811 pub contains_all: Vec<OwnerRef>,
812}
813
814#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
815#[serde(deny_unknown_fields, rename_all = "camelCase")]
816pub struct RevokeSpaceGrantRequest {
817 pub expected_version: u64,
818}
819
820#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
821#[serde(deny_unknown_fields, rename_all = "camelCase")]
822pub struct CreateSpaceGrantRequest {
823 pub principal: PrincipalRef,
824 pub capabilities: BTreeSet<SpaceCapability>,
825 #[serde(default)]
826 pub path_prefixes: Vec<String>,
827 #[serde(default, skip_serializing_if = "Option::is_none")]
828 pub expires_at_ms: Option<u64>,
829}
830
831#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
832#[serde(deny_unknown_fields, rename_all = "camelCase")]
833pub struct OpenSpaceSessionRequest {
834 pub mode: SpaceAccessMode,
835 #[serde(default, skip_serializing_if = "Option::is_none")]
836 pub delegation_id: Option<String>,
837 pub ttl_ms: u64,
838}
839
840#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
841#[serde(deny_unknown_fields, rename_all = "camelCase")]
842pub struct IssueSpaceAccessTicketRequest {
843 pub session_id: String,
844 pub ttl_ms: u64,
845}
846
847#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
848#[serde(deny_unknown_fields, rename_all = "camelCase")]
849pub struct AcquireSpaceWriteLeaseRequest {
850 pub session_id: String,
851 pub ttl_ms: u64,
852}
853
854#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
855#[serde(deny_unknown_fields, rename_all = "camelCase")]
856pub struct ReleaseSpaceWriteLeaseRequest {
857 pub session_id: String,
858 pub fencing_token: u64,
859}
860
861#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
862#[serde(deny_unknown_fields, rename_all = "camelCase")]
863pub struct HeartbeatSpaceSessionRequest {
864 pub ttl_ms: u64,
865}
866
867#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
868#[serde(deny_unknown_fields, rename_all = "camelCase")]
869pub struct CreateDelegationRequest {
870 pub delegate_agent_id: String,
871 #[serde(default)]
872 pub computer_ids: BTreeSet<String>,
873 #[serde(default)]
874 pub space_ids: BTreeSet<String>,
875 pub capabilities: BTreeSet<SpaceCapability>,
876 #[serde(default)]
877 pub path_prefixes: Vec<String>,
878 pub purpose: String,
879 pub expires_at_ms: u64,
880}
881
882#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
883#[serde(deny_unknown_fields, rename_all = "camelCase")]
884pub struct CreateTemplateVersionRequest {
885 pub template_id: String,
886 pub version: String,
887 pub provider: String,
888 #[serde(default, skip_serializing_if = "Option::is_none")]
889 pub provider_template_id: Option<String>,
890 pub target: TemplateTarget,
891 #[serde(default, skip_serializing_if = "Option::is_none")]
892 pub base_snapshot_id: Option<String>,
893 #[serde(default)]
894 pub resource_profile: BTreeMap<String, String>,
895 #[serde(default)]
896 pub parameter_schema: BTreeMap<String, String>,
897}
898
899#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
900#[serde(deny_unknown_fields, rename_all = "camelCase")]
901pub struct CreateSpaceChangeSetRequest {
902 pub source_space_id: String,
903 pub target_space_id: String,
904 pub created_by_session_id: String,
905}
906
907#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
908#[serde(deny_unknown_fields, rename_all = "camelCase")]
909pub struct ApplySpaceChangeSetRequest {
910 pub access_ticket_token: String,
911 pub fencing_token: u64,
912 pub expected_revision: u64,
913}
914
915#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
916#[serde(deny_unknown_fields, rename_all = "camelCase")]
917pub struct WriteSpaceFileRequest {
918 pub access_ticket_token: String,
919 pub fencing_token: u64,
920 pub expected_revision: u64,
921 #[serde(with = "base64_bytes")]
922 pub content: Vec<u8>,
923}
924
925#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
926#[serde(deny_unknown_fields, rename_all = "camelCase")]
927pub struct ReadSpaceFileRequest {
928 pub access_ticket_token: String,
929 pub path: String,
930}
931
932#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
934pub enum WorkspaceArchiveFormat {
935 #[default]
936 #[serde(rename = "zip")]
937 Zip,
938 #[serde(rename = "tar.gz")]
939 TarGz,
940 #[serde(rename = "tar")]
941 Tar,
942}
943
944#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
945#[serde(deny_unknown_fields, rename_all = "camelCase")]
946pub struct DownloadSpaceFileRequest {
947 pub access_ticket_token: String,
948 pub path: String,
949 #[serde(default)]
950 pub archive_format: WorkspaceArchiveFormat,
951}
952
953#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
954#[serde(deny_unknown_fields, rename_all = "camelCase")]
955pub struct ListSpaceFilesRequest {
956 pub access_ticket_token: String,
957 #[serde(default)]
958 pub path: String,
959}
960
961#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
962#[serde(deny_unknown_fields, rename_all = "camelCase")]
963pub struct DeleteSpaceFileRequest {
964 pub access_ticket_token: String,
965 pub fencing_token: u64,
966 pub expected_revision: u64,
967}
968
969#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
970#[serde(deny_unknown_fields, rename_all = "camelCase")]
971pub struct ProviderFileContent {
972 pub bytes: Vec<u8>,
973 #[serde(default, skip_serializing_if = "Option::is_none")]
974 pub provider_revision: Option<String>,
975}
976
977pub type ProviderByteStream = Pin<Box<dyn Stream<Item = Result<Bytes>> + Send + 'static>>;
978
979pub struct ProviderSourceArchive {
980 pub stream: ProviderByteStream,
981 pub digest: String,
982 pub size_bytes: u64,
983}
984
985impl Debug for ProviderSourceArchive {
986 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
987 formatter
988 .debug_struct("ProviderSourceArchive")
989 .field("digest", &self.digest)
990 .field("size_bytes", &self.size_bytes)
991 .finish_non_exhaustive()
992 }
993}
994
995#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
996#[serde(deny_unknown_fields, rename_all = "camelCase")]
997pub struct ProviderFileEntry {
998 pub path: String,
999 pub is_dir: bool,
1000 #[serde(default, skip_serializing_if = "Option::is_none")]
1001 pub size_bytes: Option<u64>,
1002}
1003
1004#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1005#[serde(deny_unknown_fields, rename_all = "camelCase")]
1006pub struct ProviderWriteResult {
1007 #[serde(default, skip_serializing_if = "Option::is_none")]
1008 pub provider_revision: Option<String>,
1009}