1mod 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#[derive(Debug, thiserror::Error)]
25#[non_exhaustive]
26pub enum OpsBuilderError {
27 #[error("authenticator not configured — call with_authenticator() before build()")]
28 MissingAuthenticator,
29}
30
31pub 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 pub fn with_authenticator(mut self, auth: Arc<dyn Authenticator>) -> Self {
51 self.authenticator = Some(auth);
52 self
53 }
54
55 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 pub fn with_task_store(mut self, store: Arc<A2aTaskStore>) -> Self {
63 self.task_store = Some(store);
64 self
65 }
66
67 pub fn with_resume_buffer(mut self, buf: Arc<KvResumeBuffer>) -> Self {
69 self.resume_buffer = Some(buf);
70 self
71 }
72
73 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}