use anyhow::Result;
use colored::Colorize as _;
use serde::Serialize;
use crate::utils::auth::{
auth_mode_has_api_key, auth_mode_has_chatgpt, auth_mode_label, detect_auth_mode,
read_email_from_codex_dir,
};
use crate::utils::config::Config;
pub async fn execute(config: Config, json: bool, quiet: bool) -> Result<()> {
let codex_dir = config.codex_dir();
let summary = build_status_summary(&config).await;
if json {
println!("{}", serde_json::to_string_pretty(&summary)?);
return Ok(());
}
if !quiet {
println!(
"{} {}",
"Codex Controller".bold(),
env!("CARGO_PKG_VERSION")
);
println!();
let codex_installed = which::which("codex").is_ok();
println!(
" {}: {}",
"(`Codex` CLI)".dimmed(),
if codex_installed {
"installed ✓".green()
} else {
"not found".red()
}
);
println!(" {}: {}", "`Codex` Dir".dimmed(), codex_dir.display());
if codex_dir.exists() {
let current_profile = summary.current_profile.clone();
let previous_profile = summary.previous_profile.clone();
println!(
" {}: {}",
"Current Profile".dimmed(),
format_profile_marker(¤t_profile)
);
if let Some(previous) = previous_profile {
println!(" {}: {}", "Previous Profile".dimmed(), previous.cyan());
}
if !summary.auth_file_present {
println!(" {}: {}", "Auth Mode".dimmed(), "not logged in".yellow());
show_profiles_info(config, quiet);
return Ok(());
}
println!(
" {}: {}",
"Auth Mode".dimmed(),
auth_mode_label(&summary.auth_mode).cyan()
);
if let Some(email) = summary.current_email.as_ref() {
println!(" {}: {}", "Current Email".dimmed(), email.green());
} else {
println!(
" {}: {}",
"Current Email".dimmed(),
"not available in this auth mode".yellow()
);
}
println!(
" {}: {}",
"Plan Claims".dimmed(),
capability_status(summary.plan_claims_available)
);
println!(
" {}: {}",
"API Realtime".dimmed(),
capability_status(summary.api_realtime_available)
);
println!("\n {} Critical Files:", "•".cyan());
for file in &summary.critical_files {
let exists = file.exists;
let status = if exists { "✓".green() } else { "✗".red() };
let missing_msg = if exists { "" } else { "(missing)" };
println!(
" {status} {} {missing_msg}",
file.path,
missing_msg = missing_msg.dimmed()
);
}
} else {
println!(" {}: {}", "Status".dimmed(), "Not configured".yellow());
}
show_profiles_info(config, quiet);
}
Ok(())
}
#[derive(Debug, Serialize)]
struct StatusSummary {
version: String,
codex_installed: bool,
codex_dir: String,
profiles_dir: String,
profile_count: usize,
current_profile: Option<String>,
previous_profile: Option<String>,
auth_file_present: bool,
auth_mode: String,
auth_mode_label: String,
current_email: Option<String>,
plan_claims_available: bool,
api_realtime_available: bool,
critical_files: Vec<CriticalFileStatus>,
}
#[derive(Debug, Serialize)]
struct CriticalFileStatus {
path: String,
exists: bool,
}
async fn build_status_summary(config: &Config) -> StatusSummary {
let codex_dir = config.codex_dir();
let current_profile = read_profile_marker(config.profiles_dir(), ".current_profile").await;
let previous_profile = read_profile_marker(config.profiles_dir(), ".previous_profile").await;
let auth_path = codex_dir.join("auth.json");
let auth_file_present = auth_path.exists();
let auth_mode = if auth_file_present {
tokio::fs::read_to_string(&auth_path)
.await
.ok()
.and_then(|content| serde_json::from_str::<serde_json::Value>(&content).ok())
.map(|auth_json| detect_auth_mode(&auth_json))
.unwrap_or_else(|| "unknown".to_string())
} else {
"unknown".to_string()
};
let critical_files = Config::critical_files()
.iter()
.map(|file| CriticalFileStatus {
path: (*file).to_string(),
exists: codex_dir.join(file).exists(),
})
.collect();
StatusSummary {
version: env!("CARGO_PKG_VERSION").to_string(),
codex_installed: which::which("codex").is_ok(),
codex_dir: codex_dir.display().to_string(),
profiles_dir: config.profiles_dir().display().to_string(),
profile_count: count_profiles(config.profiles_dir()),
current_profile,
previous_profile,
auth_file_present,
auth_mode: auth_mode.clone(),
auth_mode_label: auth_mode_label(&auth_mode).to_string(),
current_email: read_email_from_codex_dir(codex_dir).await,
plan_claims_available: auth_mode_has_chatgpt(&auth_mode),
api_realtime_available: auth_mode_has_api_key(&auth_mode),
critical_files,
}
}
async fn read_profile_marker(profiles_dir: &std::path::Path, marker_name: &str) -> Option<String> {
let marker = profiles_dir.join(marker_name);
let content = tokio::fs::read_to_string(marker).await.ok()?;
let trimmed = content.trim();
if trimmed.is_empty() {
None
} else {
Some(trimmed.to_string())
}
}
fn format_profile_marker(marker: &Option<String>) -> colored::ColoredString {
match marker {
Some(name) => name.cyan(),
None => "ad hoc / untracked".yellow(),
}
}
fn capability_status(available: bool) -> colored::ColoredString {
if available {
"available".green()
} else {
"not available".yellow()
}
}
#[allow(clippy::needless_pass_by_value)]
fn show_profiles_info(config: Config, quiet: bool) {
let profiles_dir = config.profiles_dir();
let profile_count = count_profiles(config.profiles_dir());
if !quiet {
println!(
"\n {}: {} saved profiles",
"Profiles".dimmed(),
profile_count.to_string().cyan()
);
println!(" {}: {}", "Profile Dir".dimmed(), profiles_dir.display());
}
}
fn count_profiles(profiles_dir: &std::path::Path) -> usize {
if !profiles_dir.exists() {
return 0;
}
std::fs::read_dir(profiles_dir)
.map(|entries| {
entries
.filter(|e| {
e.as_ref()
.map(|entry| {
let path = entry.path();
if !path.is_dir() {
return false;
}
let name = entry.file_name().to_string_lossy().to_string();
name != "backups" && !name.starts_with('.')
})
.unwrap_or(false)
})
.count()
})
.unwrap_or(0)
}