Something went wrong. Try again.
Flatpak-style launcher for sandboxed WebAssembly tools. Install, verify, and run .wasm binaries with explicit capability grants. Single binary, local-first, zero telemetry. wasmbox.orbiter.website
wasm rust wasmbox
Something went wrong. Try again.
4.6 kB · 154 lines
Rust
at dev
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155//! Registry client for fetching tool manifests and binaries over HTTPS.
use serde::{Deserialize, Serialize};use thiserror::Error;
#[derive(Debug, Error)]pub enum RegistryError { #[error("http error: {0}")] Http(#[from] reqwest::Error),
#[error("tool not found: {0}")] ToolNotFound(String),
#[error("invalid registry response: {0}")] InvalidResponse(String),
#[error("io error: {0}")] Io(#[from] std::io::Error),}
/// An entry in the registry index.#[derive(Debug, Clone, Serialize, Deserialize)]pub struct RegistryEntry { pub name: String, pub version: String, pub description: String, pub hash: String, pub size: u64, #[serde(default)] pub categories: Vec<String>,}
/// The full registry index.#[derive(Debug, Clone, Serialize, Deserialize)]pub struct RegistryIndex { pub registry: String, pub tools: Vec<RegistryEntry>,}
/// A configured registry endpoint.#[derive(Debug, Clone, Serialize, Deserialize)]pub struct Registry { pub url: String,}
/// Client for interacting with WasmBox registries.pub struct RegistryClient { http: reqwest::Client, registries: Vec<Registry>,}
impl RegistryClient { pub fn new(registries: Vec<Registry>) -> Self { let http = reqwest::Client::builder() .user_agent(format!("wasmbox/{}", env!("CARGO_PKG_VERSION"))) .build() .expect("failed to create HTTP client");
Self { http, registries } }
/// Fetch the index from a registry. pub async fn fetch_index(&self, registry: &Registry) -> Result<RegistryIndex, RegistryError> { let url = format!("{}/index.json", registry.url.trim_end_matches('/')); let resp = self.http.get(&url).send().await?.error_for_status()?; let index: RegistryIndex = resp.json().await?; Ok(index) }
/// Search all registries for tools matching a query. pub async fn search(&self, query: &str) -> Result<Vec<RegistryEntry>, RegistryError> { let mut results = Vec::new(); let query_lower = query.to_lowercase();
for registry in &self.registries { match self.fetch_index(registry).await { Ok(index) => { for tool in index.tools { if tool.name.to_lowercase().contains(&query_lower) || tool.description.to_lowercase().contains(&query_lower) || tool .categories .iter() .any(|c| c.to_lowercase().contains(&query_lower)) { results.push(tool); } } } Err(e) => { eprintln!("warning: failed to fetch from {}: {e}", registry.url); } } }
Ok(results) }
/// Fetch a tool's manifest JSON from a registry. pub async fn fetch_manifest( &self, registry: &Registry, tool_name: &str, ) -> Result<String, RegistryError> { let url = format!( "{}/tools/{}.json", registry.url.trim_end_matches('/'), tool_name, ); let resp = self.http.get(&url).send().await?; if resp.status() == reqwest::StatusCode::NOT_FOUND { return Err(RegistryError::ToolNotFound(tool_name.to_string())); } let text = resp.error_for_status()?.text().await?; Ok(text) }
/// Download a tool's .wasm binary from a registry. pub async fn fetch_binary( &self, registry: &Registry, tool_name: &str, ) -> Result<Vec<u8>, RegistryError> { let url = format!( "{}/tools/{}.wasm", registry.url.trim_end_matches('/'), tool_name, ); let resp = self.http.get(&url).send().await?; if resp.status() == reqwest::StatusCode::NOT_FOUND { return Err(RegistryError::ToolNotFound(tool_name.to_string())); } let bytes = resp.error_for_status()?.bytes().await?; Ok(bytes.to_vec()) }
/// Get the list of configured registries. pub fn registries(&self) -> &[Registry] { &self.registries }}
#[cfg(test)]mod tests { use super::*;
#[test] fn create_client() { let client = RegistryClient::new(vec![Registry { url: "https://registry.example.com".to_string(), }]); assert_eq!(client.registries().len(), 1); }}