feat(tools): notify — the first client tool
Desktop notifications are the wave-1 built-in the resident has no native way to send: the daemon's pushes land in the poll queue, and this is the program that turns one into an OS notification — `notify push --title --body [--device]` on the shared plumbing, the claw's `POST /v1/notifications` on the far end. The binary is the template for every later client: connection from the environment the daemon provisions, one contract-headered call, JSON or error shape, exit code. The e2e test drives the real binary against a mock claw — a wrong token is a 401 with the code to match, a consent route is a 403, a good push prints the row — so a packaging regression cannot pass silently. `skills/` ships the markdown that teaches the agent the tool exists and what its answers mean.
This commit is contained in:
1 parent
29099add9d
commit
2fbd93fd79
7 files changed
+1199
No files matched your search
@@ -0,0 +1,58 @@
|
||||
//! The `notify` client: push one notification to a device. The daemon
|
||||
//! queues the row; the device's next poll-and-ack carries it. One
|
||||
//! subcommand, one call, one JSON line out — the thin-client shape
|
||||
//! every rhema tool follows (spec: rhema-tools §3).
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use rhema_tools_common::{Client, Config, fail, print};
|
||||
use rhema_tools_contract::NotificationPush;
|
||||
|
||||
/// Push a notification to the user's devices.
|
||||
#[derive(Parser)]
|
||||
#[command(name = "notify", version, about, long_about = None)]
|
||||
struct Cli {
|
||||
/// Present for readability of examples; output is always JSON.
|
||||
#[arg(long)]
|
||||
json: bool,
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Command {
|
||||
/// Push one notification: the daemon queues it, the device's next
|
||||
/// poll-and-ack delivers it.
|
||||
Push {
|
||||
/// The notification's title line.
|
||||
#[arg(long)]
|
||||
title: String,
|
||||
/// The notification's body text.
|
||||
#[arg(long)]
|
||||
body: String,
|
||||
/// The device to deliver to; the daemon's default when unset.
|
||||
#[arg(long)]
|
||||
device: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let Cli { json: _, command } = Cli::parse();
|
||||
let client = Client::new(Config::from_env());
|
||||
match command {
|
||||
Command::Push {
|
||||
title,
|
||||
body,
|
||||
device,
|
||||
} => {
|
||||
let push = NotificationPush {
|
||||
title,
|
||||
body,
|
||||
device,
|
||||
};
|
||||
match client.call("POST", rhema_tools_contract::NOTIFICATIONS, Some(&push)) {
|
||||
Ok(record) => print(&record),
|
||||
Err(error) => fail(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user