1use anyhow::Result;
8use clap::Subcommand;
9use tracing::info;
10
11#[derive(Subcommand)]
12pub enum RelayCommand {
13 Auth {
16 #[arg(long, default_value = "https://broomva.tech")]
18 url: String,
19 },
20 Start {
22 #[arg(long, default_value = "127.0.0.1:3004")]
24 bind: String,
25 #[arg(long, default_value = "https://broomva.tech")]
27 server: String,
28 },
29 Stop,
31 Status,
33}
34
35pub async fn run(command: RelayCommand) -> Result<()> {
36 match command {
37 RelayCommand::Auth { url } => {
38 let cfg = life_relayd::config::load_config()?;
39
40 match life_relayd::config::read_token(&cfg) {
42 Ok(_) => {
43 println!(" Already authenticated (token found).");
44 println!(" Source: broomva CLI or relay credentials.");
45 println!();
46 println!(" To re-authenticate, run `life relay auth --url {url}`");
47 println!(" with a fresh device code flow.");
48 }
49 Err(_) => {
50 info!(url = %url, "starting device authorization");
51 life_relayd::auth::run(&url, &cfg.credentials_path()).await?;
52 }
53 }
54 }
55 RelayCommand::Start { bind, server } => {
56 info!(bind = %bind, server = %server, "starting relay daemon");
57 life_relayd::daemon::run(&bind, &server).await?;
58 }
59 RelayCommand::Stop => {
60 let client = reqwest::Client::new();
62 match client
63 .get("http://127.0.0.1:3004/health")
64 .timeout(std::time::Duration::from_secs(2))
65 .send()
66 .await
67 {
68 Ok(_) => {
69 println!(" Relay daemon is running but graceful stop is not yet implemented.");
70 println!(" Use `pkill -f life-relayd` or `kill $(lsof -ti :3004)` to stop.");
71 }
72 Err(_) => {
73 println!(" No relay daemon running on port 3004.");
74 }
75 }
76 }
77 RelayCommand::Status => {
78 let cfg = life_relayd::config::load_config()?;
79 let has_token = life_relayd::config::read_token(&cfg).is_ok();
80
81 println!(" Relay Configuration");
82 println!(" ───────────────────");
83 println!(" Config dir: {}", cfg.config_dir.display());
84 println!(" Authenticated: {}", if has_token { "yes" } else { "no" });
85
86 let client = reqwest::Client::new();
88 match client
89 .get("http://127.0.0.1:3004/health")
90 .timeout(std::time::Duration::from_secs(2))
91 .send()
92 .await
93 {
94 Ok(resp) if resp.status().is_success() => {
95 println!(" Daemon: running (port 3004)");
96 if let Ok(body) = resp.json::<serde_json::Value>().await {
97 if let Some(v) = body.get("version").and_then(|v| v.as_str()) {
98 println!(" Version: {v}");
99 }
100 }
101 }
102 _ => {
103 println!(" Daemon: not running");
104 }
105 }
106
107 if !has_token {
108 println!();
109 println!(" Run `life relay auth` or `broomva auth login` to authenticate.");
110 }
111 }
112 }
113
114 Ok(())
115}