Skip to main content

zeroclaw/
lib.rs

1#![warn(clippy::all, clippy::pedantic)]
2#![allow(
3    clippy::assigning_clones,
4    clippy::bool_to_int_with_if,
5    clippy::case_sensitive_file_extension_comparisons,
6    clippy::cast_possible_wrap,
7    clippy::doc_markdown,
8    clippy::field_reassign_with_default,
9    clippy::float_cmp,
10    clippy::implicit_clone,
11    clippy::items_after_statements,
12    clippy::map_unwrap_or,
13    clippy::manual_let_else,
14    clippy::missing_errors_doc,
15    clippy::missing_panics_doc,
16    clippy::module_name_repetitions,
17    clippy::must_use_candidate,
18    clippy::new_without_default,
19    clippy::needless_pass_by_value,
20    clippy::needless_raw_string_hashes,
21    clippy::redundant_closure_for_method_calls,
22    clippy::return_self_not_must_use,
23    clippy::similar_names,
24    clippy::single_match_else,
25    clippy::struct_field_names,
26    clippy::too_many_lines,
27    clippy::uninlined_format_args,
28    clippy::unnecessary_cast,
29    clippy::unnecessary_lazy_evaluations,
30    clippy::unnecessary_literal_bound,
31    clippy::unnecessary_map_or,
32    clippy::unused_self,
33    clippy::cast_precision_loss,
34    clippy::unnecessary_wraps,
35    dead_code
36)]
37
38use clap::Subcommand;
39use serde::{Deserialize, Serialize};
40
41pub mod agent;
42pub(crate) mod approval;
43pub(crate) mod auth;
44pub mod channels;
45pub(crate) mod cli_input;
46pub mod commands;
47pub mod config;
48pub(crate) mod cost;
49pub mod cron;
50pub(crate) mod daemon;
51pub(crate) mod doctor;
52pub mod gateway;
53pub mod hands;
54pub(crate) mod hardware;
55pub(crate) mod health;
56pub(crate) mod heartbeat;
57pub mod hooks;
58pub mod i18n;
59pub(crate) mod identity;
60pub(crate) mod integrations;
61pub mod memory;
62pub(crate) mod migration;
63pub(crate) mod multimodal;
64pub mod nodes;
65pub mod observability;
66pub(crate) mod onboard;
67pub mod peripherals;
68pub mod providers;
69pub mod rag;
70pub mod routines;
71pub mod runtime;
72pub(crate) mod security;
73pub(crate) mod service;
74pub(crate) mod skills;
75pub mod sop;
76pub mod tools;
77pub(crate) mod trust;
78pub mod tui;
79pub(crate) mod tunnel;
80pub(crate) mod util;
81pub mod verifiable_intent;
82
83#[cfg(feature = "plugins-wasm")]
84pub mod plugins;
85
86pub use config::Config;
87
88/// Gateway management subcommands
89#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
90pub enum GatewayCommands {
91    /// Start the gateway server (default if no subcommand specified)
92    #[command(long_about = "\
93Start the gateway server (webhooks, websockets).
94
95Runs the HTTP/WebSocket gateway that accepts incoming webhook events \
96and WebSocket connections. Bind address defaults to the values in \
97your config file (gateway.host / gateway.port).
98
99Examples:
100  zeroclaw gateway start              # use config defaults
101  zeroclaw gateway start -p 8080      # listen on port 8080
102  zeroclaw gateway start --host 0.0.0.0   # requires [gateway].allow_public_bind=true or a tunnel
103  zeroclaw gateway start -p 0         # random available port")]
104    Start {
105        /// Port to listen on (use 0 for random available port); defaults to config gateway.port
106        #[arg(short, long)]
107        port: Option<u16>,
108
109        /// Host to bind to; defaults to config gateway.host
110        /// Note: Binding to 0.0.0.0 requires `gateway.allow_public_bind = true` in config
111        #[arg(long)]
112        host: Option<String>,
113    },
114    /// Restart the gateway server
115    #[command(long_about = "\
116Restart the gateway server.
117
118Stops the running gateway if present, then starts a new instance \
119with the current configuration.
120
121Examples:
122  zeroclaw gateway restart            # restart with config defaults
123  zeroclaw gateway restart -p 8080    # restart on port 8080")]
124    Restart {
125        /// Port to listen on (use 0 for random available port); defaults to config gateway.port
126        #[arg(short, long)]
127        port: Option<u16>,
128
129        /// Host to bind to; defaults to config gateway.host
130        /// Note: Binding to 0.0.0.0 requires `gateway.allow_public_bind = true` in config
131        #[arg(long)]
132        host: Option<String>,
133    },
134    /// Show or generate the pairing code without restarting
135    #[command(long_about = "\
136Show or generate the gateway pairing code.
137
138Displays the pairing code for connecting new clients without \
139restarting the gateway. Requires the gateway to be running.
140
141With --new, generates a fresh pairing code even if the gateway \
142was previously paired (useful for adding additional clients).
143
144Examples:
145  zeroclaw gateway get-paircode       # show current pairing code
146  zeroclaw gateway get-paircode --new # generate a new pairing code")]
147    GetPaircode {
148        /// Generate a new pairing code (even if already paired)
149        #[arg(long)]
150        new: bool,
151    },
152}
153
154/// Service management subcommands
155#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
156pub enum ServiceCommands {
157    /// Install daemon service unit for auto-start and restart
158    Install,
159    /// Start daemon service
160    Start,
161    /// Stop daemon service
162    Stop,
163    /// Restart daemon service to apply latest config
164    Restart,
165    /// Check daemon service status
166    Status,
167    /// Uninstall daemon service unit
168    Uninstall,
169    /// Tail daemon service logs
170    Logs {
171        /// Number of lines to show (default: 50)
172        #[arg(short = 'n', long, default_value = "50")]
173        lines: usize,
174        /// Follow log output (like tail -f)
175        #[arg(short, long)]
176        follow: bool,
177    },
178}
179
180/// Channel management subcommands
181#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
182pub enum ChannelCommands {
183    /// List all configured channels
184    List,
185    /// Start all configured channels (handled in main.rs for async)
186    Start,
187    /// Run health checks for configured channels (handled in main.rs for async)
188    Doctor,
189    /// Add a new channel configuration
190    #[command(long_about = "\
191Add a new channel configuration.
192
193Provide the channel type and a JSON object with the required \
194configuration keys for that channel type.
195
196Supported types: telegram, discord, slack, whatsapp, matrix, imessage, email.
197
198Examples:
199  zeroclaw channel add telegram '{\"bot_token\":\"...\",\"name\":\"my-bot\"}'
200  zeroclaw channel add discord '{\"bot_token\":\"...\",\"name\":\"my-discord\"}'")]
201    Add {
202        /// Channel type (telegram, discord, slack, whatsapp, matrix, imessage, email)
203        channel_type: String,
204        /// Optional configuration as JSON
205        config: String,
206    },
207    /// Remove a channel configuration
208    Remove {
209        /// Channel name to remove
210        name: String,
211    },
212    /// Bind a Telegram identity (username or numeric user ID) into allowlist
213    #[command(long_about = "\
214Bind a Telegram identity into the allowlist.
215
216Adds a Telegram username (without the '@' prefix) or numeric user \
217ID to the channel allowlist so the agent will respond to messages \
218from that identity.
219
220Examples:
221  zeroclaw channel bind-telegram zeroclaw_user
222  zeroclaw channel bind-telegram 123456789")]
223    BindTelegram {
224        /// Telegram identity to allow (username without '@' or numeric user ID)
225        identity: String,
226    },
227    /// Send a message to a configured channel
228    #[command(long_about = "\
229Send a one-off message to a configured channel.
230
231Sends a text message through the specified channel without starting \
232the full agent loop. Useful for scripted notifications, hardware \
233sensor alerts, and automation pipelines.
234
235The --channel-id selects the channel by its config section name \
236(e.g. 'telegram', 'discord', 'slack'). The --recipient is the \
237platform-specific destination (e.g. a Telegram chat ID).
238
239Examples:
240  zeroclaw channel send 'Someone is near your device.' --channel-id telegram --recipient 123456789
241  zeroclaw channel send 'Build succeeded!' --channel-id discord --recipient 987654321")]
242    Send {
243        /// Message text to send
244        message: String,
245        /// Channel config name (e.g. telegram, discord, slack)
246        #[arg(long)]
247        channel_id: String,
248        /// Recipient identifier (platform-specific, e.g. Telegram chat ID)
249        #[arg(long)]
250        recipient: String,
251    },
252}
253
254/// Skills management subcommands
255#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
256pub enum SkillCommands {
257    /// List all installed skills
258    List,
259    /// Audit a skill source directory or installed skill name
260    Audit {
261        /// Skill path or installed skill name
262        source: String,
263    },
264    /// Install a new skill from a URL or local path
265    Install {
266        /// Source URL or local path
267        source: String,
268    },
269    /// Remove an installed skill
270    Remove {
271        /// Skill name to remove
272        name: String,
273    },
274    /// Run TEST.sh validation for a skill (or all skills)
275    Test {
276        /// Skill name to test; omit for all skills
277        name: Option<String>,
278        /// Show verbose output
279        #[arg(long)]
280        verbose: bool,
281    },
282}
283
284/// Migration subcommands
285#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
286pub enum MigrateCommands {
287    /// Import memory from an `OpenClaw` workspace into this `ZeroClaw` workspace
288    Openclaw {
289        /// Optional path to `OpenClaw` workspace (defaults to ~/.openclaw/workspace)
290        #[arg(long)]
291        source: Option<std::path::PathBuf>,
292
293        /// Validate and preview migration without writing any data
294        #[arg(long)]
295        dry_run: bool,
296    },
297}
298
299/// Cron subcommands
300#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
301pub enum CronCommands {
302    /// List all scheduled tasks
303    List,
304    /// Add a new scheduled task
305    #[command(long_about = "\
306Add a new recurring scheduled task.
307
308Uses standard 5-field cron syntax: 'min hour day month weekday'. \
309Times are evaluated in UTC by default; use --tz with an IANA \
310timezone name to override.
311
312Examples:
313  zeroclaw cron add '0 9 * * 1-5' 'Good morning' --tz America/New_York --agent
314  zeroclaw cron add '*/30 * * * *' 'Check system health' --agent
315  zeroclaw cron add '*/5 * * * *' 'echo ok'")]
316    Add {
317        /// Cron expression
318        expression: String,
319        /// Optional IANA timezone (e.g. America/Los_Angeles)
320        #[arg(long)]
321        tz: Option<String>,
322        /// Treat the argument as an agent prompt instead of a shell command
323        #[arg(long)]
324        agent: bool,
325        /// Restrict agent cron jobs to the specified tool names (repeatable, agent-only)
326        #[arg(long = "allowed-tool")]
327        allowed_tools: Vec<String>,
328        /// Command (shell) or prompt (agent) to run
329        command: String,
330    },
331    /// Add a one-shot scheduled task at an RFC3339 timestamp
332    #[command(long_about = "\
333Add a one-shot task that fires at a specific UTC timestamp.
334
335The timestamp must be in RFC 3339 format (e.g. 2025-01-15T14:00:00Z).
336
337Examples:
338  zeroclaw cron add-at 2025-01-15T14:00:00Z 'Send reminder'
339  zeroclaw cron add-at 2025-12-31T23:59:00Z 'Happy New Year!'")]
340    AddAt {
341        /// One-shot timestamp in RFC3339 format
342        at: String,
343        /// Treat the argument as an agent prompt instead of a shell command
344        #[arg(long)]
345        agent: bool,
346        /// Restrict agent cron jobs to the specified tool names (repeatable, agent-only)
347        #[arg(long = "allowed-tool")]
348        allowed_tools: Vec<String>,
349        /// Command (shell) or prompt (agent) to run
350        command: String,
351    },
352    /// Add a fixed-interval scheduled task
353    #[command(long_about = "\
354Add a task that repeats at a fixed interval.
355
356Interval is specified in milliseconds. For example, 60000 = 1 minute.
357
358Examples:
359  zeroclaw cron add-every 60000 'Ping heartbeat'     # every minute
360  zeroclaw cron add-every 3600000 'Hourly report'    # every hour")]
361    AddEvery {
362        /// Interval in milliseconds
363        every_ms: u64,
364        /// Treat the argument as an agent prompt instead of a shell command
365        #[arg(long)]
366        agent: bool,
367        /// Restrict agent cron jobs to the specified tool names (repeatable, agent-only)
368        #[arg(long = "allowed-tool")]
369        allowed_tools: Vec<String>,
370        /// Command (shell) or prompt (agent) to run
371        command: String,
372    },
373    /// Add a one-shot delayed task (e.g. "30m", "2h", "1d")
374    #[command(long_about = "\
375Add a one-shot task that fires after a delay from now.
376
377Accepts human-readable durations: s (seconds), m (minutes), \
378h (hours), d (days).
379
380Examples:
381  zeroclaw cron once 30m 'Run backup in 30 minutes'
382  zeroclaw cron once 2h 'Follow up on deployment'
383  zeroclaw cron once 1d 'Daily check'")]
384    Once {
385        /// Delay duration
386        delay: String,
387        /// Treat the argument as an agent prompt instead of a shell command
388        #[arg(long)]
389        agent: bool,
390        /// Restrict agent cron jobs to the specified tool names (repeatable, agent-only)
391        #[arg(long = "allowed-tool")]
392        allowed_tools: Vec<String>,
393        /// Command (shell) or prompt (agent) to run
394        command: String,
395    },
396    /// Remove a scheduled task
397    Remove {
398        /// Task ID
399        id: String,
400    },
401    /// Update a scheduled task
402    #[command(long_about = "\
403Update one or more fields of an existing scheduled task.
404
405Only the fields you specify are changed; others remain unchanged.
406
407Examples:
408  zeroclaw cron update <task-id> --expression '0 8 * * *'
409  zeroclaw cron update <task-id> --tz Europe/London --name 'Morning check'
410  zeroclaw cron update <task-id> --command 'Updated message'")]
411    Update {
412        /// Task ID
413        id: String,
414        /// New cron expression
415        #[arg(long)]
416        expression: Option<String>,
417        /// New IANA timezone
418        #[arg(long)]
419        tz: Option<String>,
420        /// New command to run
421        #[arg(long)]
422        command: Option<String>,
423        /// New job name
424        #[arg(long)]
425        name: Option<String>,
426        /// Replace the agent job allowlist with the specified tool names (repeatable)
427        #[arg(long = "allowed-tool")]
428        allowed_tools: Vec<String>,
429    },
430    /// Pause a scheduled task
431    Pause {
432        /// Task ID
433        id: String,
434    },
435    /// Resume a paused task
436    Resume {
437        /// Task ID
438        id: String,
439    },
440}
441
442/// Memory management subcommands
443#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
444pub enum MemoryCommands {
445    /// List memory entries with optional filters
446    List {
447        /// Filter by category (core, daily, conversation, or custom name)
448        #[arg(long)]
449        category: Option<String>,
450        /// Filter by session ID
451        #[arg(long)]
452        session: Option<String>,
453        /// Maximum number of entries to display
454        #[arg(long, default_value = "50")]
455        limit: usize,
456        /// Number of entries to skip (for pagination)
457        #[arg(long, default_value = "0")]
458        offset: usize,
459    },
460    /// Get a specific memory entry by key
461    Get {
462        /// Memory key to look up
463        key: String,
464    },
465    /// Show memory backend statistics and health
466    Stats,
467    /// Clear memories by category, by key, or clear all
468    Clear {
469        /// Delete a single entry by key (supports prefix match)
470        #[arg(long)]
471        key: Option<String>,
472        /// Only clear entries in this category
473        #[arg(long)]
474        category: Option<String>,
475        /// Skip confirmation prompt
476        #[arg(long)]
477        yes: bool,
478    },
479}
480
481/// Integration subcommands
482#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
483pub enum IntegrationCommands {
484    /// Show details about a specific integration
485    Info {
486        /// Integration name
487        name: String,
488    },
489}
490
491/// Hardware discovery subcommands
492#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
493pub enum HardwareCommands {
494    /// Enumerate USB devices (VID/PID) and show known boards
495    #[command(long_about = "\
496Enumerate USB devices and show known boards.
497
498Scans connected USB devices by VID/PID and matches them against \
499known development boards (STM32 Nucleo, Arduino, ESP32).
500
501Examples:
502  zeroclaw hardware discover")]
503    Discover,
504    /// Introspect a device by path (e.g. /dev/ttyACM0)
505    #[command(long_about = "\
506Introspect a device by its serial or device path.
507
508Opens the specified device path and queries for board information, \
509firmware version, and supported capabilities.
510
511Examples:
512  zeroclaw hardware introspect /dev/ttyACM0
513  zeroclaw hardware introspect COM3")]
514    Introspect {
515        /// Serial or device path
516        path: String,
517    },
518    /// Get chip info via USB (probe-rs over ST-Link). No firmware needed on target.
519    #[command(long_about = "\
520Get chip info via USB using probe-rs over ST-Link.
521
522Queries the target MCU directly through the debug probe without \
523requiring any firmware on the target board.
524
525Examples:
526  zeroclaw hardware info
527  zeroclaw hardware info --chip STM32F401RETx")]
528    Info {
529        /// Chip name (e.g. STM32F401RETx). Default: STM32F401RETx for Nucleo-F401RE
530        #[arg(long, default_value = "STM32F401RETx")]
531        chip: String,
532    },
533}
534
535/// Peripheral (hardware) management subcommands
536#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
537pub enum PeripheralCommands {
538    /// List configured peripherals
539    List,
540    /// Add a peripheral (board path, e.g. nucleo-f401re /dev/ttyACM0)
541    #[command(long_about = "\
542Add a peripheral by board type and transport path.
543
544Registers a hardware board so the agent can use its tools (GPIO, \
545sensors, actuators). Use 'native' as path for local GPIO on \
546single-board computers like Raspberry Pi.
547
548Supported boards: nucleo-f401re, rpi-gpio, esp32, arduino-uno.
549
550Examples:
551  zeroclaw peripheral add nucleo-f401re /dev/ttyACM0
552  zeroclaw peripheral add rpi-gpio native
553  zeroclaw peripheral add esp32 /dev/ttyUSB0")]
554    Add {
555        /// Board type (nucleo-f401re, rpi-gpio, esp32)
556        board: String,
557        /// Path for serial transport (/dev/ttyACM0) or "native" for local GPIO
558        path: String,
559    },
560    /// Flash ZeroClaw firmware to Arduino (creates .ino, installs arduino-cli if needed, uploads)
561    #[command(long_about = "\
562Flash ZeroClaw firmware to an Arduino board.
563
564Generates the .ino sketch, installs arduino-cli if it is not \
565already available, compiles, and uploads the firmware.
566
567Examples:
568  zeroclaw peripheral flash
569  zeroclaw peripheral flash --port /dev/cu.usbmodem12345
570  zeroclaw peripheral flash -p COM3")]
571    Flash {
572        /// Serial port (e.g. /dev/cu.usbmodem12345). If omitted, uses first arduino-uno from config.
573        #[arg(short, long)]
574        port: Option<String>,
575    },
576    /// Setup Arduino Uno Q Bridge app (deploy GPIO bridge for agent control)
577    SetupUnoQ {
578        /// Uno Q IP (e.g. 192.168.0.48). If omitted, assumes running ON the Uno Q.
579        #[arg(long)]
580        host: Option<String>,
581    },
582    /// Flash ZeroClaw firmware to Nucleo-F401RE (builds + probe-rs run)
583    FlashNucleo,
584}
585
586/// SOP management subcommands
587#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
588pub enum SopCommands {
589    /// List loaded SOPs
590    List,
591    /// Validate SOP definitions
592    Validate {
593        /// SOP name to validate (all if omitted)
594        name: Option<String>,
595    },
596    /// Show details of an SOP
597    Show {
598        /// Name of the SOP to show
599        name: String,
600    },
601}