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