pub mod providers;
pub mod types;
pub use providers::remote::{RemoteSandbox, RemoteSandboxConfig, SandboxAuth};
pub use types::*;
#[cfg(feature = "wasm-sandbox")]
pub use providers::wasm::{WasmSandbox, WasmSandboxConfig};
use crate::error::Result;
use async_trait::async_trait;
use std::collections::HashMap;
use std::sync::Arc;
#[async_trait]
pub trait SandboxExecutor: Send + Sync {
fn backend_kind(&self) -> SandboxBackendKind;
fn capabilities(&self) -> &SandboxCapabilities;
async fn execute_code(&self, req: ExecuteCodeRequest) -> Result<ExecuteCodeResult>;
async fn health(&self) -> Result<SandboxHealthResult>;
}
#[async_trait]
pub trait SandboxWorkspace: Send + Sync {
async fn write_file(&self, req: WriteFileRequest) -> Result<WriteFileResult>;
async fn read_file(&self, path: &str) -> Result<ReadFileResult>;
async fn delete_file(&self, path: &str, recursive: bool) -> Result<bool>;
async fn list_files(&self, path: &str, recursive: bool) -> Result<ListFilesResult>;
}
pub trait SandboxBackend: SandboxExecutor + SandboxWorkspace {}
impl<T: SandboxExecutor + SandboxWorkspace> SandboxBackend for T {}
#[async_trait]
pub trait SandboxProvider: Send + Sync {
fn name(&self) -> &'static str;
async fn create_sandbox(&self, opts: CreateSandboxOptions) -> Result<Arc<dyn SandboxBackend>>;
async fn connect_sandbox(&self, sandbox_id: &str) -> Result<Arc<dyn SandboxBackend>>;
async fn destroy_sandbox(&self, sandbox_id: &str) -> Result<bool>;
async fn list_sandboxes(&self) -> Result<Vec<SandboxInfo>>;
}
pub struct SandboxRegistry {
backends: HashMap<String, Arc<dyn SandboxBackend>>,
providers: HashMap<String, Arc<dyn SandboxProvider>>,
default_backend: Option<String>,
}
impl SandboxRegistry {
pub fn new() -> Self {
Self {
backends: HashMap::new(),
providers: HashMap::new(),
default_backend: None,
}
}
pub fn register(&mut self, name: String, backend: Arc<dyn SandboxBackend>) {
if self.default_backend.is_none() {
self.default_backend = Some(name.clone());
}
self.backends.insert(name, backend);
}
pub fn get(&self, name: &str) -> Option<Arc<dyn SandboxBackend>> {
self.backends.get(name).cloned()
}
pub fn default_backend(&self) -> Option<Arc<dyn SandboxBackend>> {
self.default_backend
.as_ref()
.and_then(|name| self.backends.get(name))
.cloned()
}
pub fn set_default(&mut self, name: &str) -> Result<()> {
if !self.backends.contains_key(name) {
return Err(crate::error::SdkError::Configuration {
message: format!("sandbox backend '{}' is not registered", name),
field: Some("default_backend".to_string()),
});
}
self.default_backend = Some(name.to_string());
Ok(())
}
pub fn list_backends(&self) -> Vec<&str> {
self.backends.keys().map(|s| s.as_str()).collect()
}
pub fn register_provider(&mut self, provider: Arc<dyn SandboxProvider>) {
self.providers.insert(provider.name().to_string(), provider);
}
pub fn get_provider(&self, name: &str) -> Option<Arc<dyn SandboxProvider>> {
self.providers.get(name).cloned()
}
pub fn list_providers(&self) -> Vec<&str> {
self.providers.keys().map(|s| s.as_str()).collect()
}
pub fn load_from_environment(&mut self) -> Result<()> {
if let Ok(endpoint) = std::env::var("AGNT5_SANDBOX_ENDPOINT") {
let sandbox_id =
std::env::var("AGNT5_SANDBOX_ID").unwrap_or_else(|_| "default".to_string());
let auth = if let Ok(key) = std::env::var("AGNT5_SANDBOX_API_KEY") {
SandboxAuth::ApiKey(key)
} else if let Ok(token) = std::env::var("AGNT5_SANDBOX_BEARER_TOKEN") {
SandboxAuth::BearerToken(token)
} else {
SandboxAuth::None
};
let timeout_secs: u64 = std::env::var("AGNT5_SANDBOX_TIMEOUT_SECS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(300);
let api_prefix = std::env::var("AGNT5_SANDBOX_API_PREFIX").unwrap_or_default();
let config = RemoteSandboxConfig {
endpoint,
sandbox_id,
auth,
timeout: std::time::Duration::from_secs(timeout_secs),
api_prefix,
};
let remote = RemoteSandbox::new(config)?;
self.register("remote".to_string(), Arc::new(remote));
return Ok(());
}
#[cfg(feature = "wasm-sandbox")]
{
let config = WasmSandboxConfig::default();
let wasm = WasmSandbox::new(config)?;
self.register("wasm".to_string(), Arc::new(wasm));
}
Ok(())
}
}
impl Default for SandboxRegistry {
fn default() -> Self {
Self::new()
}
}