1#![allow(clippy::print_stdout, clippy::print_stderr)]
3
4mod args;
5pub mod commands;
6pub mod display;
7mod error;
8mod help;
9pub mod process;
10
11pub use help::{print_command_help, print_help, print_subcommand_help};
12
13use anyhow::{Context, Result};
14use mi6_core::{CodexSessionScanner, Storage, StorageError, TranscriptScanner};
15
16pub use args::{Cli, Commands, IngestCommands, OtelCommands, TuiArgs, UpgradeMethod};
17pub use commands::{UpdateInfo, check_for_update};
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum RunResult {
22 Success,
24 ExitWithCode(i32),
26 RunTui(TuiArgs),
28}
29
30pub fn run<S, F>(cli: Cli, storage_factory: F) -> Result<RunResult>
35where
36 S: Storage,
37 F: FnOnce() -> Result<S, StorageError>,
38{
39 let command = match cli.command {
41 None => return Ok(RunResult::RunTui(cli.tui_args)),
42 Some(Commands::Tui(args)) => return Ok(RunResult::RunTui(args)),
43 Some(cmd) => cmd,
44 };
45
46 match command {
47 Commands::Tui(_) => unreachable!("handled above"),
48
49 Commands::Enable {
50 frameworks,
51 local,
52 settings_local,
53 print,
54 db_only,
55 hooks_only,
56 otel,
57 otel_port,
58 no_otel,
59 } => {
60 let result = commands::run_enable(commands::EnableCliOptions {
61 frameworks,
62 local,
63 settings_local,
64 print,
65 db_only,
66 hooks_only,
67 otel,
68 otel_port,
69 no_otel,
70 })?;
71
72 if result.should_init_db
73 && let Some(ref db_path) = result.db_path
74 {
75 let db_existed = db_path.exists();
76 let _ = storage_factory().context("failed to initialize storage")?;
77 if !db_existed {
78 eprintln!("Database initialized at: {}", db_path.display());
79 }
80 }
81
82 if !print {
83 eprintln!("\nSessions may need to be restarted to use the new configuration.");
84 }
85
86 Ok(RunResult::Success)
87 }
88
89 Commands::Disable {
90 frameworks,
91 local,
92 settings_local,
93 print,
94 } => {
95 commands::run_disable(commands::DisableOptions {
96 frameworks,
97 local,
98 settings_local,
99 print,
100 })?;
101 Ok(RunResult::Success)
102 }
103
104 Commands::Ingest(ingest_cmd) => run_ingest(ingest_cmd, storage_factory),
105
106 Commands::Session {
107 session_or_pid,
108 machine,
109 json,
110 fields,
111 } => {
112 let storage = storage_factory().context("failed to open storage")?;
113 commands::run_session(&storage, session_or_pid, machine, json, fields)?;
114 Ok(RunResult::Success)
115 }
116
117 Commands::Status {
118 json,
119 verbose,
120 check,
121 } => {
122 commands::run_status_command(json, verbose, check)?;
123 Ok(RunResult::Success)
124 }
125
126 Commands::Watch {
127 session,
128 event_type,
129 mode,
130 framework,
131 poll_ms,
132 } => {
133 let storage = storage_factory().context("failed to open storage")?;
134 commands::run_watch(
135 &storage,
136 commands::WatchOptions {
137 session,
138 event_type,
139 permission_mode: mode,
140 framework,
141 poll_ms,
142 },
143 )?;
144 Ok(RunResult::Success)
145 }
146
147 Commands::Cleanup { dry_run } => {
148 let storage = storage_factory().context("failed to open storage")?;
149 commands::run_cleanup(&storage, dry_run)?;
150 Ok(RunResult::Success)
151 }
152
153 Commands::Otel(otel_cmd) => run_otel(otel_cmd, storage_factory),
154
155 Commands::Upgrade {
156 version,
157 yes,
158 dry_run,
159 method,
160 source_path,
161 } => {
162 commands::run_upgrade(commands::UpgradeOptions {
163 version,
164 yes,
165 dry_run,
166 method,
167 source_path,
168 })?;
169 Ok(RunResult::Success)
170 }
171
172 Commands::Uninstall {
173 yes,
174 keep_data,
175 dry_run,
176 } => {
177 commands::run_uninstall(commands::UninstallOptions {
178 yes,
179 keep_data,
180 dry_run,
181 })?;
182 Ok(RunResult::Success)
183 }
184 }
185}
186
187fn run_ingest<S, F>(cmd: IngestCommands, storage_factory: F) -> Result<RunResult>
189where
190 S: Storage,
191 F: FnOnce() -> Result<S, StorageError>,
192{
193 match cmd {
194 IngestCommands::Event {
195 event_type,
196 json_payload,
197 framework,
198 ping,
199 } => {
200 if ping {
202 println!("pong");
203 return Ok(RunResult::Success);
204 }
205
206 let result: Result<(), anyhow::Error> = (|| {
210 let storage = storage_factory().context("failed to open storage")?;
211 let log_result =
212 commands::run_log(&storage, event_type, json_payload, framework.clone())?;
213
214 if let Some(ref path) = log_result.transcript_path {
217 let scanner = TranscriptScanner::new(&storage, &log_result.machine_id);
218 if let Err(e) = scanner.scan_file(std::path::Path::new(path)) {
219 eprintln!("mi6: warning: failed to scan transcript: {e:#}");
220 }
221
222 if let Err(e) = scanner
225 .backfill_initial_prompt(&log_result.session_id, std::path::Path::new(path))
226 {
227 eprintln!("mi6: warning: failed to backfill initial prompt: {e:#}");
228 }
229 }
230
231 if framework.as_deref() == Some("codex") {
234 let scanner = CodexSessionScanner::new(&storage, &log_result.machine_id);
235 if let Err(e) = scanner.scan_session(&log_result.session_id) {
236 eprintln!("mi6: warning: failed to scan codex session: {e:#}");
237 }
238 }
239
240 Ok(())
241 })();
242
243 if let Err(e) = result {
244 eprintln!("mi6: {:#}", e);
245 }
246 Ok(RunResult::ExitWithCode(0))
247 }
248
249 IngestCommands::Transcript { path } => {
250 let storage = storage_factory().context("failed to open storage")?;
251 commands::run_scan(&storage, &path)?;
252 Ok(RunResult::Success)
253 }
254
255 IngestCommands::CodexSession {
256 session_or_path,
257 file,
258 } => {
259 let storage = storage_factory().context("failed to open storage")?;
260 if file {
261 commands::run_codex_session_scan_file(
262 &storage,
263 std::path::Path::new(&session_or_path),
264 )?;
265 } else {
266 commands::run_codex_session_scan_by_id(&storage, &session_or_path)?;
267 }
268 Ok(RunResult::Success)
269 }
270
271 IngestCommands::Otel => {
272 let storage = storage_factory().context("failed to open storage")?;
273 commands::run_otel_ingest(&storage)?;
274 Ok(RunResult::Success)
275 }
276 }
277}
278
279fn run_otel<S, F>(cmd: OtelCommands, storage_factory: F) -> Result<RunResult>
281where
282 S: Storage,
283 F: FnOnce() -> Result<S, StorageError>,
284{
285 match cmd {
286 OtelCommands::Start { port, mode } => {
287 if commands::ensure_running(port, false, mode)? {
288 eprintln!("OTel server is running on port {}", port);
289 }
290 Ok(RunResult::Success)
291 }
292 OtelCommands::Stop { port } => {
293 commands::stop_server(port)?;
294 Ok(RunResult::Success)
295 }
296 OtelCommands::Restart { port, mode } => {
297 if commands::ensure_running(port, true, mode)? {
298 eprintln!("OTel server restarted on port {}", port);
299 }
300 Ok(RunResult::Success)
301 }
302 OtelCommands::Status { port } => {
303 commands::get_status(port)?;
304 Ok(RunResult::Success)
305 }
306 OtelCommands::Run { port, mode } => {
307 let storage = storage_factory().context("failed to open storage")?;
308 commands::run_server(&storage, port, mode)?;
309 Ok(RunResult::Success)
310 }
311 }
312}