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)]
36
37use clap::Subcommand;
38use serde::{Deserialize, Serialize};
39
40#[cfg(feature = "agent-runtime")]
41pub mod agent;
42#[cfg(feature = "agent-runtime")]
43pub(crate) mod approval;
44#[cfg(feature = "agent-runtime")]
45pub(crate) mod auth;
46#[cfg(feature = "agent-runtime")]
47pub mod channels;
48pub mod commands;
49pub mod config;
50#[cfg(feature = "agent-runtime")]
51pub(crate) mod cost;
52#[cfg(feature = "agent-runtime")]
53pub mod cron;
54#[cfg(feature = "agent-runtime")]
55pub(crate) mod daemon;
56#[cfg(feature = "agent-runtime")]
57pub(crate) mod doctor;
58#[cfg(feature = "gateway")]
59pub mod gateway;
60#[cfg(feature = "agent-runtime")]
61pub(crate) mod hardware;
62#[cfg(feature = "agent-runtime")]
63pub(crate) mod health;
64#[cfg(feature = "agent-runtime")]
65pub(crate) mod heartbeat;
66#[cfg(feature = "agent-runtime")]
67pub mod hooks;
68#[cfg(feature = "agent-runtime")]
69pub(crate) mod integrations;
70pub mod memory;
71#[cfg(feature = "agent-runtime")]
72pub(crate) mod multimodal;
73#[cfg(feature = "agent-runtime")]
74pub mod nodes;
75#[cfg(feature = "agent-runtime")]
76pub mod observability;
77#[cfg(feature = "agent-runtime")]
78pub mod peripherals;
79#[cfg(feature = "agent-runtime")]
80pub mod platform;
81pub mod providers;
82#[cfg(feature = "agent-runtime")]
83pub mod rag;
84#[cfg(feature = "agent-runtime")]
85pub mod routines;
86#[cfg(feature = "agent-runtime")]
87pub(crate) mod security;
88#[cfg(feature = "agent-runtime")]
89pub(crate) mod service;
90#[cfg(feature = "agent-runtime")]
91pub(crate) mod skills;
92#[cfg(feature = "agent-runtime")]
93pub mod sop;
94#[cfg(feature = "agent-runtime")]
95pub mod tools;
96#[cfg(feature = "agent-runtime")]
97pub(crate) mod trust;
98#[cfg(feature = "agent-runtime")]
99pub(crate) mod tunnel;
100#[cfg(feature = "agent-runtime")]
101pub mod verifiable_intent;
102
103#[cfg(feature = "plugins-wasm")]
104pub mod plugins;
105
106pub use config::Config;
107
108#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
110pub enum GatewayCommands {
111 #[command(long_about = "\
113Start the gateway server (webhooks, websockets).
114
115Runs the HTTP/WebSocket gateway that accepts incoming webhook events \
116and WebSocket connections. Bind address defaults to the values in \
117your config file (gateway.host / gateway.port).
118
119Examples:
120 zeroclaw gateway start # use config defaults
121 zeroclaw gateway start -p 8080 # listen on port 8080
122 zeroclaw gateway start --host 0.0.0.0 # requires [gateway].allow_public_bind=true or a tunnel
123 zeroclaw gateway start -p 0 # random available port")]
124 Start {
125 #[arg(short, long)]
127 port: Option<u16>,
128
129 #[arg(long)]
132 host: Option<String>,
133 },
134 #[command(long_about = "\
136Restart the gateway server.
137
138Stops the running gateway if present, then starts a new instance \
139with the current configuration.
140
141Examples:
142 zeroclaw gateway restart # restart with config defaults
143 zeroclaw gateway restart -p 8080 # restart on port 8080")]
144 Restart {
145 #[arg(short, long)]
147 port: Option<u16>,
148
149 #[arg(long)]
152 host: Option<String>,
153 },
154 #[command(long_about = "\
156Show or generate the gateway pairing code.
157
158Displays the pairing code for connecting new clients without \
159restarting the gateway. Requires the gateway to be running.
160
161With --new, generates a fresh pairing code even if the gateway \
162was previously paired (useful for adding additional clients).
163
164Examples:
165 zeroclaw gateway get-paircode # show current pairing code
166 zeroclaw gateway get-paircode --new # generate a new pairing code
167 zeroclaw gateway get-paircode --new --port 3001 # target alternate-port gateway")]
168 GetPaircode {
169 #[arg(long)]
171 new: bool,
172
173 #[arg(short, long)]
175 port: Option<u16>,
176
177 #[arg(long)]
179 host: Option<String>,
180 },
181}
182
183#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
185pub enum ServiceCommands {
186 Install,
188 Start,
190 Stop,
192 Restart,
194 Status,
196 Uninstall,
198 Logs {
200 #[arg(short = 'n', long, default_value = "50")]
202 lines: usize,
203 #[arg(short, long)]
205 follow: bool,
206 },
207}
208
209#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
211pub enum ChannelCommands {
212 List,
214 Start,
216 Doctor,
218 #[command(long_about = "\
220Add a new channel configuration.
221
222Provide the channel type and a JSON object with the required \
223configuration keys for that channel type.
224
225Supported types: telegram, discord, slack, whatsapp, matrix, imessage, email.
226
227Examples:
228 zeroclaw channel add telegram '{\"bot_token\":\"...\",\"name\":\"my-bot\"}'
229 zeroclaw channel add discord '{\"bot_token\":\"...\",\"name\":\"my-discord\"}'")]
230 Add {
231 channel_type: String,
233 config: String,
235 },
236 Remove {
238 name: String,
240 },
241 #[command(long_about = "\
243Bind a Telegram identity into the allowlist.
244
245Adds a Telegram username (without the '@' prefix) or numeric user \
246ID to the channel allowlist so the agent will respond to messages \
247from that identity.
248
249Examples:
250 zeroclaw channel bind-telegram zeroclaw_user
251 zeroclaw channel bind-telegram 123456789")]
252 BindTelegram {
253 identity: String,
255 },
256 #[command(long_about = "\
258Send a one-off message to a configured channel.
259
260Sends a text message through the specified channel without starting \
261the full agent loop. Useful for scripted notifications, hardware \
262sensor alerts, and automation pipelines.
263
264The --channel-id selects the channel by its config section name \
265(e.g. 'telegram', 'discord', 'slack'). The --recipient is the \
266platform-specific destination (e.g. a Telegram chat ID).
267
268Examples:
269 zeroclaw channel send 'Someone is near your device.' --channel-id telegram --recipient 123456789
270 zeroclaw channel send 'Build succeeded!' --channel-id discord --recipient 987654321")]
271 Send {
272 message: String,
274 #[arg(long)]
276 channel_id: String,
277 #[arg(long)]
279 recipient: String,
280 },
281}
282
283#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
285pub enum SkillCommands {
286 List,
288 #[command(long_about = "\
290Scaffold a new skill under a skill bundle. Writes <bundle.directory>/<name>/SKILL.md \
291plus the canonical optional subdirs (scripts/, references/, assets/). \
292Name must be lowercase + hyphens; description is required (prompted on TTY if omitted).
293
294Examples:
295 zeroclaw skills add code-review --bundle official --description \"Review PRs.\"
296 zeroclaw skills add ops-runbook --description \"Triage prod incidents.\" --edit")]
297 Add {
298 name: String,
300 #[arg(long)]
302 bundle: Option<String>,
303 #[arg(long)]
306 description: Option<String>,
307 #[arg(long)]
309 license: Option<String>,
310 #[arg(long)]
312 author: Option<String>,
313 #[arg(long)]
315 version: Option<String>,
316 #[arg(long)]
318 category: Option<String>,
319 #[arg(long)]
321 no_scaffold: bool,
322 #[arg(long)]
324 edit: bool,
325 },
326 Edit {
328 name: String,
330 #[arg(long)]
332 bundle: Option<String>,
333 #[arg(long)]
335 file: Option<String>,
336 },
337 Bundle {
339 #[command(subcommand)]
340 bundle_command: SkillBundleCommands,
341 },
342 Audit {
344 source: String,
346 },
347 Install {
349 source: String,
351 #[arg(long)]
354 no_tier_banner: bool,
355 },
356 Remove {
358 name: String,
360 },
361 Test {
363 name: Option<String>,
365 #[arg(long)]
367 verbose: bool,
368 },
369}
370
371#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
373pub enum SkillBundleCommands {
374 List,
376 Add {
378 alias: String,
380 #[arg(long)]
383 directory: Option<String>,
384 },
385 Remove {
387 alias: String,
389 },
390 Show {
392 alias: String,
394 },
395}
396
397#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
399pub enum MigrateCommands {
400 Openclaw {
402 #[arg(long)]
404 source: Option<std::path::PathBuf>,
405
406 #[arg(long)]
408 dry_run: bool,
409 },
410}
411
412#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
414pub enum CronCommands {
415 List,
417 #[command(long_about = "\
419Add a new recurring scheduled task.
420
421Uses standard 5-field cron syntax: 'min hour day month weekday'. \
422When --tz is omitted, cron schedules use the runtime local timezone. \
423For user-facing schedules, pass --tz with an explicit IANA timezone.
424
425Examples:
426 zeroclaw cron add '0 9 * * 1-5' 'Good morning' --tz America/New_York --agent
427 zeroclaw cron add '*/30 * * * *' 'Check system health' --agent
428 zeroclaw cron add '*/5 * * * *' 'echo ok'")]
429 Add {
430 expression: String,
432 #[arg(short = 'a', long = "agent")]
435 agent_alias: String,
436 #[arg(long)]
438 tz: Option<String>,
439 #[arg(long)]
441 prompt: bool,
442 #[arg(long = "allowed-tool")]
444 allowed_tools: Vec<String>,
445 command: String,
447 },
448 #[command(long_about = "\
450Add a one-shot task that fires at a specific RFC3339 timestamp with explicit Z or offset.
451
452The timestamp must include an explicit Z or numeric offset \
453(e.g. 2025-01-15T14:00:00Z or 2025-01-15T09:00:00-05:00).
454
455Examples:
456 zeroclaw cron add-at --agent morning-shift 2025-01-15T14:00:00Z 'Send reminder'
457 zeroclaw cron add-at --agent morning-shift --prompt 2025-12-31T23:59:00Z 'Happy New Year!'")]
458 AddAt {
459 at: String,
461 #[arg(short = 'a', long = "agent")]
463 agent_alias: String,
464 #[arg(long)]
466 prompt: bool,
467 #[arg(long = "allowed-tool")]
469 allowed_tools: Vec<String>,
470 command: String,
472 },
473 #[command(long_about = "\
475Add a task that repeats at a fixed interval.
476
477Interval is specified in milliseconds. For example, 60000 = 1 minute.
478
479Examples:
480 zeroclaw cron add-every --agent triage 60000 'Ping heartbeat'
481 zeroclaw cron add-every --agent triage 3600000 'Hourly report'")]
482 AddEvery {
483 every_ms: u64,
485 #[arg(short = 'a', long = "agent")]
487 agent_alias: String,
488 #[arg(long)]
490 prompt: bool,
491 #[arg(long = "allowed-tool")]
493 allowed_tools: Vec<String>,
494 command: String,
496 },
497 #[command(long_about = "\
499Add a one-shot task that fires after a delay from now.
500
501Accepts human-readable durations: s (seconds), m (minutes), \
502h (hours), d (days).
503
504Examples:
505 zeroclaw cron once --agent ops-bot 30m 'Run backup in 30 minutes'
506 zeroclaw cron once --agent researcher --prompt 2h 'Follow up on deployment'")]
507 Once {
508 delay: String,
510 #[arg(short = 'a', long = "agent")]
512 agent_alias: String,
513 #[arg(long)]
515 prompt: bool,
516 #[arg(long = "allowed-tool")]
518 allowed_tools: Vec<String>,
519 command: String,
521 },
522 Remove {
524 id: String,
526 },
527 #[command(long_about = "\
529Update one or more fields of an existing scheduled task.
530
531Only the fields you specify are changed; others remain unchanged.
532
533Examples:
534 zeroclaw cron update TASK_ID --expression '0 8 * * *'
535 zeroclaw cron update TASK_ID --tz Europe/London --name 'Morning check'
536 zeroclaw cron update TASK_ID --command 'Updated message'")]
537 Update {
538 id: String,
540 #[arg(short = 'a', long = "agent")]
543 agent_alias: String,
544 #[arg(long)]
546 expression: Option<String>,
547 #[arg(long)]
549 tz: Option<String>,
550 #[arg(long)]
552 command: Option<String>,
553 #[arg(long)]
555 name: Option<String>,
556 #[arg(long = "allowed-tool")]
558 allowed_tools: Vec<String>,
559 },
560 Pause {
562 id: String,
564 },
565 Resume {
567 id: String,
569 },
570}
571
572#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
574pub enum MemoryCommands {
575 List {
577 #[arg(long)]
579 category: Option<String>,
580 #[arg(long)]
582 session: Option<String>,
583 #[arg(long, default_value = "50")]
585 limit: usize,
586 #[arg(long, default_value = "0")]
588 offset: usize,
589 },
590 Get {
592 key: String,
594 },
595 Stats,
597 Clear {
599 #[arg(long)]
601 key: Option<String>,
602 #[arg(long)]
604 category: Option<String>,
605 #[arg(long)]
607 yes: bool,
608 },
609 Reindex,
616}
617
618#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
620pub enum IntegrationCommands {
621 Info {
623 name: String,
625 },
626}
627
628#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
630pub enum HardwareCommands {
631 #[command(long_about = "\
633Enumerate USB devices and show known boards.
634
635Scans connected USB devices by VID/PID and matches them against \
636known development boards (STM32 Nucleo, Arduino, ESP32).
637
638Examples:
639 zeroclaw hardware discover")]
640 Discover,
641 #[command(long_about = "\
643Introspect a device by its serial or device path.
644
645Opens the specified device path and queries for board information, \
646firmware version, and supported capabilities.
647
648Examples:
649 zeroclaw hardware introspect /dev/ttyACM0
650 zeroclaw hardware introspect COM3")]
651 Introspect {
652 path: String,
654 },
655 #[command(long_about = "\
657Get chip info via USB using probe-rs over ST-Link.
658
659Queries the target MCU directly through the debug probe without \
660requiring any firmware on the target board.
661
662Examples:
663 zeroclaw hardware info
664 zeroclaw hardware info --chip STM32F401RETx")]
665 Info {
666 #[arg(long, default_value = "STM32F401RETx")]
668 chip: String,
669 },
670}
671
672#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
674pub enum PeripheralCommands {
675 List,
677 #[command(long_about = "\
679Add a peripheral by board type and transport path.
680
681Registers a hardware board so the agent can use its tools (GPIO, \
682sensors, actuators). Use 'native' as path for local GPIO on \
683single-board computers like Raspberry Pi.
684
685Supported boards: nucleo-f401re, rpi-gpio, esp32, arduino-uno.
686
687Examples:
688 zeroclaw peripheral add nucleo-f401re /dev/ttyACM0
689 zeroclaw peripheral add rpi-gpio native
690 zeroclaw peripheral add esp32 /dev/ttyUSB0")]
691 Add {
692 board: String,
694 path: String,
696 },
697 #[command(long_about = "\
699Flash ZeroClaw firmware to an Arduino board.
700
701Generates the .ino sketch, installs arduino-cli if it is not \
702already available, compiles, and uploads the firmware.
703
704Examples:
705 zeroclaw peripheral flash
706 zeroclaw peripheral flash --port /dev/cu.usbmodem12345
707 zeroclaw peripheral flash -p COM3")]
708 Flash {
709 #[arg(short, long)]
711 port: Option<String>,
712 },
713 SetupUnoQ {
715 #[arg(long)]
717 host: Option<String>,
718 },
719 FlashNucleo,
721}
722
723#[derive(Subcommand, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
725pub enum SopCommands {
726 List,
728 Validate {
730 name: Option<String>,
732 },
733 Show {
735 name: String,
737 },
738}