diff --git a/Cargo.lock b/Cargo.lock index d058410..e9e8883 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1392,6 +1392,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + [[package]] name = "ecb" version = "0.1.2" @@ -4465,6 +4471,26 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "regex" version = "1.12.3" @@ -4909,6 +4935,31 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.116", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -5018,6 +5069,17 @@ dependencies = [ "syn 2.0.116", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "serde_html_form" version = "0.2.8" @@ -7098,6 +7160,7 @@ dependencies = [ "rusqlite", "rustls", "rustls-pki-types", + "schemars", "serde", "serde_json", "sha2", diff --git a/Cargo.toml b/Cargo.toml index 498f2b7..30e943e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,9 @@ directories = "6.0" toml = "1.0" shellexpand = "3.1" +# JSON Schema generation for config export +schemars = "1.2" + # Logging - minimal tracing = { version = "0.1", default-features = false } tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter"] } diff --git a/src/channels/email_channel.rs b/src/channels/email_channel.rs index 410e9dd..7a60efd 100644 --- a/src/channels/email_channel.rs +++ b/src/channels/email_channel.rs @@ -20,6 +20,7 @@ use lettre::{Message, SmtpTransport, Transport}; use mail_parser::{MessageParser, MimeHeaders}; use rustls::{ClientConfig, RootCertStore}; use rustls_pki_types::DnsName; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::collections::HashSet; use std::sync::Arc; @@ -35,7 +36,7 @@ use uuid::Uuid; use super::traits::{Channel, ChannelMessage, SendMessage}; /// Email channel configuration -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct EmailConfig { /// IMAP server hostname pub imap_host: String, diff --git a/src/config/schema.rs b/src/config/schema.rs index a4127d4..22590e3 100644 --- a/src/config/schema.rs +++ b/src/config/schema.rs @@ -2,6 +2,7 @@ use crate::providers::{is_glm_alias, is_zai_alias}; use crate::security::AutonomyLevel; use anyhow::{Context, Result}; use directories::UserDirs; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs::{self, File, OpenOptions}; @@ -45,7 +46,7 @@ static RUNTIME_PROXY_CLIENT_CACHE: OnceLock u32 { // ── Hardware Config (wizard-driven) ───────────────────────────── /// Hardware transport mode. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)] pub enum HardwareTransport { #[default] None, @@ -194,7 +195,7 @@ impl std::fmt::Display for HardwareTransport { } /// Wizard-driven hardware configuration for physical world interaction. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct HardwareConfig { /// Whether hardware access is enabled #[serde(default)] @@ -240,7 +241,7 @@ impl Default for HardwareConfig { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct AgentConfig { /// When true: bootstrap_max_chars=6000, rag_chunk_limit=2. Use for 13B or smaller models. #[serde(default)] @@ -281,7 +282,7 @@ impl Default for AgentConfig { // ── Identity (AIEOS / OpenClaw format) ────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct IdentityConfig { /// Identity format: "openclaw" (default) or "aieos" #[serde(default = "default_identity_format")] @@ -310,7 +311,7 @@ impl Default for IdentityConfig { // ── Cost tracking and budget enforcement ─────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct CostConfig { /// Enable cost tracking (default: false) #[serde(default)] @@ -337,7 +338,7 @@ pub struct CostConfig { pub prices: std::collections::HashMap, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct ModelPricing { /// Input price per 1M tokens #[serde(default)] @@ -451,7 +452,7 @@ fn get_default_pricing() -> std::collections::HashMap { // ── Peripherals (hardware: STM32, RPi GPIO, etc.) ──────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)] pub struct PeripheralsConfig { /// Enable peripheral support (boards become agent tools) #[serde(default)] @@ -465,7 +466,7 @@ pub struct PeripheralsConfig { pub datasheet_dir: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct PeripheralBoardConfig { /// Board type: "nucleo-f401re", "rpi-gpio", "esp32", etc. pub board: String, @@ -501,7 +502,7 @@ impl Default for PeripheralBoardConfig { // ── Gateway security ───────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct GatewayConfig { /// Gateway port (default: 3000) #[serde(default = "default_gateway_port")] @@ -597,7 +598,7 @@ impl Default for GatewayConfig { // ── Composio (managed tool surface) ───────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct ComposioConfig { /// Enable Composio integration for 1000+ OAuth tools #[serde(default)] @@ -626,7 +627,7 @@ impl Default for ComposioConfig { // ── Secrets (encrypted credential store) ──────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct SecretsConfig { /// Enable encryption for API keys and tokens in config.toml #[serde(default = "default_true")] @@ -641,7 +642,7 @@ impl Default for SecretsConfig { // ── Browser (friendly-service browsing only) ─────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct BrowserComputerUseConfig { /// Sidecar endpoint for computer-use actions (OS-level mouse/keyboard/screenshot) #[serde(default = "default_browser_computer_use_endpoint")] @@ -688,7 +689,7 @@ impl Default for BrowserComputerUseConfig { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct BrowserConfig { /// Enable `browser_open` tool (opens URLs in Brave without scraping) #[serde(default)] @@ -741,7 +742,7 @@ impl Default for BrowserConfig { // ── HTTP request tool ─────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)] pub struct HttpRequestConfig { /// Enable `http_request` tool for API interactions #[serde(default)] @@ -767,7 +768,7 @@ fn default_http_timeout_secs() -> u64 { // ── Web search ─────────────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct WebSearchConfig { /// Enable `web_search_tool` for web searches #[serde(default = "default_true")] @@ -812,7 +813,7 @@ impl Default for WebSearchConfig { // ── Proxy ─────────────────────────────────────────────────────── -#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ProxyScope { Environment, @@ -821,7 +822,7 @@ pub enum ProxyScope { Services, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct ProxyConfig { /// Enable proxy support for selected scope. #[serde(default)] @@ -1271,19 +1272,19 @@ fn parse_proxy_enabled(raw: &str) -> Option { } // ── Memory ─────────────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)] pub struct StorageConfig { #[serde(default)] pub provider: StorageProviderSection, } -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)] pub struct StorageProviderSection { #[serde(default)] pub config: StorageProviderConfig, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct StorageProviderConfig { /// Storage engine key (e.g. "postgres", "sqlite"). #[serde(default)] @@ -1332,7 +1333,7 @@ impl Default for StorageProviderConfig { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[allow(clippy::struct_excessive_bools)] pub struct MemoryConfig { /// "sqlite" | "lucid" | "postgres" | "markdown" | "none" (`none` = explicit no-op memory) @@ -1482,7 +1483,7 @@ impl Default for MemoryConfig { // ── Observability ───────────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct ObservabilityConfig { /// "none" | "log" | "prometheus" | "otel" pub backend: String, @@ -1508,7 +1509,7 @@ impl Default for ObservabilityConfig { // ── Autonomy / Security ────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct AutonomyConfig { pub level: AutonomyLevel, pub workspace_only: bool, @@ -1593,7 +1594,7 @@ impl Default for AutonomyConfig { // ── Runtime ────────────────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct RuntimeConfig { /// Runtime kind (`native` | `docker`). #[serde(default = "default_runtime_kind")] @@ -1604,7 +1605,7 @@ pub struct RuntimeConfig { pub docker: DockerRuntimeConfig, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct DockerRuntimeConfig { /// Runtime image used to execute shell commands. #[serde(default = "default_docker_image")] @@ -1680,7 +1681,7 @@ impl Default for RuntimeConfig { // ── Reliability / supervision ──────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct ReliabilityConfig { /// Retries per provider before failing over. #[serde(default = "default_provider_retries")] @@ -1755,7 +1756,7 @@ impl Default for ReliabilityConfig { // ── Scheduler ──────────────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct SchedulerConfig { /// Enable the built-in scheduler loop. #[serde(default = "default_scheduler_enabled")] @@ -1807,7 +1808,7 @@ impl Default for SchedulerConfig { /// ``` /// /// Usage: pass `hint:reasoning` as the model parameter to route the request. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct ModelRouteConfig { /// Task hint name (e.g. "reasoning", "fast", "code", "summarize") pub hint: String, @@ -1824,7 +1825,7 @@ pub struct ModelRouteConfig { /// Automatic query classification — classifies user messages by keyword/pattern /// and routes to the appropriate model hint. Disabled by default. -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)] pub struct QueryClassificationConfig { #[serde(default)] pub enabled: bool, @@ -1833,7 +1834,7 @@ pub struct QueryClassificationConfig { } /// A single classification rule mapping message patterns to a model hint. -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)] pub struct ClassificationRule { /// Must match a `[[model_routes]]` hint value. pub hint: String, @@ -1856,7 +1857,7 @@ pub struct ClassificationRule { // ── Heartbeat ──────────────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct HeartbeatConfig { pub enabled: bool, pub interval_minutes: u32, @@ -1873,7 +1874,7 @@ impl Default for HeartbeatConfig { // ── Cron ──────────────────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct CronConfig { #[serde(default = "default_true")] pub enabled: bool, @@ -1896,7 +1897,7 @@ impl Default for CronConfig { // ── Tunnel ────────────────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct TunnelConfig { /// "none", "cloudflare", "tailscale", "ngrok", "custom" pub provider: String, @@ -1926,13 +1927,13 @@ impl Default for TunnelConfig { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct CloudflareTunnelConfig { /// Cloudflare Tunnel token (from Zero Trust dashboard) pub token: String, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct TailscaleTunnelConfig { /// Use Tailscale Funnel (public internet) vs Serve (tailnet only) #[serde(default)] @@ -1941,7 +1942,7 @@ pub struct TailscaleTunnelConfig { pub hostname: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct NgrokTunnelConfig { /// ngrok auth token pub auth_token: String, @@ -1949,7 +1950,7 @@ pub struct NgrokTunnelConfig { pub domain: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct CustomTunnelConfig { /// Command template to start the tunnel. Use {port} and {host} placeholders. /// Example: "bore local {port} --to bore.pub" @@ -1962,7 +1963,7 @@ pub struct CustomTunnelConfig { // ── Channels ───────────────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct ChannelsConfig { pub cli: bool, pub telegram: Option, @@ -2015,7 +2016,7 @@ impl Default for ChannelsConfig { } /// Streaming mode for channels that support progressive message updates. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum StreamMode { /// No streaming -- send the complete response as a single message (default). @@ -2029,7 +2030,7 @@ fn default_draft_update_interval_ms() -> u64 { 1000 } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct TelegramConfig { pub bot_token: String, pub allowed_users: Vec, @@ -2045,7 +2046,7 @@ pub struct TelegramConfig { pub mention_only: bool, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct DiscordConfig { pub bot_token: String, pub guild_id: Option, @@ -2061,7 +2062,7 @@ pub struct DiscordConfig { pub mention_only: bool, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct SlackConfig { pub bot_token: String, pub app_token: Option, @@ -2070,7 +2071,7 @@ pub struct SlackConfig { pub allowed_users: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct MattermostConfig { pub url: String, pub bot_token: String, @@ -2087,18 +2088,18 @@ pub struct MattermostConfig { pub mention_only: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct WebhookConfig { pub port: u16, pub secret: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct IMessageConfig { pub allowed_contacts: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct MatrixConfig { pub homeserver: String, pub access_token: String, @@ -2110,7 +2111,7 @@ pub struct MatrixConfig { pub allowed_users: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct SignalConfig { /// Base URL for the signal-cli HTTP daemon (e.g. "http://127.0.0.1:8686"). pub http_url: String, @@ -2133,7 +2134,7 @@ pub struct SignalConfig { pub ignore_stories: bool, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct WhatsAppConfig { /// Access token from Meta Business Suite pub access_token: String, @@ -2150,7 +2151,7 @@ pub struct WhatsAppConfig { pub allowed_numbers: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct LinqConfig { /// Linq Partner API token (Bearer auth) pub api_token: String, @@ -2164,7 +2165,7 @@ pub struct LinqConfig { pub allowed_senders: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct IrcConfig { /// IRC server hostname pub server: String, @@ -2199,7 +2200,7 @@ fn default_irc_port() -> u16 { /// /// - `websocket` (default) — persistent WSS long-connection; no public URL required. /// - `webhook` — HTTP callback server; requires a public HTTPS endpoint. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum LarkReceiveMode { #[default] @@ -2209,7 +2210,7 @@ pub enum LarkReceiveMode { /// Lark/Feishu configuration for messaging integration. /// Lark is the international version; Feishu is the Chinese version. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct LarkConfig { /// App ID from Lark/Feishu developer console pub app_id: String, @@ -2239,7 +2240,7 @@ pub struct LarkConfig { // ── Security Config ───────────────────────────────────────────────── /// Security configuration for sandboxing, resource limits, and audit logging -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)] pub struct SecurityConfig { /// Sandbox configuration #[serde(default)] @@ -2255,7 +2256,7 @@ pub struct SecurityConfig { } /// Sandbox configuration for OS-level isolation -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct SandboxConfig { /// Enable sandboxing (None = auto-detect, Some = explicit) #[serde(default)] @@ -2281,7 +2282,7 @@ impl Default for SandboxConfig { } /// Sandbox backend selection -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum SandboxBackend { /// Auto-detect best available (default) @@ -2300,7 +2301,7 @@ pub enum SandboxBackend { } /// Resource limits for command execution -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct ResourceLimitsConfig { /// Maximum memory in MB per command #[serde(default = "default_max_memory_mb")] @@ -2347,7 +2348,7 @@ impl Default for ResourceLimitsConfig { } /// Audit logging configuration -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct AuditConfig { /// Enable audit logging #[serde(default = "default_audit_enabled")] @@ -2390,7 +2391,7 @@ impl Default for AuditConfig { } /// DingTalk configuration for Stream Mode messaging -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct DingTalkConfig { /// Client ID (AppKey) from DingTalk developer console pub client_id: String, @@ -2402,7 +2403,7 @@ pub struct DingTalkConfig { } /// QQ Official Bot configuration (Tencent QQ Bot SDK) -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct QQConfig { /// App ID from QQ Bot developer console pub app_id: String, diff --git a/src/main.rs b/src/main.rs index 4f6c08c..5b8e892 100644 --- a/src/main.rs +++ b/src/main.rs @@ -242,6 +242,18 @@ enum Commands { #[command(subcommand)] peripheral_command: zeroclaw::PeripheralCommands, }, + + /// Manage configuration + Config { + #[command(subcommand)] + config_command: ConfigCommands, + }, +} + +#[derive(Subcommand, Debug)] +enum ConfigCommands { + /// Dump the full configuration JSON Schema to stdout + Schema, } #[derive(Subcommand, Debug)] @@ -766,6 +778,18 @@ async fn main() -> Result<()> { Commands::Peripheral { peripheral_command } => { peripherals::handle_command(peripheral_command.clone(), &config) } + + Commands::Config { config_command } => match config_command { + ConfigCommands::Schema => { + let schema = schemars::schema_for!(config::Config); + println!( + "{}", + serde_json::to_string_pretty(&schema) + .expect("failed to serialize JSON Schema") + ); + Ok(()) + } + }, } } diff --git a/src/security/policy.rs b/src/security/policy.rs index 806a399..bf799ef 100644 --- a/src/security/policy.rs +++ b/src/security/policy.rs @@ -1,10 +1,11 @@ use parking_lot::Mutex; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use std::time::Instant; /// How much autonomy the agent has -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum AutonomyLevel { /// Read-only: can observe but not act