Skip to main content

mail4agent_messenger_shell/cli/
mod.rs

1//! The client's commands. They used to be eight binaries; they are subcommands of ONE binary,
2//! `m4a-agent <command> ...`, and the old binary names remain as thin wrappers over the same
3//! `run` functions for one transition release (service units keep working; update them to
4//! `m4a-agent <command>` at leisure).
5//!
6//! | `m4a-agent`            | old binary                 |
7//! | ---------------------- | -------------------------- |
8//! | `web-client`           | `m4a-web-client`           |
9//! | `grok-listen`          | `m4a-grok-listen`          |
10//! | `inbox`                | `m4a-inbox`                |
11//! | `send`                 | `m4a-send`                 |
12//! | `mail`                 | `m4a`                      |
13//! | `ensure-agent-webhooks`| `m4a-ensure-agent-webhooks`|
14//! | `enroll`               | —                          |
15//! | `wake`                 | — (owner control of wakes) |
16//! | `vault`                | — (status, backup, restore)|
17//! | `element-open`         | — (open Element, logged in)|
18//! | `door-listen`          | — (wake on hatchery node mail)|
19
20pub mod enroll;
21pub mod session_arg;
22pub mod vault;
23pub mod wake;
24#[cfg(feature = "element-open")]
25pub mod element_open;
26pub mod ensure_agent_webhooks;
27pub mod grok_listen;
28pub mod inbox;
29pub mod mail;
30pub mod send;
31pub mod web_client;
32pub mod door_listen;
33pub mod launch;
34pub mod account;
35mod letter;
36
37/// The subcommand names, in the order of the table above.
38pub const COMMANDS: &[&str] = &["web-client", "grok-listen", "inbox", "send", "mail", "ensure-agent-webhooks", "enroll", "wake", "vault", "element-open", "door-listen", "launch", "account", "letter"];
39
40/// Runs `command` with its arguments. `Err` names an unknown command (or one this build lacks).
41pub fn dispatch(command: &str, args: Vec<String>) -> Result<(), String> {
42    match command {
43        "web-client" => web_client::run(args),
44        "grok-listen" => grok_listen::run(args),
45        "node-client" | "claude-channel" => {
46            return Err(format!("{command} moved out of m4a: session delivery is g4a's (hatchery node / gate4agent-wake)"))
47        }
48        "inbox" => inbox::run(args),
49        "send" => send::run(args),
50        "mail" => mail::run(args),
51        "ensure-agent-webhooks" => ensure_agent_webhooks::run(args),
52        "enroll" => enroll::run(args),
53        "wake" => wake::run(args),
54        "vault" => vault::run(args),
55        "door-listen" => door_listen::run(args),
56        "launch" => launch::run(args),
57        "account" => account::run(args),
58        "letter" => letter::run(args),
59        #[cfg(feature = "element-open")]
60        "element-open" => element_open::run(args),
61        #[cfg(not(feature = "element-open"))]
62        "element-open" => return Err("this build was made without feature element-open".to_string()),
63        other => return Err(format!("unknown command {other:?}; commands: {}", COMMANDS.join(", "))),
64    }
65    Ok(())
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn an_unknown_command_is_named_not_run() {
74        let e = dispatch("nope", vec![]).unwrap_err();
75        assert!(e.contains("nope") && e.contains("web-client") && e.contains("mail"));
76    }
77
78    #[test]
79    fn enroll_is_a_command() {
80        assert!(COMMANDS.contains(&"enroll"));
81    }
82}