feat(dashboard): add paused badge and pause/resume toggle to agent cards
When ContainerView.paused is true, show a clickable yellow `⏸ paused`
badge on the agent card that POSTs to the new /api/resume/{name} endpoint
to un-park the turn loop. The badge doubles as the resume button so the
state is self-documenting and one click to fix.
The agent action menu gains ⏸ P4US3 (when not paused) and ▶ R3SUM3
(when paused), orthogonal to the running/stopped start/stop actions.
On the backend, /api/pause/{name} and /api/resume/{name} POST routes
wire to Coordinator::set_paused and trigger an immediate rescan so the
badge flips via the existing SSE ContainerUpdate without polling.
Depends on the ContainerView.paused field and Coordinator::set_paused
added in the parent PR.
This commit is contained in:
parent
538b56d2e4
commit
59041d4f03
4 changed files with 80 additions and 0 deletions
|
|
@ -137,6 +137,56 @@ pub(super) async fn post_start(
|
|||
(StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
/// `POST /api/pause/{name}` — write the pause marker for `name`.
|
||||
///
|
||||
/// Unlike the lifecycle ops above this is not a DAG: it writes a single
|
||||
/// marker file, which the harness stats at the top of its serve loop.
|
||||
/// Works on stopped containers too (the marker is sticky and takes effect
|
||||
/// when the container next boots). Triggers an immediate rescan so the
|
||||
/// `paused` badge flips on the dashboard without waiting for the next
|
||||
/// periodic sweep.
|
||||
pub(super) async fn post_pause(
|
||||
State(state): State<AppState>,
|
||||
AxumPath(name): AxumPath<String>,
|
||||
) -> Response {
|
||||
let logical = strip_container_prefix(&name);
|
||||
if let Some(reject) = guard_agent_name(&state, &logical).await {
|
||||
return reject;
|
||||
}
|
||||
// guard_agent_name already validated `logical` as a well-formed Ident.
|
||||
let Ok(ident) = hive_types::Ident::parse(&logical) else {
|
||||
unreachable!("guard_agent_name passed an invalid ident");
|
||||
};
|
||||
if let Err(e) = crate::coordinator::Coordinator::set_paused(&ident, true) {
|
||||
return error_response(&format!("pause {logical}: {e}"));
|
||||
}
|
||||
state.coord.rescan_containers_and_emit().await;
|
||||
(StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
/// `POST /api/resume/{name}` — remove the pause marker for `name`.
|
||||
///
|
||||
/// The inverse of `post_pause`. Removing a non-existent marker is a no-op
|
||||
/// (idempotent). Triggers an immediate rescan so the paused badge clears.
|
||||
pub(super) async fn post_resume(
|
||||
State(state): State<AppState>,
|
||||
AxumPath(name): AxumPath<String>,
|
||||
) -> Response {
|
||||
let logical = strip_container_prefix(&name);
|
||||
if let Some(reject) = guard_agent_name(&state, &logical).await {
|
||||
return reject;
|
||||
}
|
||||
// guard_agent_name already validated `logical` as a well-formed Ident.
|
||||
let Ok(ident) = hive_types::Ident::parse(&logical) else {
|
||||
unreachable!("guard_agent_name passed an invalid ident");
|
||||
};
|
||||
if let Err(e) = crate::coordinator::Coordinator::set_paused(&ident, false) {
|
||||
return error_response(&format!("resume {logical}: {e}"));
|
||||
}
|
||||
state.coord.rescan_containers_and_emit().await;
|
||||
(StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
pub(super) async fn post_update_all(State(state): State<AppState>) -> Response {
|
||||
let containers = lifecycle::list().await.unwrap_or_default();
|
||||
for container in containers {
|
||||
|
|
|
|||
|
|
@ -193,6 +193,8 @@ pub async fn serve(
|
|||
.route("/api/restart/{name}", post(lifecycle_ops::post_restart))
|
||||
.route("/api/start/{name}", post(lifecycle_ops::post_start))
|
||||
.route("/api/rebuild/{name}", post(lifecycle_ops::post_rebuild))
|
||||
.route("/api/pause/{name}", post(lifecycle_ops::post_pause))
|
||||
.route("/api/resume/{name}", post(lifecycle_ops::post_resume))
|
||||
.route("/api/update-all", post(lifecycle_ops::post_update_all))
|
||||
.route(
|
||||
"/api/infra-container/{name}/{action}",
|
||||
|
|
|
|||
Loading…
Reference in a new issue