use std::collections::HashMap; use std::sync::Arc; use lazyboy_control::SandboxProvider; use lazyboy_sandbox::{DockerSandbox, FakeSandbox}; use sqlx::PgPool; use sqlx::postgres::PgPoolOptions; use tokio::sync::Mutex; use crate::auth::AuthConfig; use crate::db::{Actor, Db}; use crate::mcp::McpHub; use crate::memory::MemoryService; #[derive(Clone, Default)] pub struct CallRegistry { inner: Arc>>, } impl CallRegistry { pub async fn try_begin(&self, bot_id: &str, call_id: &str) -> bool { let mut map = self.inner.lock().await; if map.contains_key(bot_id) { return false; } map.insert(bot_id.to_string(), call_id.to_string()); true } pub async fn end(&self, bot_id: &str, call_id: &str) { let mut map = self.inner.lock().await; if map.get(bot_id).is_some_and(|held| held == call_id) { map.remove(bot_id); } } } #[derive(Clone)] pub struct AppState { pub db: Db, pub sandbox: Arc, pub data_dir: String, pub auth: AuthConfig, pub memory: MemoryService, pub mcp: McpHub, pub calls: CallRegistry, } impl AppState { pub async fn connect(database_url: &str) -> Result { let pool = PgPoolOptions::new() .max_connections(10) .connect(database_url) .await .map_err(|error| error.to_string())?; sqlx::migrate!("../../migrations") .run(&pool) .await .map_err(|error| error.to_string())?; let sandbox = sandbox_from_env(); Ok(Self { db: Db { pool }, sandbox, data_dir: std::env::var("DATA_DIR").unwrap_or_else(|_| "./data".into()), auth: AuthConfig::from_env(), memory: MemoryService::from_env(), mcp: McpHub::new(), calls: CallRegistry::default(), }) } pub async fn bootstrap(&self) -> Result { self.db.ensure_local_actor().await } pub fn pool(&self) -> &PgPool { &self.db.pool } } fn sandbox_from_env() -> Arc { match std::env::var("SANDBOX_PROVIDER") .unwrap_or_else(|_| "docker".into()) .as_str() { "fake" => Arc::new(FakeSandbox::new()), _ => { let url = std::env::var("SANDBOX_SUPERVISOR_URL") .unwrap_or_else(|_| "http://127.0.0.1:7091".into()); let token = std::env::var("SANDBOX_SUPERVISOR_TOKEN") .expect("SANDBOX_SUPERVISOR_TOKEN must be set"); assert!( token.len() >= 32 && token != "dev-token", "SANDBOX_SUPERVISOR_TOKEN must be a non-default value of at least 32 characters" ); Arc::new(DockerSandbox::new(url, token)) } } }