pub mod fs_operations;
pub mod middleware;
pub mod routes;
pub mod session;
pub mod state;
pub mod streaming;
pub mod trace_models;
pub mod trace_parser;
pub mod trace_routes;
pub use state::AppState;
use axum::Router;
use std::net::SocketAddr;
use tower_http::cors::{Any, CorsLayer};
use tower_http::trace::TraceLayer;
use tracing::info;
const EXPERIMENTAL_WEB_API_ENABLED: bool = false;
pub async fn serve(host: String, port: u16, agents_dir: std::path::PathBuf) -> anyhow::Result<()> {
if !EXPERIMENTAL_WEB_API_ENABLED {
anyhow::bail!(
"The Appam web API is experimental and currently disabled for security reasons."
);
}
info!("Starting Appam Web API server");
info!(host = %host, port = port, "Server configuration");
let state = AppState::new(agents_dir).await?;
info!(agents = state.list_agents().len(), "Agents loaded");
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any);
let app = Router::new()
.merge(routes::agent_routes())
.merge(routes::session_routes())
.merge(routes::chat_routes())
.layer(cors)
.layer(TraceLayer::new_for_http())
.layer(middleware::rate_limit_layer())
.with_state(state);
let addr: SocketAddr = format!("{}:{}", host, port).parse()?;
info!("Server listening on http://{}", addr);
info!("API Documentation: http://{}/api/docs", addr);
let listener = tokio::net::TcpListener::bind(addr).await?;
axum::serve(
listener,
app.into_make_service_with_connect_info::<SocketAddr>(),
)
.await?;
Ok(())
}
pub async fn serve_tracing(
host: String,
port: u16,
traces_dir: std::path::PathBuf,
) -> anyhow::Result<()> {
use std::sync::Arc;
info!("Starting Appam Trace Visualizer");
info!(host = %host, port = port, traces_dir = %traces_dir.display(), "Server configuration");
if !traces_dir.exists() {
anyhow::bail!("Traces directory does not exist: {}", traces_dir.display());
}
let state = Arc::new(trace_routes::TraceState::new(traces_dir));
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any);
let app = trace_routes::trace_routes()
.layer(cors)
.layer(TraceLayer::new_for_http())
.with_state(state);
let addr: SocketAddr = format!("{}:{}", host, port).parse()?;
info!("Trace Visualizer listening on http://{}", addr);
info!("Open in browser: http://{}", addr);
let listener = tokio::net::TcpListener::bind(addr).await?;
axum::serve(
listener,
app.into_make_service_with_connect_info::<SocketAddr>(),
)
.await?;
Ok(())
}