use firstpass_proxy::calibrate::calibrate_from_store;
use firstpass_proxy::{ProxyConfig, cli, load_all_traces, run};
const HELP: &str = "\
firstpass — the cheapest model that provably passes, with a receipt for every call.
USAGE:
firstpass up start the proxy (serves until Ctrl-C)
firstpass doctor validate config, provider key, and gate binaries
firstpass trace [--limit N] print recent audit traces as JSON lines (default 20)
firstpass calibrate [--alpha A] [--delta D] [--min-n N]
recalibrate the serving threshold from deferred feedback
firstpass mcp serve MCP over stdio (agent reads traces + submits feedback)
firstpass --help | --version
ENVIRONMENT (shared by every subcommand):
FIRSTPASS_MODE=observe|enforce FIRSTPASS_BIND=127.0.0.1:8080
FIRSTPASS_CONFIG=./firstpass.toml (or FIRSTPASS_CONFIG_TOML=<inline>)
FIRSTPASS_DB=./firstpass.db RUST_LOG=info
Point your agent at it: export ANTHROPIC_BASE_URL=http://127.0.0.1:8080
Offboard any time: unset ANTHROPIC_BASE_URL";
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = std::env::args().collect();
match args.get(1).map(String::as_str).unwrap_or("--help") {
"up" => {
run::init_tracing();
run::serve(ProxyConfig::from_env()?).await
}
"doctor" => cmd_doctor(),
"trace" => cmd_trace(&args),
"calibrate" => cmd_calibrate(&args),
"mcp" => {
let db = ProxyConfig::from_env()?.db_path;
tokio::task::spawn_blocking(move || firstpass_proxy::mcp::serve_stdio(&db)).await??;
Ok(())
}
"--help" | "-h" | "help" => {
println!("{HELP}");
Ok(())
}
"--version" | "-V" => {
println!("firstpass {}", env!("CARGO_PKG_VERSION"));
Ok(())
}
other => {
eprintln!("firstpass: unknown command `{other}`\n\n{HELP}");
std::process::exit(2);
}
}
}
fn cmd_doctor() -> Result<(), Box<dyn std::error::Error>> {
match ProxyConfig::from_env() {
Ok(config) => {
let report = cli::doctor(&config, |k| std::env::var(k).ok());
print!("{}", report.render());
if report.healthy() {
Ok(())
} else {
std::process::exit(1);
}
}
Err(e) => {
println!("✗ config: {e}\n\nnot healthy — fix the config error above.");
std::process::exit(1);
}
}
}
fn cmd_trace(args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
let limit = args
.iter()
.position(|a| a == "--limit")
.and_then(|i| args.get(i + 1))
.and_then(|n| n.parse::<usize>().ok())
.unwrap_or(20);
let config = ProxyConfig::from_env()?;
let traces = load_all_traces(std::path::Path::new(&config.db_path)).unwrap_or_default();
println!("{}", cli::format_traces(&traces, limit));
Ok(())
}
fn cmd_calibrate(args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
let flag = |name: &str, default: f64| -> f64 {
args.iter()
.position(|a| a == name)
.and_then(|i| args.get(i + 1))
.and_then(|v| v.parse::<f64>().ok())
.unwrap_or(default)
};
let alpha = flag("--alpha", 0.1);
let delta = flag("--delta", 0.05);
let min_n = flag("--min-n", 30.0) as usize;
let config = ProxyConfig::from_env()?;
let report = calibrate_from_store(&config.db_path, alpha, delta, min_n)?;
print!("{}", report.render());
Ok(())
}