use clap::Parser;
use openlatch_client::cli;
use openlatch_client::cli::Commands;
use openlatch_client::telemetry::{self, Event};
fn main() {
let _ = ctrlc::set_handler(|| {
#[cfg(feature = "crash-report")]
openlatch_client::crash_report::flush(std::time::Duration::from_secs(2));
std::process::exit(130);
});
#[cfg(feature = "crash-report")]
let _crash_guard = init_crash_report();
init_telemetry();
install_panic_hook();
let cli_args = cli::Cli::parse();
let output = cli::build_output_config(&cli_args);
let Some(command) = cli_args.command.as_ref() else {
use clap::CommandFactory;
cli::header::print_full_banner(&output);
let _ = cli::Cli::command().print_help();
println!();
return;
};
let started_at = std::time::Instant::now();
let (command_label, subcommand_label) = command_labels(command);
let skip_command_event = should_skip_command_event(command);
#[cfg(feature = "crash-report")]
openlatch_client::crash_report::enrich_cli_scope(command_label);
let result = dispatch(command, &output);
let exit_code = if result.is_err() { 1 } else { 0 };
let duration_ms = started_at.elapsed().as_millis().min(u128::from(u64::MAX)) as u64;
if !skip_command_event {
telemetry::capture_global(Event::command_invoked(
command_label,
subcommand_label,
exit_code,
duration_ms,
));
}
if let Err(e) = result {
output.print_error(&e);
std::process::exit(1);
}
}
#[cfg(feature = "crash-report")]
fn init_crash_report() -> Option<sentry::ClientInitGuard> {
let dir = openlatch_client::config::openlatch_dir();
openlatch_client::crash_report::init(&dir)
}
fn init_telemetry() {
let dir = openlatch_client::config::openlatch_dir();
let agent_id = std::fs::read_to_string(dir.join("config.toml"))
.ok()
.and_then(|raw| {
raw.lines()
.find_map(|l| {
let l = l.trim();
l.strip_prefix("agent_id")
.and_then(|rest| rest.split('=').nth(1))
.map(|v| v.trim().trim_matches('"').to_string())
})
.filter(|s| s.starts_with("agt_"))
})
.unwrap_or_else(|| "agt_unknown".to_string());
let baked_key_present = openlatch_client::telemetry::network::key_is_present();
let handle = telemetry::init(&dir, agent_id, false, baked_key_present);
let _ = telemetry::install_global(handle);
}
fn install_panic_hook() {
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let location = info
.location()
.map(|l| format!("{}:{}", l.file(), l.line()))
.unwrap_or_else(|| "unknown".to_string());
telemetry::capture_global(Event::daemon_crashed(&location, 0));
prev(info);
}));
}
fn command_labels(cmd: &Commands) -> (&'static str, Option<&'static str>) {
match cmd {
Commands::Init(_) => ("init", None),
Commands::Status => ("status", None),
Commands::Start(_) => ("start", None),
Commands::Stop => ("stop", None),
Commands::Restart => ("restart", None),
Commands::Logs(_) => ("logs", None),
Commands::Doctor(_) => ("doctor", None),
Commands::Uninstall(_) => ("uninstall", None),
Commands::Docs => ("docs", None),
Commands::Hooks { cmd } => (
"hooks",
Some(match cmd {
cli::HooksCommands::Install(_) => "install",
cli::HooksCommands::Uninstall(_) => "uninstall",
cli::HooksCommands::Status => "status",
}),
),
Commands::Daemon { cmd } => (
"daemon",
Some(match cmd {
cli::DaemonCommands::Start(_) => "start",
cli::DaemonCommands::Stop => "stop",
cli::DaemonCommands::Restart => "restart",
}),
),
Commands::Auth { cmd } => (
"auth",
Some(match cmd {
cli::AuthCommands::Login(_) => "login",
cli::AuthCommands::Logout => "logout",
cli::AuthCommands::Status => "status",
}),
),
Commands::Telemetry { cmd } => (
"telemetry",
Some(match cmd {
cli::TelemetryCommands::Status => "status",
cli::TelemetryCommands::Enable => "enable",
cli::TelemetryCommands::Disable => "disable",
cli::TelemetryCommands::Purge => "purge",
cli::TelemetryCommands::Debug => "debug",
}),
),
Commands::Supervision { cmd } => (
"supervision",
Some(match cmd {
cli::SupervisionCommands::Install => "install",
cli::SupervisionCommands::Uninstall => "uninstall",
cli::SupervisionCommands::Status => "status",
cli::SupervisionCommands::Enable => "enable",
cli::SupervisionCommands::Disable => "disable",
}),
),
}
}
fn should_skip_command_event(_cmd: &Commands) -> bool {
false
}
fn dispatch(
command: &Commands,
output: &openlatch_client::cli::output::OutputConfig,
) -> Result<(), openlatch_client::error::OlError> {
match command {
Commands::Init(args) => openlatch_client::cli::commands::init::run_init(args, output),
Commands::Status => openlatch_client::cli::commands::status::run_status(output),
Commands::Start(args) => {
openlatch_client::cli::commands::lifecycle::run_start(args, output)
}
Commands::Stop => openlatch_client::cli::commands::lifecycle::run_stop(output),
Commands::Restart => openlatch_client::cli::commands::lifecycle::run_restart(output),
Commands::Logs(args) => openlatch_client::cli::commands::logs::run_logs(args, output),
Commands::Doctor(args) => openlatch_client::cli::commands::doctor::run_doctor(args, output),
Commands::Uninstall(args) => {
openlatch_client::cli::commands::uninstall::run_uninstall(args, output)
}
Commands::Docs => openlatch_client::cli::commands::docs::run_docs(output),
Commands::Hooks { cmd } => match cmd {
cli::HooksCommands::Install(args) => {
openlatch_client::cli::commands::init::run_init(args, output)
}
cli::HooksCommands::Uninstall(args) => {
openlatch_client::cli::commands::uninstall::run_uninstall(args, output)
}
cli::HooksCommands::Status => {
openlatch_client::cli::commands::status::run_status(output)
}
},
Commands::Daemon { cmd } => match cmd {
cli::DaemonCommands::Start(args) => {
openlatch_client::cli::commands::lifecycle::run_start(args, output)
}
cli::DaemonCommands::Stop => {
openlatch_client::cli::commands::lifecycle::run_stop(output)
}
cli::DaemonCommands::Restart => {
openlatch_client::cli::commands::lifecycle::run_restart(output)
}
},
Commands::Auth { cmd } => match cmd {
cli::AuthCommands::Login(args) => {
openlatch_client::cli::commands::auth::run_login(args, output)
}
cli::AuthCommands::Logout => openlatch_client::cli::commands::auth::run_logout(output),
cli::AuthCommands::Status => openlatch_client::cli::commands::auth::run_status(output),
},
Commands::Telemetry { cmd } => openlatch_client::cli::commands::telemetry::run(cmd, output),
Commands::Supervision { cmd } => {
openlatch_client::cli::commands::supervision::run(cmd, output)
}
}
}