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.
114 lines
3.7 KiB
Rust
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)))
|
|
}
|