//! Operations that are exposed through more than one surface (the host admin //! socket *and* the dashboard's POST endpoints). Each function takes a //! `&Coordinator` and the request parameters; callers stitch the response //! shape they want (HTTP redirect vs JSON). use anyhow::Result; use crate::coordinator::Coordinator; use crate::lifecycle; /// Approve a pending request: read the agent.nix at the approval's commit from /// the proposed repo, copy into the applied repo, commit there, and rebuild /// the agent container. On failure marks the approval failed (with the error /// note) and returns the error. pub async fn approve(coord: &Coordinator, id: i64) -> Result<()> { let approval = coord.approvals.mark_approved(id)?; tracing::info!(%approval.id, %approval.agent, %approval.commit_ref, "approval: applying + rebuilding"); let agent_dir = coord.register_agent(&approval.agent)?; let proposed_dir = Coordinator::agent_proposed_dir(&approval.agent); let applied_dir = Coordinator::agent_applied_dir(&approval.agent); let result: Result<()> = async { lifecycle::apply_commit(&applied_dir, &proposed_dir, &approval.commit_ref).await?; lifecycle::rebuild( &approval.agent, &coord.hyperhive_flake, &agent_dir, &applied_dir, ) .await } .await; if let Err(e) = result { let note = format!("{e:#}"); let _ = coord.approvals.mark_failed(approval.id, ¬e); return Err(e); } Ok(()) } pub fn deny(coord: &Coordinator, id: i64) -> Result<()> { coord.approvals.mark_denied(id)?; tracing::info!(%id, "approval denied"); Ok(()) }