Skip to main content

klieo_ops_api/
lib.rs

1//! `klieo-ops-api` — read-only HTTP monitoring router for klieo agents.
2//!
3//! Mount the router returned by [`OpsRouterBuilder::build`] at `/_ops`
4//! to expose run history, A2A task state, and MCP stream state.
5//!
6//! All three backing stores are optional. Endpoints whose store is not
7//! wired return `501 Not Implemented`.
8
9mod auth;
10mod error;
11pub mod handlers;
12mod state;
13pub mod types;
14
15use axum::{middleware, Router};
16use crate::state::OpsState;
17use klieo_a2a::task_store::A2aTaskStore;
18use klieo_auth_common::Authenticator;
19use klieo_core::resume::KvResumeBuffer;
20use klieo_runlog::RunLogStore;
21use std::sync::Arc;
22
23/// Error returned by [`OpsRouterBuilder::build`].
24#[derive(Debug, thiserror::Error)]
25#[non_exhaustive]
26pub enum OpsBuilderError {
27    #[error("authenticator not configured — call with_authenticator() before build()")]
28    MissingAuthenticator,
29}
30
31/// Builds the `/_ops` monitoring router.
32pub struct OpsRouterBuilder {
33    authenticator: Option<Arc<dyn Authenticator>>,
34    run_log_store: Option<Arc<dyn RunLogStore>>,
35    task_store: Option<Arc<A2aTaskStore>>,
36    resume_buffer: Option<Arc<KvResumeBuffer>>,
37}
38
39impl OpsRouterBuilder {
40    pub fn new() -> Self {
41        Self {
42            authenticator: None,
43            run_log_store: None,
44            task_store: None,
45            resume_buffer: None,
46        }
47    }
48
49    /// Required — [`build`] returns `Err` if not called.
50    pub fn with_authenticator(mut self, auth: Arc<dyn Authenticator>) -> Self {
51        self.authenticator = Some(auth);
52        self
53    }
54
55    /// Enables `GET /runs`, `GET /runs/{id}`, `GET /agents`.
56    pub fn with_run_log_store(mut self, store: Arc<dyn RunLogStore>) -> Self {
57        self.run_log_store = Some(store);
58        self
59    }
60
61    /// Enables `GET /tasks`.
62    pub fn with_task_store(mut self, store: Arc<A2aTaskStore>) -> Self {
63        self.task_store = Some(store);
64        self
65    }
66
67    /// Enables `GET /streams`.
68    pub fn with_resume_buffer(mut self, buf: Arc<KvResumeBuffer>) -> Self {
69        self.resume_buffer = Some(buf);
70        self
71    }
72
73    /// # Errors
74    /// Returns [`OpsBuilderError::MissingAuthenticator`] if
75    /// [`with_authenticator`] was not called before `build`.
76    pub fn build(self) -> Result<Router, OpsBuilderError> {
77        let authenticator = self
78            .authenticator
79            .ok_or(OpsBuilderError::MissingAuthenticator)?;
80
81        let state = Arc::new(OpsState {
82            authenticator,
83            run_log_store: self.run_log_store,
84            task_store: self.task_store,
85            resume_buffer: self.resume_buffer,
86        });
87
88        use axum::routing::get;
89        use crate::handlers::{runs, tasks, streams};
90
91        Ok(Router::new()
92            .route("/runs",          get(runs::list_runs))
93            .route("/runs/:run_id",  get(runs::get_run))
94            .route("/agents",        get(runs::list_agents))
95            .route("/tasks",         get(tasks::list_tasks))
96            .route("/streams",       get(streams::list_streams))
97            .layer(middleware::from_fn_with_state(
98                Arc::clone(&state),
99                crate::auth::require_auth,
100            ))
101            .with_state(state))
102    }
103}
104
105impl Default for OpsRouterBuilder {
106    fn default() -> Self {
107        Self::new()
108    }
109}