#![warn(clippy::all, clippy::pedantic)] #![allow( clippy::assigning_clones, clippy::bool_to_int_with_if, clippy::case_sensitive_file_extension_comparisons, clippy::cast_possible_wrap, clippy::doc_markdown, clippy::field_reassign_with_default, clippy::float_cmp, clippy::implicit_clone, clippy::items_after_statements, clippy::map_unwrap_or, clippy::manual_let_else, clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::module_name_repetitions, clippy::must_use_candidate, clippy::new_without_default, clippy::needless_pass_by_value, clippy::needless_raw_string_hashes, clippy::redundant_closure_for_method_calls, clippy::return_self_not_must_use, clippy::similar_names, clippy::single_match_else, clippy::struct_field_names, clippy::too_many_lines, clippy::uninlined_format_args, clippy::unnecessary_cast, clippy::unnecessary_lazy_evaluations, clippy::unnecessary_literal_bound, clippy::unnecessary_map_or, clippy::unused_self, clippy::cast_precision_loss, clippy::unnecessary_wraps, dead_code )] use clap::Subcommand; use serde::{Deserialize, Serialize}; pub mod agent; pub(crate) mod approval; pub(crate) mod auth; pub mod channels; pub mod config; pub(crate) mod cost; pub(crate) mod cron; pub(crate) mod daemon; pub(crate) mod doctor; pub mod gateway; pub(crate) mod hardware; pub(crate) mod health; pub(crate) mod heartbeat; pub(crate) mod identity; pub(crate) mod integrations; pub mod memory; pub(crate) mod migration; pub(crate) mod multimodal; pub mod observability; pub(crate) mod onboard; pub mod peripherals; pub mod providers; pub mod rag; pub mod runtime; pub(crate) mod security; pub(crate) mod service; pub(crate) mod skills; pub mod tools; pub(crate) mod tunnel; pub(crate) mod util; pub use config::Config; /// Service management subcommands #[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub(crate) enum ServiceCommands { /// Install daemon service unit for auto-start and restart Install, /// Start daemon service Start, /// Stop daemon service Stop, /// Restart daemon service to apply latest config Restart, /// Check daemon service status Status, /// Uninstall daemon service unit Uninstall, } /// Channel management subcommands #[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub(crate) enum ChannelCommands { /// List all configured channels List, /// Start all configured channels (handled in main.rs for async) Start, /// Run health checks for configured channels (handled in main.rs for async) Doctor, /// Add a new channel configuration #[command(long_about = "\ Add a new channel configuration. Provide the channel type and a JSON object with the required \ configuration keys for that channel type. Supported types: telegram, discord, slack, whatsapp, matrix, imessage, email. Examples: zeroclaw channel add telegram '{\"bot_token\":\"...\",\"name\":\"my-bot\"}' zeroclaw channel add discord '{\"bot_token\":\"...\",\"name\":\"my-discord\"}'")] Add { /// Channel type (telegram, discord, slack, whatsapp, matrix, imessage, email) channel_type: String, /// Optional configuration as JSON config: String, }, /// Remove a channel configuration Remove { /// Channel name to remove name: String, }, /// Bind a Telegram identity (username or numeric user ID) into allowlist #[command(long_about = "\ Bind a Telegram identity into the allowlist. Adds a Telegram username (without the '@' prefix) or numeric user \ ID to the channel allowlist so the agent will respond to messages \ from that identity. Examples: zeroclaw channel bind-telegram zeroclaw_user zeroclaw channel bind-telegram 123456789")] BindTelegram { /// Telegram identity to allow (username without '@' or numeric user ID) identity: String, }, } /// Skills management subcommands #[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub(crate) enum SkillCommands { /// List all installed skills List, /// Install a new skill from a git URL (HTTPS/SSH) or local path Install { /// Source git URL (HTTPS/SSH) or local path source: String, }, /// Remove an installed skill Remove { /// Skill name to remove name: String, }, } /// Migration subcommands #[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub(crate) enum MigrateCommands { /// Import memory from an `OpenClaw` workspace into this `ZeroClaw` workspace Openclaw { /// Optional path to `OpenClaw` workspace (defaults to ~/.openclaw/workspace) #[arg(long)] source: Option, /// Validate and preview migration without writing any data #[arg(long)] dry_run: bool, }, } /// Cron subcommands #[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub(crate) enum CronCommands { /// List all scheduled tasks List, /// Add a new scheduled task #[command(long_about = "\ Add a new recurring scheduled task. Uses standard 5-field cron syntax: 'min hour day month weekday'. \ Times are evaluated in UTC by default; use --tz with an IANA \ timezone name to override. Examples: zeroclaw cron add '0 9 * * 1-5' 'Good morning' --tz America/New_York zeroclaw cron add '*/30 * * * *' 'Check system health'")] Add { /// Cron expression expression: String, /// Optional IANA timezone (e.g. America/Los_Angeles) #[arg(long)] tz: Option, /// Command to run command: String, }, /// Add a one-shot scheduled task at an RFC3339 timestamp #[command(long_about = "\ Add a one-shot task that fires at a specific UTC timestamp. The timestamp must be in RFC 3339 format (e.g. 2025-01-15T14:00:00Z). Examples: zeroclaw cron add-at 2025-01-15T14:00:00Z 'Send reminder' zeroclaw cron add-at 2025-12-31T23:59:00Z 'Happy New Year!'")] AddAt { /// One-shot timestamp in RFC3339 format at: String, /// Command to run command: String, }, /// Add a fixed-interval scheduled task #[command(long_about = "\ Add a task that repeats at a fixed interval. Interval is specified in milliseconds. For example, 60000 = 1 minute. Examples: zeroclaw cron add-every 60000 'Ping heartbeat' # every minute zeroclaw cron add-every 3600000 'Hourly report' # every hour")] AddEvery { /// Interval in milliseconds every_ms: u64, /// Command to run command: String, }, /// Add a one-shot delayed task (e.g. "30m", "2h", "1d") #[command(long_about = "\ Add a one-shot task that fires after a delay from now. Accepts human-readable durations: s (seconds), m (minutes), \ h (hours), d (days). Examples: zeroclaw cron once 30m 'Run backup in 30 minutes' zeroclaw cron once 2h 'Follow up on deployment' zeroclaw cron once 1d 'Daily check'")] Once { /// Delay duration delay: String, /// Command to run command: String, }, /// Remove a scheduled task Remove { /// Task ID id: String, }, /// Update a scheduled task #[command(long_about = "\ Update one or more fields of an existing scheduled task. Only the fields you specify are changed; others remain unchanged. Examples: zeroclaw cron update --expression '0 8 * * *' zeroclaw cron update --tz Europe/London --name 'Morning check' zeroclaw cron update --command 'Updated message'")] Update { /// Task ID id: String, /// New cron expression #[arg(long)] expression: Option, /// New IANA timezone #[arg(long)] tz: Option, /// New command to run #[arg(long)] command: Option, /// New job name #[arg(long)] name: Option, }, /// Pause a scheduled task Pause { /// Task ID id: String, }, /// Resume a paused task Resume { /// Task ID id: String, }, } /// Integration subcommands #[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub(crate) enum IntegrationCommands { /// Show details about a specific integration Info { /// Integration name name: String, }, } /// Hardware discovery subcommands #[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub enum HardwareCommands { /// Enumerate USB devices (VID/PID) and show known boards #[command(long_about = "\ Enumerate USB devices and show known boards. Scans connected USB devices by VID/PID and matches them against \ known development boards (STM32 Nucleo, Arduino, ESP32). Examples: zeroclaw hardware discover")] Discover, /// Introspect a device by path (e.g. /dev/ttyACM0) #[command(long_about = "\ Introspect a device by its serial or device path. Opens the specified device path and queries for board information, \ firmware version, and supported capabilities. Examples: zeroclaw hardware introspect /dev/ttyACM0 zeroclaw hardware introspect COM3")] Introspect { /// Serial or device path path: String, }, /// Get chip info via USB (probe-rs over ST-Link). No firmware needed on target. #[command(long_about = "\ Get chip info via USB using probe-rs over ST-Link. Queries the target MCU directly through the debug probe without \ requiring any firmware on the target board. Examples: zeroclaw hardware info zeroclaw hardware info --chip STM32F401RETx")] Info { /// Chip name (e.g. STM32F401RETx). Default: STM32F401RETx for Nucleo-F401RE #[arg(long, default_value = "STM32F401RETx")] chip: String, }, } /// Peripheral (hardware) management subcommands #[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub enum PeripheralCommands { /// List configured peripherals List, /// Add a peripheral (board path, e.g. nucleo-f401re /dev/ttyACM0) #[command(long_about = "\ Add a peripheral by board type and transport path. Registers a hardware board so the agent can use its tools (GPIO, \ sensors, actuators). Use 'native' as path for local GPIO on \ single-board computers like Raspberry Pi. Supported boards: nucleo-f401re, rpi-gpio, esp32, arduino-uno. Examples: zeroclaw peripheral add nucleo-f401re /dev/ttyACM0 zeroclaw peripheral add rpi-gpio native zeroclaw peripheral add esp32 /dev/ttyUSB0")] Add { /// Board type (nucleo-f401re, rpi-gpio, esp32) board: String, /// Path for serial transport (/dev/ttyACM0) or "native" for local GPIO path: String, }, /// Flash ZeroClaw firmware to Arduino (creates .ino, installs arduino-cli if needed, uploads) #[command(long_about = "\ Flash ZeroClaw firmware to an Arduino board. Generates the .ino sketch, installs arduino-cli if it is not \ already available, compiles, and uploads the firmware. Examples: zeroclaw peripheral flash zeroclaw peripheral flash --port /dev/cu.usbmodem12345 zeroclaw peripheral flash -p COM3")] Flash { /// Serial port (e.g. /dev/cu.usbmodem12345). If omitted, uses first arduino-uno from config. #[arg(short, long)] port: Option, }, /// Setup Arduino Uno Q Bridge app (deploy GPIO bridge for agent control) SetupUnoQ { /// Uno Q IP (e.g. 192.168.0.48). If omitted, assumes running ON the Uno Q. #[arg(long)] host: Option, }, /// Flash ZeroClaw firmware to Nucleo-F401RE (builds + probe-rs run) FlashNucleo, }