From ec1c7b436603c7585ec5b5b8bd64e5674a95a202 Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Mon, 12 Oct 2026 17:15:35 +0530 Subject: [PATCH 01/17] whoami: --short prints one line for scripts and prompts autter whoami --short (-s) prints ` () ` and keeps the full report's exit rule (fails when not logged in and no API key). Unit tests for the formatter, the exit rule and API-key masking. Test PR for Autter's product walk. Co-Authored-By: Claude Opus 5.5 --- src/commands/whoami.rs | 88 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/src/commands/whoami.rs b/src/commands/whoami.rs index 4d15a85..f7a3cdb 100644 --- a/src/commands/whoami.rs +++ b/src/commands/whoami.rs @@ -11,12 +11,29 @@ pub fn handle_whoami(args: &[String]) { std::process::exit(0); } - if !args.is_empty() { - eprintln!("Error: unknown whoami argument(s): {}", args.join(" ")); + let short = args.iter().any(|arg| arg.as_str() == "--short" || arg.as_str() == "-s"); + let unknown: Vec<&str> = args + .iter() + .map(|arg| arg.as_str()) + .filter(|arg| *arg != "--short" && *arg != "-s") + .collect(); + if !unknown.is_empty() { + eprintln!("Error: unknown whoami argument(s): {}", unknown.join(" ")); print_help(); std::process::exit(crate::commands::EXIT_USAGE_ERROR); } + if short { + let auth = collect_auth_status(); + let has_api_key = ApiContext::new(None).api_key.is_some(); + let org_slug = auth.orgs.first().and_then(|org| org.org_slug.as_deref()); + println!("{}", short_identity(&auth.state, auth.email.as_deref(), org_slug)); + if identity_failed(&auth.state, has_api_key) { + std::process::exit(1); + } + return; + } + // Use Config::fresh() to support runtime config updates (daemon mode) let api_base_url = config::Config::fresh().api_base_url().to_string(); let auth = collect_auth_status(); @@ -134,10 +151,77 @@ fn mask_api_key(key: &str) -> String { format!("{}...{}", prefix, suffix) } +/// The auth state in a few words, for `autter whoami --short`. +fn state_label(state: &AuthState) -> String { + match state { + AuthState::LoggedOut => "logged out".to_string(), + AuthState::LoggedIn => "logged in".to_string(), + AuthState::SyncBlocked => "cloud sync blocked".to_string(), + AuthState::RefreshExpired => "credentials expired".to_string(), + AuthState::Error(err) => format!("error: {}", err), + } +} + +/// One line for scripts and shell prompts: ` () `. +fn short_identity(state: &AuthState, email: Option<&str>, org_slug: Option<&str>) -> String { + let mut parts: Vec = Vec::new(); + if let Some(email) = email { + parts.push(email.to_string()); + } + if let Some(org) = org_slug { + parts.push(format!("({})", org)); + } + parts.push(state_label(state)); + parts.join(" ") +} + +/// Same rule as the full report: a session that is not logged in fails +/// unless an API key is configured. +fn identity_failed(state: &AuthState, has_api_key: bool) -> bool { + *state != AuthState::LoggedIn && !has_api_key +} + fn print_help() { eprintln!("autter whoami - Show current auth state and identity"); eprintln!(); eprintln!("Usage:"); eprintln!(" autter whoami"); + eprintln!(" autter whoami --short One line: () "); eprintln!(" autter whoami --help"); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn short_identity_names_the_user_org_and_state() { + assert_eq!( + short_identity(&AuthState::LoggedIn, Some("ada@example.com"), Some("acme")), + "ada@example.com (acme) logged in" + ); + assert_eq!( + short_identity(&AuthState::RefreshExpired, Some("ada@example.com"), None), + "ada@example.com credentials expired" + ); + assert_eq!(short_identity(&AuthState::LoggedOut, None, None), "logged out"); + assert_eq!( + short_identity(&AuthState::Error("keychain locked".to_string()), None, None), + "error: keychain locked" + ); + } + + #[test] + fn identity_fails_only_without_a_login_or_an_api_key() { + assert!(!identity_failed(&AuthState::LoggedIn, false)); + assert!(identity_failed(&AuthState::LoggedOut, false)); + assert!(!identity_failed(&AuthState::LoggedOut, true)); + assert!(identity_failed(&AuthState::SyncBlocked, false)); + } + + #[test] + fn api_keys_are_masked() { + assert_eq!(mask_api_key("short"), "*****"); + assert_eq!(mask_api_key("abcd1234efgh5678"), "abcd...5678"); + } +} From 8218fb60e23ffff281fa8144e4a3695b181e715d Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Mon, 12 Oct 2026 23:12:22 +0530 Subject: [PATCH 02/17] Re-run Autter product walk (after walk fixes #426-#428) From dbd4bc30b89e09854327e94bd885f9e3aa624326 Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Mon, 12 Oct 2026 23:13:08 +0530 Subject: [PATCH 03/17] Re-run Autter's product walk on the fixed runner (test PR) Co-Authored-By: Claude Opus 5.5 From f7104fd21e6da7719fa75cdffcfafda3f76cb408 Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Mon, 12 Oct 2026 23:42:54 +0530 Subject: [PATCH 04/17] Re-run Autter product walk (after fixes #429) From 0798db90d49d65a5a3f56244cdd4f8a9e5e811a3 Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 00:00:01 +0530 Subject: [PATCH 05/17] Re-run Autter product walk (after fixes #430) From 1d44c88f4f30c4ad36b7b4098612414527a85586 Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 00:16:26 +0530 Subject: [PATCH 06/17] Re-run Autter product walk (after fixes #431) From c9f5306d7eacc29eb052dc1a51cdfbbcce68f39f Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 01:10:26 +0530 Subject: [PATCH 07/17] Re-run Autter product walk (after fixes #434 #433) From e2e90b591bcc028fad8c51fb4bbf4aed05f6e921 Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 01:39:39 +0530 Subject: [PATCH 08/17] Re-run Autter product walk (after fixes #437) From 2d57de5c07b03bdbaa60f09f21eb4c2315aaed6d Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 02:19:46 +0530 Subject: [PATCH 09/17] Re-run Autter product walk (after fixes #439) From ba90c30eb8dd290d659a707de7cea1a3add32be7 Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 02:51:41 +0530 Subject: [PATCH 10/17] Re-run Autter product walk (after fixes #443 #440) From a8b0b337ea32ae3d9c4df3448c17f2eb83499afd Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 03:25:47 +0530 Subject: [PATCH 11/17] Re-run Autter product walk (after fixes #446) From 58d0511b1d7ad459f3d100cab9c4c07ba9b52527 Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 03:44:45 +0530 Subject: [PATCH 12/17] Re-run Autter product walk (after fixes #447) From 8232711582327637b1d7362e8586aa882fb33522 Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 10:10:28 +0530 Subject: [PATCH 13/17] Re-run Autter product walk (after fixes #448) From 84427ec36690866ee5c1abdca3aaffe049d2a1e5 Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 10:36:49 +0530 Subject: [PATCH 14/17] Re-run Autter product walk (after fixes #450) From 7b2b7c4c544a3616743ac52c0437587e5fb4affd Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 11:10:03 +0530 Subject: [PATCH 15/17] Re-run Autter product walk (after fixes #454) From c96296388f30abea7fd97c9f0d36022c35187b63 Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 11:41:11 +0530 Subject: [PATCH 16/17] Re-run Autter product walk (after fixes #458) From 46159e97e8940d9369f7690695981b8785c4c24c Mon Sep 17 00:00:00 2001 From: Sagnik Ghosh Date: Tue, 13 Oct 2026 15:47:59 +0530 Subject: [PATCH 17/17] Re-run Autter product walk (after fixes #459)