This repository has been archived on 2026-10-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
maestro/crates/maestro-cli/src/dispatch.rs
T
Miguel Palhas 6790f41652 refactor(maestro): point CLI and TUI at the local daemon only
CLI drops the registry fallback, cookie auth, set-registry, and the
--daemon selector; every command hits [cli].daemon_url. TUI drops the
daemon tree from the sidebar and the daemon field from the spawn modal.
GET /api/web/info becomes GET /api/info.
2026-08-22 10:07:12 +01:00

114 lines
3.7 KiB
Rust

//! Public dispatch for the `maestro` CLI subcommands.
//!
//! Every command talks to the local daemon (`[cli].daemon_url`, default
//! `http://127.0.0.1:7070`): `list` reads its session catalog, `spawn` / `kill`
//! hit its session endpoints, and `attach` opens `WS /api/attach/:id`.
use std::path::{Path, PathBuf};
use crate::attach::{attach_url, parse_detach_key, run_attach_url};
use crate::cli::{AttachArgs, Command, KillArgs, ListArgs, SpawnArgs};
use crate::config::{default_config_path, CliConfig, ConfigError};
use crate::http::DaemonClient;
use crate::sessions::{run_kill, run_list, run_spawn};
#[derive(Debug, thiserror::Error)]
#[allow(clippy::result_large_err)]
pub enum CliError {
#[error("config: {0}")]
Config(#[from] ConfigError),
#[error("{0}")]
Http(#[from] crate::http::HttpError),
#[error("{0}")]
Attach(#[from] crate::attach::AttachError),
#[error("{0}")]
Io(#[from] std::io::Error),
}
/// Dispatch a parsed top-level subcommand. The umbrella binary passes the
/// global flags (`--config`, `--daemon-url`) in alongside the variant.
#[allow(clippy::result_large_err)]
pub async fn dispatch(
command: Command,
config: Option<PathBuf>,
daemon_url_override: Option<String>,
) -> Result<(), CliError> {
let config_path = match config {
Some(p) => p,
None => default_config_path()?,
};
match command {
Command::List(args) => list(&config_path, daemon_url_override, args).await,
Command::Spawn(args) => spawn(&config_path, daemon_url_override, args).await,
Command::Attach(args) => attach(&config_path, daemon_url_override, args).await,
Command::Kill(args) => kill(&config_path, daemon_url_override, args).await,
}
}
#[allow(clippy::result_large_err)]
async fn list(
config_path: &Path,
daemon_url_override: Option<String>,
args: ListArgs,
) -> Result<(), CliError> {
let daemon = client(config_path, daemon_url_override)?;
let mut stdout = std::io::stdout().lock();
run_list(&daemon, &args, &mut stdout).await?;
Ok(())
}
#[allow(clippy::result_large_err)]
async fn spawn(
config_path: &Path,
daemon_url_override: Option<String>,
args: SpawnArgs,
) -> Result<(), CliError> {
let daemon = client(config_path, daemon_url_override)?;
let attach_after = args.attach;
// `spawn --attach` has no detach-key flag; use the attach default (Ctrl-q).
let detach_key = 0x11;
let session = run_spawn(&daemon, args).await?;
println!("spawned {}", session.id);
if attach_after {
let url = attach_url(daemon.base(), session.id)?;
run_attach_url(&url, detach_key).await?;
}
Ok(())
}
#[allow(clippy::result_large_err)]
async fn attach(
config_path: &Path,
daemon_url_override: Option<String>,
args: AttachArgs,
) -> Result<(), CliError> {
let daemon = client(config_path, daemon_url_override)?;
let detach = parse_detach_key(&args.detach_key)?;
let url = attach_url(daemon.base(), args.session_id)?;
run_attach_url(&url, detach).await?;
Ok(())
}
#[allow(clippy::result_large_err)]
async fn kill(
config_path: &Path,
daemon_url_override: Option<String>,
args: KillArgs,
) -> Result<(), CliError> {
let daemon = client(config_path, daemon_url_override)?;
run_kill(&daemon, &args).await?;
println!("killed {}", args.session_id);
Ok(())
}
/// HTTP client pointed at the daemon's API. That surface needs no auth —
/// reaching `bind_addr` is the authorization.
#[allow(clippy::result_large_err)]
fn client(config_path: &Path, override_url: Option<String>) -> Result<DaemonClient, CliError> {
let cfg = CliConfig::load(config_path)?;
Ok(DaemonClient::new(cfg.resolve_daemon_url(override_url)))
}