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 clap::CommandFactory;
96 use std::io::Write;
97
98 let raw: Vec<String> = std::env::args().collect();
99 let mut handle = std::io::stdout();
100 let build = match env!("GIT_SHA") {
101 "unknown" => None,
102 sha => Some(sha),
103 };
104 match agent_first_data::cli_handle_version_or_continue(
105 &raw,
106 &args::Cli::command(),
107 "afhttp",
108 Some(env!("DISPLAY_NAME")),
109 env!("CARGO_PKG_VERSION"),
110 build,
111 ) {
112 Ok(Some(version)) => {
113 let _ = write!(handle, "{version}");
114 Some(ExitCode::SUCCESS)
115 }
116 Ok(None) => None,
117 Err(err) => {
118 let err = Error::new(
119 crate::shared::error::ErrorCode::InvalidArgument,
120 err.to_string(),
121 );
122 let _ = crate::shared::afdata::emit_error(&mut handle, &err);
123 Some(ExitCode::from(2))
124 }
125 }
126}
127
128fn maybe_render_help() -> Option<ExitCode> {
130 use clap::CommandFactory;
131 use std::io::Write;
132
133 let raw: Vec<String> = std::env::args().collect();
134 let mut handle = std::io::stdout();
135 match agent_first_data::cli_handle_help_or_continue(
136 &raw,
137 &args::Cli::command(),
138 &agent_first_data::HelpConfig::human_cli_default(),
139 ) {
140 Ok(Some(help)) => {
141 let _ = write!(handle, "{help}");
142 Some(ExitCode::SUCCESS)
143 }
144 Ok(None) => None,
145 Err(err) => {
146 let err = Error::new(
147 crate::shared::error::ErrorCode::InvalidArgument,
148 err.to_string(),
149 );
150 let _ = crate::shared::afdata::emit_error(&mut handle, &err);
151 Some(ExitCode::from(2))
152 }
153 }
154}
155
156async fn dispatch(parsed: args::Parsed) -> Result<(), Error> {
157 let command = match parsed.command {
158 args::Command::Fetch(a) => {
159 return cmd::fetch::run(*a).await;
162 }
163 command => command,
164 };
165 let res = match command {
166 args::Command::Host(a) => cmd::host::run(a).await,
167 args::Command::Fetch(_) => unreachable!("fetch handled above"),
168 args::Command::Upload(a) => cmd::upload::run(a).await,
169 args::Command::Cdp(a) => cmd::cdp::run(a).await,
170 args::Command::Panel(a) => cmd::panel::run(a).await,
171 args::Command::Health(a) => cmd::health::run(a).await,
172 args::Command::Capabilities(a) => cmd::capabilities::run(a).await,
173 args::Command::Profile(a) => cmd::profile::run(a).await,
174 args::Command::Tabs(a) => cmd::tabs::run(a).await,
175 args::Command::Skill(a) => cmd::skill::run(a).await,
176 args::Command::Container(a) => cmd::container::run(a).await,
177 };
178 if let Err(ref e) = res {
179 emit_cli_error(e);
180 }
181 res
182}
183
184fn emit_cli_error(err: &Error) {
185 let stdout = std::io::stdout();
186 let mut handle = stdout.lock();
187 let _ = crate::shared::afdata::emit_error(&mut handle, err);
188}
189
190fn emit_bootstrap_error(msg: &str) {
191 let stdout = std::io::stdout();
194 let mut handle = stdout.lock();
195 let err = Error::new(crate::shared::error::ErrorCode::InternalError, msg);
196 let _ = crate::shared::afdata::emit_error(&mut handle, &err);
197}