pub mod discovery;
pub mod manager;
pub mod registry;
pub use discovery::OnlineDiscovery;
pub use manager::ExtensionManager;
pub use registry::ExtensionRegistry;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ExtensionKind {
McpServer,
WasmTool,
WasmChannel,
}
impl std::fmt::Display for ExtensionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ExtensionKind::McpServer => write!(f, "mcp_server"),
ExtensionKind::WasmTool => write!(f, "wasm_tool"),
ExtensionKind::WasmChannel => write!(f, "wasm_channel"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegistryEntry {
pub name: String,
pub display_name: String,
pub kind: ExtensionKind,
pub description: String,
#[serde(default)]
pub keywords: Vec<String>,
pub source: ExtensionSource,
pub auth_hint: AuthHint,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ExtensionSource {
McpUrl { url: String },
WasmDownload {
wasm_url: String,
#[serde(default)]
capabilities_url: Option<String>,
},
WasmBuildable {
repo_url: String,
#[serde(default)]
build_dir: Option<String>,
},
Discovered { url: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AuthHint {
Dcr,
OAuthPreConfigured {
setup_url: String,
},
CapabilitiesAuth,
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResultSource {
Registry,
Discovered,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
#[serde(flatten)]
pub entry: RegistryEntry,
pub source: ResultSource,
#[serde(default)]
pub validated: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstallResult {
pub name: String,
pub kind: ExtensionKind,
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthResult {
pub name: String,
pub kind: ExtensionKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub callback_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub instructions: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub setup_url: Option<String>,
#[serde(default)]
pub awaiting_token: bool,
pub status: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActivateResult {
pub name: String,
pub kind: ExtensionKind,
pub tools_loaded: Vec<String>,
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstalledExtension {
pub name: String,
pub kind: ExtensionKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
pub authenticated: bool,
pub active: bool,
#[serde(default)]
pub tools: Vec<String>,
}
#[derive(Debug, thiserror::Error)]
pub enum ExtensionError {
#[error("Extension not found: {0}")]
NotFound(String),
#[error("Extension already installed: {0}")]
AlreadyInstalled(String),
#[error("Extension not installed: {0}")]
NotInstalled(String),
#[error("Authentication failed: {0}")]
AuthFailed(String),
#[error("Activation failed: {0}")]
ActivationFailed(String),
#[error("Installation failed: {0}")]
InstallFailed(String),
#[error("Discovery failed: {0}")]
DiscoveryFailed(String),
#[error("Invalid URL: {0}")]
InvalidUrl(String),
#[error("Download failed: {0}")]
DownloadFailed(String),
#[error("Config error: {0}")]
Config(String),
#[error("Channels require restart to activate")]
ChannelNeedsRestart,
#[error("{0}")]
Other(String),
}