use firstpass_proxy::calibrate::calibrate_from_store;
use firstpass_proxy::{ProxyConfig, cli, run, store};
const HELP: &str = "\
firstpass — the cheapest model that provably passes, with a receipt for every call.
USAGE:
firstpass onboard [--apply] agentic setup: detect env, start proxy, route your agent, verify
firstpass offboard undo it: strip the rc line, stop the proxy, print the unset
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
}
"onboard" => cmd_onboard(args.iter().any(|a| a == "--apply")),
"offboard" => cmd_offboard(),
"doctor" => cmd_doctor(),
"trace" => cmd_trace(&args),
"calibrate" => cmd_calibrate(&args),
"mcp" => {
let config = ProxyConfig::from_env()?;
let tenant = tenant_arg(&args, &config);
let db = config.db_path;
tokio::task::spawn_blocking(move || firstpass_proxy::mcp::serve_stdio(&db, &tenant))
.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_onboard(apply: bool) -> Result<(), Box<dyn std::error::Error>> {
use firstpass_proxy::onboard;
let env = onboard::detect(
|k| std::env::var(k).ok(),
|bin| {
std::env::var("PATH")
.is_ok_and(|p| std::env::split_paths(&p).any(|d| d.join(bin).is_file()))
},
|| {
let bind = std::env::var("FIRSTPASS_BIND").unwrap_or_else(|_| "127.0.0.1:8080".into());
std::net::TcpStream::connect_timeout(
&bind
.parse()
.unwrap_or_else(|_| ([127, 0, 0, 1], 8080).into()),
std::time::Duration::from_millis(400),
)
.is_ok()
},
);
let home = std::path::PathBuf::from(std::env::var("HOME").unwrap_or_else(|_| ".".into()));
let (rc, _) = onboard::shell_wiring(&env.shell, &home, &env.bind);
let steps = onboard::plan(&env, &home, onboard::rc_wired(&rc));
print!("{}", onboard::render(&env, &steps, apply));
if apply {
print!("\n{}", onboard::execute(&env, &steps)?);
}
Ok(())
}
fn cmd_offboard() -> Result<(), Box<dyn std::error::Error>> {
let home = std::path::PathBuf::from(std::env::var("HOME").unwrap_or_else(|_| ".".into()));
print!("{}", firstpass_proxy::onboard::offboard(&home)?);
Ok(())
}
fn tenant_arg(args: &[String], config: &ProxyConfig) -> String {
args.iter()
.position(|a| a == "--tenant")
.and_then(|i| args.get(i + 1))
.cloned()
.unwrap_or_else(|| config.tenant_id.clone())
}
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 tenant = tenant_arg(args, &config);
let traces = store::load_tenant_traces(std::path::Path::new(&config.db_path), &tenant)
.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 tenant = tenant_arg(args, &config);
let report = calibrate_from_store(&config.db_path, &tenant, alpha, delta, min_n)?;
print!("{}", report.render());
Ok(())
}