//! `hivectl stop` / `start` / `restart` — hive-wide + scoped container //! lifecycle. Each verb makes one daemon call and renders the queued DAGs. use std::path::Path; use anyhow::{Context as _, Result}; use crate::dag_progress::wait_for_nodes; use crate::util::render_lifecycle; pub(crate) async fn stop( socket: &Path, scope: hive_host_sock::LifecycleScope, graceful: bool, no_wait: bool, ) -> Result<()> { let resp = crate::client::request( socket, hive_host_sock::HostRequest::Stop { scope, graceful }, ) .await .with_context(|| format!("connect to daemon socket {}", socket.display()))?; // Render first, but even when an infra failure makes it bail, // watch the already-queued agent DAGs before surfacing the error — // they run regardless. let rendered = render_lifecycle(&resp, "stop queued"); wait_for_nodes(socket, resp.queued_dags.unwrap_or_default(), no_wait).await?; rendered } pub(crate) async fn start( socket: &Path, scope: hive_host_sock::LifecycleScope, no_wait: bool, ) -> Result<()> { let resp = crate::client::request(socket, hive_host_sock::HostRequest::Start { scope }) .await .with_context(|| format!("connect to daemon socket {}", socket.display()))?; let rendered = render_lifecycle(&resp, "start queued"); wait_for_nodes(socket, resp.queued_dags.unwrap_or_default(), no_wait).await?; rendered } /// Restart — one `RestartScoped` daemon call, server-side DAG-based (see /// issue tracker "dagify hivectl commands"). Each targeted agent rides /// exactly one atomic DAG queued up front — `Restart` (mechanical stop + /// reconcile), or `GracefulRestart` with `--graceful` (signal → drain → /// mechanical stop → reconcile); infra containers restart synchronously. /// No "submit one DAG, wait for it, submit another" composition on either /// side of the wire: unlike the old client-side stop-then-start compose, a /// dropped `hivectl` connection mid-restart no longer leaves an agent /// stopped with no automatic follow-up — the daemon owns the whole /// sequence once this call is made. /// /// No `--no-wait` here on purpose, same as before: the operator wants to /// see the restart actually land, not just get queued. pub(crate) async fn restart( socket: &Path, scope: hive_host_sock::LifecycleScope, graceful: bool, ) -> Result<()> { let resp = crate::client::request( socket, hive_host_sock::HostRequest::RestartScoped { scope, graceful }, ) .await .with_context(|| format!("connect to daemon socket {}", socket.display()))?; let rendered = render_lifecycle(&resp, "restart queued"); wait_for_nodes(socket, resp.queued_dags.unwrap_or_default(), false).await?; rendered }