1use agcodex_common::CliConfigOverrides;
2use agcodex_core::config::Config;
3use agcodex_core::config::ConfigOverrides;
4use agcodex_login::AuthMode;
5use agcodex_login::CLIENT_ID;
6use agcodex_login::CodexAuth;
7use agcodex_login::OPENAI_API_KEY_ENV_VAR;
8use agcodex_login::ServerOptions;
9use agcodex_login::login_with_api_key;
10use agcodex_login::logout;
11use agcodex_login::run_login_server;
12use std::env;
13use std::path::PathBuf;
14
15pub async fn login_with_chatgpt(codex_home: PathBuf) -> std::io::Result<()> {
16 let opts = ServerOptions::new(codex_home, CLIENT_ID.to_string());
17 let server = run_login_server(opts)?;
18
19 eprintln!(
20 "Starting local login server on http://localhost:{}.\nIf your browser did not open, navigate to this URL to authenticate:\n\n{}",
21 server.actual_port, server.auth_url,
22 );
23
24 server.block_until_done().await
25}
26
27pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! {
28 let config = load_config_or_exit(cli_config_overrides);
29
30 match login_with_chatgpt(config.codex_home).await {
31 Ok(_) => {
32 eprintln!("Successfully logged in");
33 std::process::exit(0);
34 }
35 Err(e) => {
36 eprintln!("Error logging in: {e}");
37 std::process::exit(1);
38 }
39 }
40}
41
42pub async fn run_login_with_api_key(
43 cli_config_overrides: CliConfigOverrides,
44 api_key: String,
45) -> ! {
46 let config = load_config_or_exit(cli_config_overrides);
47
48 match login_with_api_key(&config.codex_home, &api_key) {
49 Ok(_) => {
50 eprintln!("Successfully logged in");
51 std::process::exit(0);
52 }
53 Err(e) => {
54 eprintln!("Error logging in: {e}");
55 std::process::exit(1);
56 }
57 }
58}
59
60pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! {
61 let config = load_config_or_exit(cli_config_overrides);
62
63 match CodexAuth::from_codex_home(&config.codex_home, config.preferred_auth_method) {
64 Ok(Some(auth)) => match auth.mode {
65 AuthMode::ApiKey => match auth.get_token().await {
66 Ok(api_key) => {
67 eprintln!("Logged in using an API key - {}", safe_format_key(&api_key));
68
69 if let Ok(env_api_key) = env::var(OPENAI_API_KEY_ENV_VAR)
70 && env_api_key == api_key
71 {
72 eprintln!(
73 " API loaded from OPENAI_API_KEY environment variable or .env file"
74 );
75 }
76 std::process::exit(0);
77 }
78 Err(e) => {
79 eprintln!("Unexpected error retrieving API key: {e}");
80 std::process::exit(1);
81 }
82 },
83 AuthMode::ChatGPT => {
84 eprintln!("Logged in using ChatGPT");
85 std::process::exit(0);
86 }
87 },
88 Ok(None) => {
89 eprintln!("Not logged in");
90 std::process::exit(1);
91 }
92 Err(e) => {
93 eprintln!("Error checking login status: {e}");
94 std::process::exit(1);
95 }
96 }
97}
98
99pub async fn run_logout(cli_config_overrides: CliConfigOverrides) -> ! {
100 let config = load_config_or_exit(cli_config_overrides);
101
102 match logout(&config.codex_home) {
103 Ok(true) => {
104 eprintln!("Successfully logged out");
105 std::process::exit(0);
106 }
107 Ok(false) => {
108 eprintln!("Not logged in");
109 std::process::exit(0);
110 }
111 Err(e) => {
112 eprintln!("Error logging out: {e}");
113 std::process::exit(1);
114 }
115 }
116}
117
118fn load_config_or_exit(cli_config_overrides: CliConfigOverrides) -> Config {
119 let cli_overrides = match cli_config_overrides.parse_overrides() {
120 Ok(v) => v,
121 Err(e) => {
122 eprintln!("Error parsing -c overrides: {e}");
123 std::process::exit(1);
124 }
125 };
126
127 let config_overrides = ConfigOverrides::default();
128 match Config::load_with_cli_overrides(cli_overrides, config_overrides) {
129 Ok(config) => config,
130 Err(e) => {
131 eprintln!("Error loading configuration: {e}");
132 std::process::exit(1);
133 }
134 }
135}
136
137fn safe_format_key(key: &str) -> String {
138 if key.len() <= 13 {
139 return "***".to_string();
140 }
141 let prefix = &key[..8];
142 let suffix = &key[key.len() - 5..];
143 format!("{prefix}***{suffix}")
144}
145
146#[cfg(test)]
147mod tests {
148 use super::safe_format_key;
149
150 #[test]
151 fn formats_long_key() {
152 let key = "sk-proj-1234567890ABCDE";
153 assert_eq!(safe_format_key(key), "sk-proj-***ABCDE");
154 }
155
156 #[test]
157 fn short_key_returns_stars() {
158 let key = "sk-proj-12345";
159 assert_eq!(safe_format_key(key), "***");
160 }
161}