use std::collections::HashMap;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use crate::client::Client;
use crate::error::BoxxkiteError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum SandboxSize {
Small,
Medium,
Large,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct CreateSandboxOptions {
#[serde(skip_serializing_if = "Option::is_none")]
label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
size: Option<SandboxSize>,
#[serde(skip_serializing_if = "Option::is_none")]
storage_gb: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
lifetime_minutes: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
secret_names: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
image_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
mcp_connection_names: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
volume_mounts: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
gpu_count: Option<i64>,
}
impl CreateSandboxOptions {
pub fn new() -> Self {
Self::default()
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
pub fn size(mut self, size: SandboxSize) -> Self {
self.size = Some(size);
self
}
pub fn storage_gb(mut self, storage_gb: f64) -> Self {
self.storage_gb = Some(storage_gb);
self
}
pub fn lifetime_minutes(mut self, lifetime_minutes: i64) -> Self {
self.lifetime_minutes = Some(lifetime_minutes);
self
}
pub fn count(mut self, count: u32) -> Self {
self.count = Some(count);
self
}
pub fn secret_names<I, S>(mut self, secret_names: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.secret_names = Some(secret_names.into_iter().map(Into::into).collect());
self
}
pub fn image_id(mut self, image_id: impl Into<String>) -> Self {
self.image_id = Some(image_id.into());
self
}
pub fn mcp_connection_names<I, S>(mut self, names: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.mcp_connection_names = Some(names.into_iter().map(Into::into).collect());
self
}
pub fn volume_mounts(mut self, volume_mounts: HashMap<String, String>) -> Self {
self.volume_mounts = Some(volume_mounts);
self
}
pub fn gpu_count(mut self, gpu_count: i64) -> Self {
self.gpu_count = Some(gpu_count);
self
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct SandboxConnectInfo {
pub pod_name: Option<String>,
pub note: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct UsageSummary {
pub monthly_sandbox_hours_used: f64,
pub monthly_sandbox_hours_limit: f64,
pub concurrent_sandboxes: i64,
pub concurrent_sandboxes_limit: i64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Sandbox {
pub id: String,
pub status: String,
pub label: Option<String>,
pub created_at: String,
pub destroyed_at: Option<String>,
pub expires_at: Option<String>,
pub connect: Option<SandboxConnectInfo>,
pub usage: Option<UsageSummary>,
}
impl Client {
pub async fn create_sandbox(
&self,
options: CreateSandboxOptions,
) -> Result<Sandbox, BoxxkiteError> {
let builder = self.request(Method::POST, "/v1/sandboxes").json(&options);
self.send(builder).await
}
pub async fn create_sandbox_batch(
&self,
options: CreateSandboxOptions,
) -> Result<Vec<Sandbox>, BoxxkiteError> {
let builder = self.request(Method::POST, "/v1/sandboxes").json(&options);
self.send(builder).await
}
pub async fn get_sandbox(&self, session_id: &str) -> Result<Sandbox, BoxxkiteError> {
let builder = self.request(Method::GET, &format!("/v1/sandboxes/{session_id}"));
self.send(builder).await
}
pub async fn list_sandboxes(&self, active_only: bool) -> Result<Vec<Sandbox>, BoxxkiteError> {
let builder = self
.request(Method::GET, "/v1/sandboxes")
.query(&[("active_only", active_only.to_string())]);
self.send_or_default(builder).await
}
pub async fn destroy_sandbox(&self, session_id: &str) -> Result<(), BoxxkiteError> {
let builder = self.request(Method::DELETE, &format!("/v1/sandboxes/{session_id}"));
self.send_no_content(builder).await
}
}