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};
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 { json, verbose } => {
117 commands::run_status_command(json, verbose)?;
118 Ok(RunResult::Success)
119 }
120
121 Commands::Watch {
122 session,
123 event_type,
124 mode,
125 framework,
126 poll_ms,
127 } => {
128 let storage = storage_factory().context("failed to open storage")?;
129 commands::run_watch(
130 &storage,
131 commands::WatchOptions {
132 session,
133 event_type,
134 permission_mode: mode,
135 framework,
136 poll_ms,
137 },
138 )?;
139 Ok(RunResult::Success)
140 }
141
142 Commands::Gc { dry_run } => {
143 let storage = storage_factory().context("failed to open storage")?;
144 commands::run_gc(&storage, dry_run)?;
145 Ok(RunResult::Success)
146 }
147
148 Commands::Otel(otel_cmd) => run_otel(otel_cmd, storage_factory),
149
150 Commands::Upgrade {
151 version,
152 yes,
153 dry_run,
154 method,
155 source_path,
156 } => {
157 commands::run_upgrade(commands::UpgradeOptions {
158 version,
159 yes,
160 dry_run,
161 method,
162 source_path,
163 })?;
164 Ok(RunResult::Success)
165 }
166
167 Commands::Uninstall {
168 confirm,
169 keep_data,
170 dry_run,
171 } => {
172 commands::run_uninstall(commands::UninstallOptions {
173 confirm,
174 keep_data,
175 dry_run,
176 })?;
177 Ok(RunResult::Success)
178 }
179 }
180}
181
182fn run_ingest<S, F>(cmd: IngestCommands, storage_factory: F) -> Result<RunResult>
184where
185 S: Storage,
186 F: FnOnce() -> Result<S, StorageError>,
187{
188 match cmd {
189 IngestCommands::Event {
190 event_type,
191 json_payload,
192 framework,
193 } => {
194 let result: Result<(), anyhow::Error> = (|| {
198 let storage = storage_factory().context("failed to open storage")?;
199 let log_result =
200 commands::run_log(&storage, event_type, json_payload, framework.clone())?;
201
202 if let Some(ref path) = log_result.transcript_path {
205 let scanner = TranscriptScanner::new(&storage, &log_result.machine_id);
206 if let Err(e) = scanner.scan_file(std::path::Path::new(path)) {
207 eprintln!("mi6: warning: failed to scan transcript: {e:#}");
208 }
209
210 if let Err(e) = scanner
213 .backfill_initial_prompt(&log_result.session_id, std::path::Path::new(path))
214 {
215 eprintln!("mi6: warning: failed to backfill initial prompt: {e:#}");
216 }
217 }
218
219 if framework.as_deref() == Some("codex") {
222 let scanner = CodexSessionScanner::new(&storage, &log_result.machine_id);
223 if let Err(e) = scanner.scan_session(&log_result.session_id) {
224 eprintln!("mi6: warning: failed to scan codex session: {e:#}");
225 }
226 }
227
228 Ok(())
229 })();
230
231 if let Err(e) = result {
232 eprintln!("mi6: {:#}", e);
233 }
234 Ok(RunResult::ExitWithCode(0))
235 }
236
237 IngestCommands::Transcript { path } => {
238 let storage = storage_factory().context("failed to open storage")?;
239 commands::run_scan(&storage, &path)?;
240 Ok(RunResult::Success)
241 }
242
243 IngestCommands::CodexSession {
244 session_or_path,
245 file,
246 } => {
247 let storage = storage_factory().context("failed to open storage")?;
248 if file {
249 commands::run_codex_session_scan_file(
250 &storage,
251 std::path::Path::new(&session_or_path),
252 )?;
253 } else {
254 commands::run_codex_session_scan_by_id(&storage, &session_or_path)?;
255 }
256 Ok(RunResult::Success)
257 }
258
259 IngestCommands::Otel => {
260 let storage = storage_factory().context("failed to open storage")?;
261 commands::run_otel_ingest(&storage)?;
262 Ok(RunResult::Success)
263 }
264 }
265}
266
267fn run_otel<S, F>(cmd: OtelCommands, storage_factory: F) -> Result<RunResult>
269where
270 S: Storage,
271 F: FnOnce() -> Result<S, StorageError>,
272{
273 match cmd {
274 OtelCommands::Start { port, mode } => {
275 if commands::ensure_running(port, false, mode)? {
276 eprintln!("OTel server is running on port {}", port);
277 }
278 Ok(RunResult::Success)
279 }
280 OtelCommands::Stop { port } => {
281 commands::stop_server(port)?;
282 Ok(RunResult::Success)
283 }
284 OtelCommands::Restart { port, mode } => {
285 if commands::ensure_running(port, true, mode)? {
286 eprintln!("OTel server restarted on port {}", port);
287 }
288 Ok(RunResult::Success)
289 }
290 OtelCommands::Status { port } => {
291 commands::get_status(port)?;
292 Ok(RunResult::Success)
293 }
294 OtelCommands::Run { port, mode } => {
295 let storage = storage_factory().context("failed to open storage")?;
296 commands::run_server(&storage, port, mode)?;
297 Ok(RunResult::Success)
298 }
299 }
300}