96 lines
2.9 KiB
Rust
96 lines
2.9 KiB
Rust
//! Clap-derive subcommand for the daemon, plus a runner that wires everything
|
|
//! together.
|
|
|
|
use std::error::Error;
|
|
use std::path::PathBuf;
|
|
|
|
use clap::Subcommand;
|
|
use tokio::net::TcpListener;
|
|
|
|
use crate::session::DEFAULT_LIVENESS_INTERVAL;
|
|
use crate::sidecar::SidecarStore;
|
|
use crate::tmux::Tmux;
|
|
use crate::worktree::WorktreeManager;
|
|
use crate::{
|
|
default_config_path, default_state_dir, load_config, local_api, EnvOverrides, SessionManager,
|
|
};
|
|
|
|
#[derive(Debug, Subcommand)]
|
|
pub enum DaemonCmd {
|
|
/// Start the daemon. Default when no subcommand is given.
|
|
Run,
|
|
}
|
|
|
|
/// Dispatch a parsed daemon subcommand. `config_override` is the unified
|
|
/// `--config` flag from the top-level CLI.
|
|
pub async fn dispatch(
|
|
config_override: Option<PathBuf>,
|
|
cmd: Option<DaemonCmd>,
|
|
) -> Result<(), Box<dyn Error>> {
|
|
match cmd.unwrap_or(DaemonCmd::Run) {
|
|
DaemonCmd::Run => run(config_override).await,
|
|
}
|
|
}
|
|
|
|
async fn run(config_override: Option<PathBuf>) -> Result<(), Box<dyn Error>> {
|
|
let config_path: PathBuf = match config_override {
|
|
Some(p) => p,
|
|
None => default_config_path()?,
|
|
};
|
|
let config = load_config(&config_path, EnvOverrides::from_env())?;
|
|
tracing::info!(
|
|
hostname = %config.hostname,
|
|
bind = %config.bind_addr,
|
|
"loaded config"
|
|
);
|
|
|
|
let _ = default_state_dir();
|
|
std::fs::create_dir_all(config.sidecar_dir())?;
|
|
std::fs::create_dir_all(config.fifo_dir())?;
|
|
// No worktree dir to pre-create: without an override the worktrees live
|
|
// under their base repos, created on demand (#56).
|
|
|
|
let sidecar = SidecarStore::new(config.sidecar_dir());
|
|
let worktree_mgr = WorktreeManager::new(config.worktree_root.clone());
|
|
let tmux = match config.tmux_tmpdir.clone() {
|
|
Some(p) => Tmux::new().with_tmpdir(p),
|
|
None => Tmux::new(),
|
|
};
|
|
let manager = SessionManager::new_with_replay(
|
|
std::sync::Arc::new(tmux),
|
|
sidecar,
|
|
worktree_mgr,
|
|
config.fifo_dir(),
|
|
config.pty_replay_bytes,
|
|
)
|
|
.with_spawn_history(crate::spawn_history::SpawnHistory::new(
|
|
config.spawn_history_path(),
|
|
config.spawn_history_days,
|
|
));
|
|
|
|
let recovered = manager.recover().await?;
|
|
tracing::info!(recovered, "adopted existing tmux sessions");
|
|
manager.start_liveness_watcher(DEFAULT_LIVENESS_INTERVAL);
|
|
|
|
let router = local_api::router(local_api::state_for(
|
|
manager,
|
|
config.hostname.clone(),
|
|
config.home_dir.clone(),
|
|
config.spawn_presets.clone(),
|
|
));
|
|
|
|
let listener = TcpListener::bind(config.bind_addr).await?;
|
|
let bound = listener.local_addr()?;
|
|
tracing::info!(addr = %bound, "API listening");
|
|
|
|
let shutdown = async {
|
|
let _ = tokio::signal::ctrl_c().await;
|
|
tracing::info!("ctrl-c received — shutting down");
|
|
};
|
|
|
|
axum::serve(listener, router)
|
|
.with_graceful_shutdown(shutdown)
|
|
.await?;
|
|
Ok(())
|
|
}
|