//! `hivectl` — operator-facing host CLI for hyperhive. //! //! A thin client for the `hive-c0re` daemon: it speaks the host admin //! socket protocol (`hive-host-sock`) and does NOT link the daemon crate. //! Container lifecycle + the approval queue (`agent `, `list-agents`, `approvals `, `stop` / `start`) and provisioning (`forge` / `matrix` / //! `github` / `gateway`) all forward to the daemon, which owns the broker, //! the credentials, and the provisioning logic — a running daemon is //! required for those. A couple of verbs work off local host state //! directly instead (`wg` / `peer-config` read the mesh key + TLS CA), so //! they don't need the socket. `agent choom` execs into a container //! rather than asking the daemon to do anything, but still uses the socket //! for its "is this an agent?" pre-flight — that answer lives in a //! directory only the daemon's user can read. //! //! One module per subcommand family (see the `mod` list below); `main` is //! just the clap parse + the top-level dispatch match. use anyhow::Result; use clap::Parser; mod cli; /// The host admin socket client (`request`), split out so it lives with /// hivectl rather than in the daemon crate. mod client; /// Rebuild-queue node progress rendering (`wait_for_nodes` + the spinner / /// plain renderers), split out to keep this file manageable. mod dag_progress; use cli::{Cli, Cmd, ForgeCmd, GatewayCmd, GithubCmd, WgCmd}; mod completions; mod quota; mod util; use completions::generate_completions; mod gateway; use gateway::{gateway_create_user, gateway_delete_user, gateway_list_users}; mod matrix; use matrix::run_matrix_cmd; mod open; use open::open_url; mod wg; use wg::{peer_config, require_hive_domain, wg_init, wg_peer, wg_status}; mod choom; mod github; mod watch; use github::github_set_token; mod forge; use forge::{forge_create_user, forge_reconcile_config}; mod agents; use agents::{agents_list, run_agent}; mod power; use power::{restart, start, stop}; mod approvals; mod subvol; use approvals::run_approvals; #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt() .with_env_filter( tracing_subscriber::EnvFilter::try_from_default_env() .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")), ) .init(); let cli = Cli::parse(); let socket = cli.socket; match cli.cmd { Cmd::Forge { cmd } => match cmd { ForgeCmd::CreateUser { name, password, password_stdin, } => forge_create_user(&socket, &name, password.as_deref(), password_stdin).await, ForgeCmd::ReconcileConfig { agent, from, verbose, } => forge_reconcile_config(&socket, &agent, from, verbose).await, }, Cmd::Matrix { cmd } => run_matrix_cmd(&socket, cmd).await, Cmd::Github { cmd } => match cmd { GithubCmd::SetToken { agent, token, token_stdin, } => github_set_token(&socket, &agent, token, token_stdin).await, }, Cmd::Gateway { cmd } => match cmd { GatewayCmd::CreateUser { username, password, password_stdin, } => gateway_create_user(&socket, &username, password.as_deref(), password_stdin).await, GatewayCmd::DeleteUser { username } => gateway_delete_user(&socket, &username).await, GatewayCmd::ListUsers => gateway_list_users(&socket).await, }, Cmd::Agent { name, cmd } => run_agent(&socket, &name, cmd).await, Cmd::ListAgents { json } => agents_list(&socket, json).await, Cmd::QuotaEnable => quota::quota_enable(&socket).await, Cmd::Approvals { cmd } => run_approvals(&socket, cmd).await, Cmd::Wg { cmd } => match cmd { WgCmd::Init { address } => wg_init(&socket, address.as_deref()).await, WgCmd::Peer { domain, pubkey, address, endpoint, } => { wg_peer(&domain, &pubkey, &address, endpoint.as_deref()); Ok(()) } WgCmd::Status => wg_status(), }, Cmd::PeerConfig { wg_address, wg_endpoint, } => { let domain = require_hive_domain(&socket).await?; peer_config(&domain, wg_address.as_deref(), wg_endpoint.as_deref()); Ok(()) } Cmd::Stop { scope, graceful, no_wait, } => stop(&socket, scope.to_scope(), graceful, no_wait).await, Cmd::Start { scope, no_wait } => start(&socket, scope.to_scope(), no_wait).await, Cmd::Restart { scope, graceful } => restart(&socket, scope.to_scope(), graceful).await, Cmd::MarkdownDocs => { print!("{}", clap_markdown::help_markdown::()); Ok(()) } Cmd::Open { target } => open_url(&socket, target).await, Cmd::Completions { shell } => { generate_completions(shell); Ok(()) } } }