Skip to main content

codex_login/
auth_env_telemetry.rs

1use codex_model_provider_info::ModelProviderInfo;
2use codex_otel::AuthEnvTelemetryMetadata;
3
4use crate::CODEX_API_KEY_ENV_VAR;
5use crate::OPENAI_API_KEY_ENV_VAR;
6use crate::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR;
7
8#[derive(Debug, Clone, Default, PartialEq, Eq)]
9pub struct AuthEnvTelemetry {
10    pub openai_api_key_env_present: bool,
11    pub codex_api_key_env_present: bool,
12    pub codex_api_key_env_enabled: bool,
13    pub provider_env_key_name: Option<String>,
14    pub provider_env_key_present: Option<bool>,
15    pub refresh_token_url_override_present: bool,
16}
17
18impl AuthEnvTelemetry {
19    pub fn to_otel_metadata(&self) -> AuthEnvTelemetryMetadata {
20        AuthEnvTelemetryMetadata {
21            openai_api_key_env_present: self.openai_api_key_env_present,
22            codex_api_key_env_present: self.codex_api_key_env_present,
23            codex_api_key_env_enabled: self.codex_api_key_env_enabled,
24            provider_env_key_name: self.provider_env_key_name.clone(),
25            provider_env_key_present: self.provider_env_key_present,
26            refresh_token_url_override_present: self.refresh_token_url_override_present,
27        }
28    }
29}
30
31pub fn collect_auth_env_telemetry(
32    provider: &ModelProviderInfo,
33    codex_api_key_env_enabled: bool,
34) -> AuthEnvTelemetry {
35    AuthEnvTelemetry {
36        openai_api_key_env_present: env_var_present(OPENAI_API_KEY_ENV_VAR),
37        codex_api_key_env_present: env_var_present(CODEX_API_KEY_ENV_VAR),
38        codex_api_key_env_enabled,
39        provider_env_key_name: provider.env_key.as_ref().map(|_| "configured".to_string()),
40        provider_env_key_present: provider.env_key.as_deref().map(env_var_present),
41        refresh_token_url_override_present: env_var_present(REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR),
42    }
43}
44
45fn env_var_present(name: &str) -> bool {
46    match std::env::var(name) {
47        Ok(value) => !value.trim().is_empty(),
48        Err(std::env::VarError::NotUnicode(_)) => true,
49        Err(std::env::VarError::NotPresent) => false,
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use codex_model_provider_info::WireApi;
57    use pretty_assertions::assert_eq;
58
59    #[test]
60    fn collect_auth_env_telemetry_buckets_provider_env_key_name() {
61        let provider = ModelProviderInfo {
62            name: "Custom".to_string(),
63            base_url: None,
64            env_key: Some("sk-should-not-leak".to_string()),
65            env_key_instructions: None,
66            experimental_bearer_token: None,
67            auth: None,
68            aws: None,
69            wire_api: WireApi::Responses,
70            query_params: None,
71            http_headers: None,
72            env_http_headers: None,
73            request_max_retries: None,
74            stream_max_retries: None,
75            stream_idle_timeout_ms: None,
76            websocket_connect_timeout_ms: None,
77            requires_openai_auth: false,
78            supports_websockets: false,
79        };
80
81        let telemetry =
82            collect_auth_env_telemetry(&provider, /*codex_api_key_env_enabled*/ false);
83
84        assert_eq!(
85            telemetry.provider_env_key_name,
86            Some("configured".to_string())
87        );
88    }
89}