api: wire up real agent channel for HUD commands, add connected_viewers to health

This commit is contained in:
Butterfly Dev 2026-04-07 03:58:33 +00:00
parent 2344060d73
commit 4bc5e099cc
3 changed files with 27 additions and 27 deletions

View File

@ -6,15 +6,16 @@ use crate::state::AppState;
/// `GET /api/health` /// `GET /api/health`
/// ///
/// Returns server uptime, active sessions, connected agents and version. /// Returns server uptime, active sessions, connected agents, connected viewers, and version.
pub async fn health(state: web::Data<Arc<AppState>>) -> HttpResponse { pub async fn health(state: web::Data<Arc<AppState>>) -> HttpResponse {
let uptime = state.started_at.elapsed().as_secs(); let uptime = state.started_at.elapsed().as_secs();
let (sessions, agents) = state.stats(); let (active_sessions, connected_agents, connected_viewers) = state.stats();
let info = HealthInfo { let info = HealthInfo {
status: "ok".into(), status: "ok".into(),
uptime_secs: uptime, uptime_secs: uptime,
active_sessions: sessions, active_sessions,
connected_agents: agents, connected_agents,
connected_viewers,
version: env!("CARGO_PKG_VERSION").into(), version: env!("CARGO_PKG_VERSION").into(),
}; };
HttpResponse::Ok().json(ApiResponse::ok(info)) HttpResponse::Ok().json(ApiResponse::ok(info))

View File

@ -2,7 +2,7 @@ use actix_web::{web, HttpResponse};
use serde::Deserialize; use serde::Deserialize;
use std::sync::Arc; use std::sync::Arc;
use crate::models::{ApiResponse, Session}; use crate::models::{ApiResponse, Session, WsMessage};
use crate::state::AppState; use crate::state::AppState;
/// `GET /api/sessions` — list every session. /// `GET /api/sessions` — list every session.
@ -54,8 +54,8 @@ pub struct HudCommandPayload {
/// `POST /api/sessions/{id}/hud` — send a HUD command to the agent for a session. /// `POST /api/sessions/{id}/hud` — send a HUD command to the agent for a session.
/// ///
/// The command is forwarded to the connected agent via the session's agent /// Forwards the command through the agent's mpsc channel so it reaches the
/// channel (stored in AppState). If no agent is connected, returns 409. /// agent's WebSocket connection in real time.
pub async fn send_hud_command( pub async fn send_hud_command(
state: web::Data<Arc<AppState>>, state: web::Data<Arc<AppState>>,
path: web::Path<String>, path: web::Path<String>,
@ -63,26 +63,24 @@ pub async fn send_hud_command(
) -> HttpResponse { ) -> HttpResponse {
let session_id = path.into_inner(); let session_id = path.into_inner();
// Find the first agent registered for this session. // Build the forward message.
let agent_id = state let msg = WsMessage::ForwardHudCommand {
.agents command: body.command.clone(),
.iter() params: body.params.clone(),
.find(|r| r.session_id == session_id) };
.map(|r| r.agent_id.clone()); let json = match serde_json::to_string(&msg) {
Ok(j) => j,
match agent_id { Err(e) => {
Some(aid) => { return HttpResponse::InternalServerError()
// TODO: in the WS handler we will add a per-agent mpsc sender so .json(ApiResponse::<()>::err(format!("serialization error: {}", e)));
// we can forward the HUD command there. For now log and acknowledge.
log::info!(
"HUD command '{}' for session {} → agent {} (params: {})",
body.command,
session_id,
aid,
body.params
);
HttpResponse::Ok().json(ApiResponse::ok("command forwarded"))
} }
None => HttpResponse::Conflict().json(ApiResponse::<()>::err("no agent connected for this session")), };
// Send through the agent channel.
if state.send_to_agent(&session_id, &json).await {
HttpResponse::Ok().json(ApiResponse::ok("command forwarded"))
} else {
HttpResponse::Conflict()
.json(ApiResponse::<()>::err("no agent connected for this session"))
} }
} }

View File

@ -141,5 +141,6 @@ pub struct HealthInfo {
pub uptime_secs: u64, pub uptime_secs: u64,
pub active_sessions: usize, pub active_sessions: usize,
pub connected_agents: usize, pub connected_agents: usize,
pub connected_viewers: usize,
pub version: String, pub version: String,
} }