//! The `ops-agent` HTTP wire contract, shared by the `AgentRpc` client //! ([`crate::rpc`]) and the agent server ([`crate::agent`]). //! //! `/run` streams a body of newline-delimited JSON [`Frame`]s: zero or more //! `chunk` frames (merged stdout+stderr as it arrives), then exactly one //! terminal frame — `exit` on a clean run or `error` if the agent refused or //! failed before the child produced an exit code. use crate::step::Step; use serde::{Deserialize, Serialize}; /// `POST /run` request body. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RunRequest { pub step: Step, } /// One newline-delimited frame in a `/run` response stream. #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "t", rename_all = "snake_case")] pub enum Frame { /// A slice of merged stdout/stderr (UTF-8-lossy; not line-aligned). Chunk { text: String }, /// Terminal: the child exited with this code. Exit { code: i32 }, /// Terminal: the agent refused (capability/identity) or failed to spawn. Error { message: String }, } impl Frame { /// Serialize as one NDJSON line (trailing `\n` included). pub fn to_line(&self) -> String { let mut s = serde_json::to_string(self).unwrap_or_else(|e| { format!("{{\"t\":\"error\",\"message\":\"frame serialize failed: {e}\"}}") }); s.push('\n'); s } } /// `GET /health` response body. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct HealthResponse { pub ok: bool, /// The agent's own actuate grant tokens (introspection / audit). pub actuate: Vec, /// The agent's own observe grant tokens. pub observe: Vec, }