atomr_agents_coding_cli_harness_web/
lib.rs1#![forbid(unsafe_code)]
8
9pub mod error;
10pub mod routes;
11pub mod runner;
12pub mod spa;
13pub mod sse;
14pub mod ws;
15
16use std::net::SocketAddr;
17use std::sync::Arc;
18
19use atomr_agents_coding_cli_harness::CodingCliHarness;
20use axum::Router;
21use tokio::sync::oneshot;
22use tokio::task::JoinHandle;
23
24use crate::runner::RunSupervisor;
25
26#[derive(Clone, Debug)]
27pub struct WebConfig {
28 pub bind: SocketAddr,
29}
30
31impl Default for WebConfig {
32 fn default() -> Self {
33 Self {
34 bind: "127.0.0.1:7300".parse().expect("valid default addr"),
36 }
37 }
38}
39
40#[derive(Clone)]
41pub struct AppState {
42 pub harness: Arc<CodingCliHarness>,
43 pub supervisor: Arc<parking_lot::Mutex<RunSupervisor>>,
44}
45
46pub struct WebHandle {
47 pub bound_addr: SocketAddr,
48 shutdown_tx: Option<oneshot::Sender<()>>,
49 join: Option<JoinHandle<()>>,
50}
51
52impl WebHandle {
53 pub async fn shutdown(mut self) {
54 if let Some(tx) = self.shutdown_tx.take() {
55 let _ = tx.send(());
56 }
57 if let Some(join) = self.join.take() {
58 let _ = join.await;
59 }
60 }
61}
62
63pub struct WebServer {
64 config: WebConfig,
65 state: AppState,
66}
67
68impl WebServer {
69 pub fn new(config: WebConfig, harness: Arc<CodingCliHarness>) -> Self {
70 Self {
71 config,
72 state: AppState {
73 harness,
74 supervisor: Arc::new(parking_lot::Mutex::new(RunSupervisor::default())),
75 },
76 }
77 }
78
79 pub fn router(&self) -> Router {
80 routes::build_router(self.state.clone())
81 }
82
83 pub async fn start(self) -> Result<WebHandle, ServerError> {
84 let router = self.router();
85 let listener = tokio::net::TcpListener::bind(self.config.bind)
86 .await
87 .map_err(ServerError::Bind)?;
88 let bound_addr = listener.local_addr().map_err(ServerError::Bind)?;
89 let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>();
90 let join = tokio::spawn(async move {
91 let _ = axum::serve(listener, router.into_make_service())
92 .with_graceful_shutdown(async {
93 let _ = shutdown_rx.await;
94 })
95 .await;
96 });
97 Ok(WebHandle {
98 bound_addr,
99 shutdown_tx: Some(shutdown_tx),
100 join: Some(join),
101 })
102 }
103}
104
105#[derive(Debug, thiserror::Error)]
106pub enum ServerError {
107 #[error("failed to bind: {0}")]
108 Bind(std::io::Error),
109}