pub mod args;
pub mod cmd;
pub mod output;
use std::process::ExitCode;
use crate::shared::error::Error;
pub fn run() -> ExitCode {
crate::host::bootstrap::install_rustls_provider();
if let Some(code) = maybe_render_help() {
return code;
}
match std::thread::Builder::new()
.name("afhttp-main".to_string())
.stack_size(16 * 1024 * 1024)
.spawn(run_blocking)
{
Ok(handle) => match handle.join() {
Ok(code) => code,
Err(_) => {
emit_bootstrap_error("afhttp worker thread panicked");
ExitCode::from(2)
}
},
Err(e) => {
emit_bootstrap_error(&format!("spawn worker thread: {e}"));
ExitCode::from(2)
}
}
}
fn run_blocking() -> ExitCode {
let rt = match tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_stack_size(16 * 1024 * 1024)
.build()
{
Ok(rt) => rt,
Err(e) => {
emit_bootstrap_error(&format!("tokio runtime: {e}"));
return ExitCode::from(2);
}
};
let exit = rt.block_on(async {
match args::parse() {
Ok(parsed) => dispatch(parsed).await,
Err(err) => {
emit_cli_error(&err);
Err(err)
}
}
});
match exit {
Ok(()) => ExitCode::SUCCESS,
Err(_) => ExitCode::from(1),
}
}
fn maybe_render_help() -> Option<ExitCode> {
use clap::CommandFactory;
use std::io::Write;
let raw: Vec<String> = std::env::args().collect();
let mut handle = std::io::stdout();
if raw.len() == 2 && matches!(raw[1].as_str(), "--help" | "-h") {
let _ = writeln!(
handle,
"{}",
agent_first_data::cli_render_help(&args::Cli::command(), &[])
);
return Some(ExitCode::SUCCESS);
}
let wants_markdown = raw
.iter()
.skip(1)
.take_while(|a| a.as_str() != "--")
.any(|a| a == "--help-markdown");
if wants_markdown {
let _ = writeln!(
handle,
"{}",
agent_first_data::cli_render_help_markdown(&args::Cli::command(), &[])
);
return Some(ExitCode::SUCCESS);
}
None
}
async fn dispatch(parsed: args::Parsed) -> Result<(), Error> {
let res = match parsed.command {
args::Command::Host(a) => cmd::host::run(a).await,
args::Command::Fetch(a) => cmd::fetch::run(*a).await,
args::Command::Upload(a) => cmd::upload::run(a).await,
args::Command::Cdp(a) => cmd::cdp::run(a).await,
args::Command::Ui(a) => cmd::ui::run(a).await,
args::Command::Health(a) => cmd::health::run(a).await,
args::Command::Capabilities(a) => cmd::capabilities::run(a).await,
args::Command::Profile(a) => cmd::profile::run(a).await,
args::Command::Tabs(a) => cmd::tabs::run(a).await,
args::Command::Skill(a) => cmd::skill::run(a).await,
args::Command::Container(a) => cmd::container::run(a).await,
};
if let Err(ref e) = res {
emit_cli_error(e);
}
res
}
fn emit_cli_error(err: &Error) {
let stdout = std::io::stdout();
let mut handle = stdout.lock();
let _ = crate::shared::envelope::emit_error(&mut handle, err);
}
fn emit_bootstrap_error(msg: &str) {
use std::io::Write;
let stdout = std::io::stdout();
let mut handle = stdout.lock();
let _ = writeln!(
handle,
"{{\"code\":\"error\",\"error_code\":\"internal_error\",\"error\":{},\"retryable\":false}}",
serde_json::Value::String(msg.to_string())
);
}