//! CodeCraft – web-based coding harness for AI models. mod api; mod app; mod auth; mod backup; mod cli_api; mod budget; mod config; mod digest; mod docs; mod export; mod files; mod git; mod health; mod gitcreds; mod hooks; mod images; mod jobs; mod lsp; mod mentions; mod metrics; mod mcp; mod permission; mod prompts; mod push; mod provider; mod reverse; mod runner; mod sandbox; mod shells; mod store; mod terminal; mod tools; mod types; mod update; use anyhow::Result; use tracing_subscriber::EnvFilter; #[tokio::main] async fn main() -> Result<()> { // CODECRAFT_LOG_FORMAT=json: one JSON object per line (Loki, Elastic, …). let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info,codecraft=info")); if std::env::var("CODECRAFT_LOG_FORMAT").is_ok_and(|f| f.eq_ignore_ascii_case("json")) { tracing_subscriber::fmt().json().flatten_event(true).with_current_span(false).with_env_filter(filter).init(); } else { tracing_subscriber::fmt().with_env_filter(filter).init(); } // In Docker, HOME lives on the data volume; make sure it exists for git/npm. if let Ok(home) = std::env::var("HOME") { let _ = std::fs::create_dir_all(home); } let path = config::config_path(); let (mut cfg, _) = config::load_or_init(&path)?; // A backup restored from the settings is applied here, before the database is opened. if backup::apply_pending_restore(&cfg.data_dir(), &path)? { tracing::warn!("restored database and config from a backup"); cfg = config::load_or_init(&path)?.0; } tracing::info!("config: {}", path.display()); tracing::info!("workspace: {}", cfg.workspace().display()); let _ = sandbox::probe().await; let bind = cfg.server.bind.clone(); let app = app::App::new(path, cfg).await?; lsp::spawn_reaper(app.clone()); push::spawn_listener(app.clone()); terminal::spawn_reaper(app.clone()); jobs::spawn_scheduler(app.clone()); update::spawn_checker(app.clone()); health::spawn_checker(app.clone()); digest::spawn(app.clone()); backup::spawn(app.clone()); let router = api::router(app); let listener = tokio::net::TcpListener::bind(&bind).await?; tracing::info!("listening on http://{bind}"); axum::serve(listener, router).with_graceful_shutdown(shutdown()).await?; Ok(()) } async fn shutdown() { let _ = tokio::signal::ctrl_c().await; tracing::info!("shutting down"); }