Skip to main content

greentic_runner_host/runner/
mod.rs

1pub mod adapt_events_email;
2pub mod adapt_timer;
3pub mod agent_node;
4pub mod card_nav;
5pub mod component_invoker;
6pub mod contract_cache;
7pub mod contract_introspection;
8pub mod dispatch_listener;
9pub mod engine;
10pub mod flow_adapter;
11pub mod graph_node;
12pub mod i18n;
13pub mod invocation;
14#[cfg(feature = "knowledge-chronicle")]
15pub mod knowledge_corpus;
16#[cfg(feature = "knowledge-chronicle")]
17pub mod knowledge_mount;
18#[cfg(feature = "long-term-chronicle")]
19pub mod long_term_memory;
20pub mod mcp_node;
21pub mod mcp_pack_routes;
22#[cfg(feature = "agentic-worker")]
23pub mod mcp_warm_listener;
24pub mod mocks;
25pub mod operator;
26pub mod remote_dispatch;
27pub mod runtime_session_resumer;
28pub mod schema_validator;
29pub mod templating;
30
31use std::net::SocketAddr;
32use std::sync::Arc;
33
34use anyhow::Result;
35use axum::routing::{get, post};
36use axum::{Router, serve};
37use tokio::net::TcpListener;
38
39use crate::host::RunnerHost;
40use crate::http::{self, admin, auth::AdminAuth, health::HealthState};
41use crate::routing::TenantRouting;
42use crate::runtime::ActivePacks;
43use crate::sql::SqlGateway;
44use crate::watcher::PackReloadHandle;
45
46pub struct HostServer {
47    addr: SocketAddr,
48    router: Router,
49    _state: ServerState,
50}
51
52impl HostServer {
53    pub fn new(
54        port: u16,
55        active: Arc<ActivePacks>,
56        routing: TenantRouting,
57        health: Arc<HealthState>,
58        reload: Option<PackReloadHandle>,
59        admin: AdminAuth,
60        host: Arc<RunnerHost>,
61    ) -> Result<Self> {
62        Self::with_sql(port, active, routing, health, reload, admin, host, None)
63    }
64
65    /// Like [`HostServer::new`] but wires an optional [`SqlGateway`] into the
66    /// server state.  When `sql_gateway` is `None` the server uses an empty
67    /// gateway that returns 404/401 for all `/sql` routes — safe by default.
68    // Builder param set mirrors the existing fields plus `host`; a config-struct
69    // refactor is deferred. See B0 plan.
70    #[allow(clippy::too_many_arguments)]
71    pub fn with_sql(
72        port: u16,
73        active: Arc<ActivePacks>,
74        routing: TenantRouting,
75        health: Arc<HealthState>,
76        reload: Option<PackReloadHandle>,
77        admin: AdminAuth,
78        host: Arc<RunnerHost>,
79        sql_gateway: Option<SqlGateway>,
80    ) -> Result<Self> {
81        let addr = SocketAddr::from(([0, 0, 0, 0], port));
82        let sql = sql_gateway
83            .unwrap_or_else(|| SqlGateway::new(std::collections::HashMap::new(), String::new()));
84        let state = ServerState {
85            active,
86            routing,
87            health,
88            reload,
89            admin,
90            host,
91            sql,
92        };
93        let router = Router::new()
94            .route("/operator/op/invoke", post(operator::invoke))
95            .route("/healthz", get(http::health::handler))
96            .route("/admin/packs/status", get(admin::status))
97            .route("/admin/packs/reload", post(admin::reload))
98            .route("/agent/chat", post(crate::http::agent_chat::agent_chat))
99            .route(
100                "/sql/{conn}/schema",
101                get(crate::sql::routes::schema_handler),
102            )
103            .route("/sql/{conn}/query", post(crate::sql::routes::query_handler))
104            .with_state(state.clone());
105        Ok(Self {
106            addr,
107            router,
108            _state: state,
109        })
110    }
111
112    pub async fn serve(self) -> Result<()> {
113        tracing::info!(addr = %self.addr, "starting host server");
114        let listener = TcpListener::bind(self.addr).await?;
115        serve(
116            listener,
117            self.router
118                .into_make_service_with_connect_info::<SocketAddr>(),
119        )
120        .await?;
121        Ok(())
122    }
123}
124
125#[derive(Clone)]
126pub struct ServerState {
127    pub active: Arc<ActivePacks>,
128    pub routing: TenantRouting,
129    pub health: Arc<HealthState>,
130    pub reload: Option<PackReloadHandle>,
131    pub admin: AdminAuth,
132    /// The runner host instance used by the `POST /agent/chat` handler to
133    /// dispatch activity turns to loaded worker packs.
134    pub host: Arc<RunnerHost>,
135    /// SQL gateway for `/sql/{conn}/schema` and `/sql/{conn}/query`.
136    /// Defaults to an empty gateway (returns 404/401 for all connections)
137    /// when no SQL connections are configured.
138    pub sql: SqlGateway,
139}
140
141impl axum::extract::FromRef<ServerState> for SqlGateway {
142    fn from_ref(state: &ServerState) -> Self {
143        state.sql.clone()
144    }
145}