feat: add zeroclaw config schema for JSON Schema export

Add a `config schema` subcommand that dumps the full configuration
schema as JSON Schema (draft 2020-12) to stdout. This enables
downstream consumers (like PankoAgent) to programmatically validate
configs, generate forms, and stay in sync with zeroclaw's evolving
config surface without hand-maintaining copies of the schema.

- Add schemars 1.2 dependency and derive JsonSchema on all config
  structs/enums (schema.rs, policy.rs, email_channel.rs)
- Add `Config` subcommand group with `Schema` sub-command
- Output is valid JSON Schema with $defs for all 56 config types
This commit is contained in:
s04 2026-02-18 20:49:15 +01:00 committed by Chummy
parent d44dc5a048
commit 996f66b6a7
6 changed files with 156 additions and 63 deletions

63
Cargo.lock generated
View file

@ -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",

View file

@ -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"] }

View file

@ -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,

View file

@ -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<RwLock<HashMap<String, reqwest::Clie
// ── Top-level config ──────────────────────────────────────────────
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct Config {
/// Workspace directory - computed from home, not serialized
#[serde(skip)]
@ -146,7 +147,7 @@ pub struct Config {
// ── Delegate Agents ──────────────────────────────────────────────
/// Configuration for a delegate sub-agent used by the `delegate` tool.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct DelegateAgentConfig {
/// Provider name (e.g. "ollama", "openrouter", "anthropic")
pub provider: String,
@ -173,7 +174,7 @@ fn default_max_depth() -> 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<String, ModelPricing>,
}
#[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<String, ModelPricing> {
// ── 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<String>,
}
#[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<bool> {
}
// ── 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<String>,
}
#[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<String>,
}
#[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<TelegramConfig>,
@ -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<String>,
@ -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<String>,
@ -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<String>,
@ -2070,7 +2071,7 @@ pub struct SlackConfig {
pub allowed_users: Vec<String>,
}
#[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<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct WebhookConfig {
pub port: u16,
pub secret: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct IMessageConfig {
pub allowed_contacts: Vec<String>,
}
#[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<String>,
}
#[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<String>,
}
#[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<String>,
}
#[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,

View file

@ -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(())
}
},
}
}

View file

@ -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