//! sb — Substrate CLI //! //! Currently supports: //! sb shell [--host HOST] [--port PORT] [--user USER] //! //! Connects to the bascule-agent's governed SSH shell. use std::process::Command; use clap::{Parser, Subcommand}; #[derive(Parser)] #[command(name = "sb", about = "Substrate CLI", version)] struct Cli { #[command(subcommand)] command: SubCmd, } #[derive(Subcommand)] enum SubCmd { /// Connect to the bascule-agent governed shell via SSH. Shell { /// SSH host to connect to. #[arg(long, default_value = "localhost")] host: String, /// SSH port. #[arg(short, long, default_value = "2222")] port: u16, /// SSH username. #[arg(short, long, default_value_t = whoami())] user: String, /// SSH identity file (private key). #[arg(short, long)] identity: Option, }, /// Show agent status via IPC socket. Status { /// Path to the agent Unix socket. #[arg(long, default_value = "/var/run/substrate/agent.sock")] socket: String, }, } fn whoami() -> String { std::env::var("USER").unwrap_or_else(|_| "substrate".to_string()) } fn main() -> anyhow::Result<()> { let cli = Cli::parse(); match cli.command { SubCmd::Shell { host, port, user, identity, } => { let mut cmd = Command::new("ssh"); cmd.arg("-p").arg(port.to_string()); cmd.arg("-o").arg("StrictHostKeyChecking=no"); cmd.arg("-o").arg("UserKnownHostsFile=/dev/null"); cmd.arg("-o").arg("LogLevel=ERROR"); if let Some(ref key) = identity { cmd.arg("-i").arg(key); } cmd.arg(format!("{user}@{host}")); eprintln!("Connecting to bascule-agent shell at {host}:{port}..."); let status = cmd.status()?; if !status.success() { std::process::exit(status.code().unwrap_or(1)); } } SubCmd::Status { socket } => { if std::path::Path::new(&socket).exists() { println!("Agent socket: {} (active)", socket); } else { println!("Agent socket: {} (not found)", socket); std::process::exit(1); } } } Ok(()) }