From 301ec72107f107ac5c6103660e276a25bc18273e Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Wed, 30 Jul 2025 14:09:26 -0700 Subject: [PATCH] Add login status command (#1716) Print the current login mode, sanitized key and return an appropriate status. --- README.md | 6 +++ codex-rs/cli/src/login.rs | 93 ++++++++++++++++++++++++++++++++------- codex-rs/cli/src/main.rs | 21 ++++++++- 3 files changed, 102 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 60e44298..c7f6a1d5 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,12 @@ codex login If you complete the process successfully, you should have a `~/.codex/auth.json` file that contains the credentials that Codex will use. +To verify whether you are currently logged in, run: + +``` +codex login status +``` + If you encounter problems with the login flow, please comment on .
diff --git a/codex-rs/cli/src/login.rs b/codex-rs/cli/src/login.rs index af3fb667..390c3100 100644 --- a/codex-rs/cli/src/login.rs +++ b/codex-rs/cli/src/login.rs @@ -1,25 +1,12 @@ use codex_common::CliConfigOverrides; use codex_core::config::Config; use codex_core::config::ConfigOverrides; +use codex_login::AuthMode; +use codex_login::load_auth; use codex_login::login_with_chatgpt; pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! { - let cli_overrides = match cli_config_overrides.parse_overrides() { - Ok(v) => v, - Err(e) => { - eprintln!("Error parsing -c overrides: {e}"); - std::process::exit(1); - } - }; - - let config_overrides = ConfigOverrides::default(); - let config = match Config::load_with_cli_overrides(cli_overrides, config_overrides) { - Ok(config) => config, - Err(e) => { - eprintln!("Error loading configuration: {e}"); - std::process::exit(1); - } - }; + let config = load_config_or_exit(cli_config_overrides); let capture_output = false; match login_with_chatgpt(&config.codex_home, capture_output).await { @@ -33,3 +20,77 @@ pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> } } } + +pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! { + let config = load_config_or_exit(cli_config_overrides); + + match load_auth(&config.codex_home) { + Ok(Some(auth)) => match auth.mode { + AuthMode::ApiKey => { + if let Some(api_key) = auth.api_key.as_deref() { + eprintln!("Logged in using an API key - {}", safe_format_key(api_key)); + } else { + eprintln!("Logged in using an API key"); + } + std::process::exit(0); + } + AuthMode::ChatGPT => { + eprintln!("Logged in using ChatGPT"); + std::process::exit(0); + } + }, + Ok(None) => { + eprintln!("Not logged in"); + std::process::exit(1); + } + Err(e) => { + eprintln!("Error checking login status: {e}"); + std::process::exit(1); + } + } +} + +fn load_config_or_exit(cli_config_overrides: CliConfigOverrides) -> Config { + let cli_overrides = match cli_config_overrides.parse_overrides() { + Ok(v) => v, + Err(e) => { + eprintln!("Error parsing -c overrides: {e}"); + std::process::exit(1); + } + }; + + let config_overrides = ConfigOverrides::default(); + match Config::load_with_cli_overrides(cli_overrides, config_overrides) { + Ok(config) => config, + Err(e) => { + eprintln!("Error loading configuration: {e}"); + std::process::exit(1); + } + } +} + +fn safe_format_key(key: &str) -> String { + if key.len() <= 13 { + return "***".to_string(); + } + let prefix = &key[..8]; + let suffix = &key[key.len() - 5..]; + format!("{prefix}***{suffix}") +} + +#[cfg(test)] +mod tests { + use super::safe_format_key; + + #[test] + fn formats_long_key() { + let key = "sk-proj-1234567890ABCDE"; + assert_eq!(safe_format_key(key), "sk-proj-***ABCDE"); + } + + #[test] + fn short_key_returns_stars() { + let key = "sk-proj-12345"; + assert_eq!(safe_format_key(key), "***"); + } +} diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index 6dd596ff..c5fd69f9 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -7,6 +7,7 @@ use codex_chatgpt::apply_command::ApplyCommand; use codex_chatgpt::apply_command::run_apply_command; use codex_cli::LandlockCommand; use codex_cli::SeatbeltCommand; +use codex_cli::login::run_login_status; use codex_cli::login::run_login_with_chatgpt; use codex_cli::proto; use codex_common::CliConfigOverrides; @@ -43,7 +44,7 @@ enum Subcommand { #[clap(visible_alias = "e")] Exec(ExecCli), - /// Login with ChatGPT. + /// Manage login. Login(LoginCommand), /// Experimental: run Codex as an MCP server. @@ -90,6 +91,15 @@ enum DebugCommand { struct LoginCommand { #[clap(skip)] config_overrides: CliConfigOverrides, + + #[command(subcommand)] + action: Option, +} + +#[derive(Debug, clap::Subcommand)] +enum LoginSubcommand { + /// Show login status. + Status, } fn main() -> anyhow::Result<()> { @@ -118,7 +128,14 @@ async fn cli_main(codex_linux_sandbox_exe: Option) -> anyhow::Result<() } Some(Subcommand::Login(mut login_cli)) => { prepend_config_flags(&mut login_cli.config_overrides, cli.config_overrides); - run_login_with_chatgpt(login_cli.config_overrides).await; + match login_cli.action { + Some(LoginSubcommand::Status) => { + run_login_status(login_cli.config_overrides).await; + } + None => { + run_login_with_chatgpt(login_cli.config_overrides).await; + } + } } Some(Subcommand::Proto(mut proto_cli)) => { prepend_config_flags(&mut proto_cli.config_overrides, cli.config_overrides);