agent_first_http/cli/
mod.rs1pub mod args;
4pub mod cmd;
5pub mod output;
6
7use std::process::ExitCode;
8
9use crate::shared::error::Error;
10
11pub fn run() -> ExitCode {
15 let _stream_redirect =
16 match agent_first_data::stream_redirect::install_from_raw_args(std::env::args()) {
17 Ok(redirect) => redirect,
18 Err(err) => {
19 emit_bootstrap_error(&err.to_string());
20 return ExitCode::from(2);
21 }
22 };
23
24 crate::host::bootstrap::install_rustls_provider();
29
30 if let Some(code) = maybe_render_version() {
33 return code;
34 }
35 if let Some(code) = maybe_render_help() {
38 return code;
39 }
40 match std::thread::Builder::new()
46 .name("afhttp-main".to_string())
47 .stack_size(16 * 1024 * 1024)
48 .spawn(run_blocking)
49 {
50 Ok(handle) => match handle.join() {
51 Ok(code) => code,
52 Err(_) => {
53 emit_bootstrap_error("afhttp worker thread panicked");
54 ExitCode::from(2)
55 }
56 },
57 Err(e) => {
58 emit_bootstrap_error(&format!("spawn worker thread: {e}"));
59 ExitCode::from(2)
60 }
61 }
62}
63
64fn run_blocking() -> ExitCode {
67 let rt = match tokio::runtime::Builder::new_multi_thread()
68 .enable_all()
69 .thread_stack_size(16 * 1024 * 1024)
70 .build()
71 {
72 Ok(rt) => rt,
73 Err(e) => {
74 emit_bootstrap_error(&format!("tokio runtime: {e}"));
75 return ExitCode::from(2);
76 }
77 };
78 let exit = rt.block_on(async {
79 match args::parse() {
80 Ok(parsed) => dispatch(parsed).await,
81 Err(err) => {
82 emit_cli_error(&err);
83 Err(err)
84 }
85 }
86 });
87 match exit {
88 Ok(()) => ExitCode::SUCCESS,
89 Err(_) => ExitCode::from(1),
90 }
91}
92
93fn maybe_render_version() -> Option<ExitCode> {
95 use std::io::Write;
96
97 let raw: Vec<String> = std::env::args().collect();
98 let mut handle = std::io::stdout();
99 match agent_first_data::cli_handle_version_or_continue(
100 &raw,
101 "afhttp",
102 env!("CARGO_PKG_VERSION"),
103 &agent_first_data::VersionConfig::conventional_default().with_protocol_v1(),
104 ) {
105 Ok(Some(version)) => {
106 let _ = write!(handle, "{version}");
107 Some(ExitCode::SUCCESS)
108 }
109 Ok(None) => None,
110 Err(err) => {
111 let err = Error::new(
112 crate::shared::error::ErrorCode::InvalidArgument,
113 err.to_string(),
114 );
115 let _ = crate::shared::afdata::emit_error(&mut handle, &err);
116 Some(ExitCode::from(2))
117 }
118 }
119}
120
121fn maybe_render_help() -> Option<ExitCode> {
123 use clap::CommandFactory;
124 use std::io::Write;
125
126 let raw: Vec<String> = std::env::args().collect();
127 let mut handle = std::io::stdout();
128 match agent_first_data::cli_handle_help_or_continue(
129 &raw,
130 &args::Cli::command(),
131 &agent_first_data::HelpConfig::human_cli_default().with_protocol_v1(),
132 ) {
133 Ok(Some(help)) => {
134 let _ = write!(handle, "{help}");
135 Some(ExitCode::SUCCESS)
136 }
137 Ok(None) => None,
138 Err(err) => {
139 let err = Error::new(
140 crate::shared::error::ErrorCode::InvalidArgument,
141 err.to_string(),
142 );
143 let _ = crate::shared::afdata::emit_error(&mut handle, &err);
144 Some(ExitCode::from(2))
145 }
146 }
147}
148
149async fn dispatch(parsed: args::Parsed) -> Result<(), Error> {
150 let command = match parsed.command {
151 args::Command::Fetch(a) => {
152 return cmd::fetch::run(*a).await;
155 }
156 command => command,
157 };
158 let res = match command {
159 args::Command::Host(a) => cmd::host::run(a).await,
160 args::Command::Fetch(_) => unreachable!("fetch handled above"),
161 args::Command::Upload(a) => cmd::upload::run(a).await,
162 args::Command::Cdp(a) => cmd::cdp::run(a).await,
163 args::Command::Panel(a) => cmd::panel::run(a).await,
164 args::Command::Health(a) => cmd::health::run(a).await,
165 args::Command::Capabilities(a) => cmd::capabilities::run(a).await,
166 args::Command::Profile(a) => cmd::profile::run(a).await,
167 args::Command::Tabs(a) => cmd::tabs::run(a).await,
168 args::Command::Skill(a) => cmd::skill::run(a).await,
169 args::Command::Container(a) => cmd::container::run(a).await,
170 };
171 if let Err(ref e) = res {
172 emit_cli_error(e);
173 }
174 res
175}
176
177fn emit_cli_error(err: &Error) {
178 let stdout = std::io::stdout();
179 let mut handle = stdout.lock();
180 let _ = crate::shared::afdata::emit_error(&mut handle, err);
181}
182
183fn emit_bootstrap_error(msg: &str) {
184 let stdout = std::io::stdout();
187 let mut handle = stdout.lock();
188 let err = Error::new(crate::shared::error::ErrorCode::InternalError, msg);
189 let _ = crate::shared::afdata::emit_error(&mut handle, &err);
190}