From c09e131653652bc0357556284c67a644edcf7a79 Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Mon, 29 Sep 2025 20:59:19 -0700 Subject: [PATCH] Set originator for codex exec (#4485) Distinct from the main CLI. --- codex-rs/core/src/default_client.rs | 38 +++++++++++++++++++++++---- codex-rs/core/src/otel_init.rs | 4 +-- codex-rs/core/src/rollout/recorder.rs | 4 +-- codex-rs/exec/src/lib.rs | 5 ++++ codex-rs/login/src/server.rs | 4 +-- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/codex-rs/core/src/default_client.rs b/codex-rs/core/src/default_client.rs index 5ce08d9d..5a6ea8d8 100644 --- a/codex-rs/core/src/default_client.rs +++ b/codex-rs/core/src/default_client.rs @@ -2,6 +2,7 @@ use crate::spawn::CODEX_SANDBOX_ENV_VAR; use reqwest::header::HeaderValue; use std::sync::LazyLock; use std::sync::Mutex; +use std::sync::OnceLock; /// Set this to add a suffix to the User-Agent string. /// @@ -26,8 +27,15 @@ pub struct Originator { pub value: String, pub header_value: HeaderValue, } +static ORIGINATOR: OnceLock = OnceLock::new(); -pub static ORIGINATOR: LazyLock = LazyLock::new(|| { +#[derive(Debug)] +pub enum SetOriginatorError { + InvalidHeaderValue, + AlreadyInitialized, +} + +fn init_originator_from_env() -> Originator { let default = "codex_cli_rs"; let value = std::env::var(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR) .unwrap_or_else(|_| default.to_string()); @@ -45,14 +53,34 @@ pub static ORIGINATOR: LazyLock = LazyLock::new(|| { } } } -}); +} + +fn build_originator(value: String) -> Result { + let header_value = + HeaderValue::from_str(&value).map_err(|_| SetOriginatorError::InvalidHeaderValue)?; + Ok(Originator { + value, + header_value, + }) +} + +pub fn set_default_originator(value: &str) -> Result<(), SetOriginatorError> { + let originator = build_originator(value.to_string())?; + ORIGINATOR + .set(originator) + .map_err(|_| SetOriginatorError::AlreadyInitialized) +} + +pub fn originator() -> &'static Originator { + ORIGINATOR.get_or_init(init_originator_from_env) +} pub fn get_codex_user_agent() -> String { let build_version = env!("CARGO_PKG_VERSION"); let os_info = os_info::get(); let prefix = format!( "{}/{build_version} ({} {}; {}) {}", - ORIGINATOR.value.as_str(), + originator().value.as_str(), os_info.os_type(), os_info.version(), os_info.architecture().unwrap_or("unknown"), @@ -100,7 +128,7 @@ fn sanitize_user_agent(candidate: String, fallback: &str) -> String { tracing::warn!( "Falling back to default Codex originator because base user agent string is invalid" ); - ORIGINATOR.value.clone() + originator().value.clone() } } @@ -109,7 +137,7 @@ pub fn create_client() -> reqwest::Client { use reqwest::header::HeaderMap; let mut headers = HeaderMap::new(); - headers.insert("originator", ORIGINATOR.header_value.clone()); + headers.insert("originator", originator().header_value.clone()); let ua = get_codex_user_agent(); let mut builder = reqwest::Client::builder() diff --git a/codex-rs/core/src/otel_init.rs b/codex-rs/core/src/otel_init.rs index 0d6002c2..e7175edc 100644 --- a/codex-rs/core/src/otel_init.rs +++ b/codex-rs/core/src/otel_init.rs @@ -1,7 +1,7 @@ use crate::config::Config; use crate::config_types::OtelExporterKind as Kind; use crate::config_types::OtelHttpProtocol as Protocol; -use crate::default_client::ORIGINATOR; +use crate::default_client::originator; use codex_otel::config::OtelExporter; use codex_otel::config::OtelHttpProtocol; use codex_otel::config::OtelSettings; @@ -46,7 +46,7 @@ pub fn build_provider( }; OtelProvider::from(&OtelSettings { - service_name: ORIGINATOR.value.to_owned(), + service_name: originator().value.to_owned(), service_version: service_version.to_string(), codex_home: config.codex_home.clone(), environment: config.otel.environment.to_string(), diff --git a/codex-rs/core/src/rollout/recorder.rs b/codex-rs/core/src/rollout/recorder.rs index 23e64541..6d99dd13 100644 --- a/codex-rs/core/src/rollout/recorder.rs +++ b/codex-rs/core/src/rollout/recorder.rs @@ -24,7 +24,7 @@ use super::list::Cursor; use super::list::get_conversations; use super::policy::is_persisted_response_item; use crate::config::Config; -use crate::default_client::ORIGINATOR; +use crate::default_client::originator; use crate::git_info::collect_git_info; use codex_protocol::protocol::InitialHistory; use codex_protocol::protocol::ResumedHistory; @@ -124,7 +124,7 @@ impl RolloutRecorder { id: session_id, timestamp, cwd: config.cwd.clone(), - originator: ORIGINATOR.value.clone(), + originator: originator().value.clone(), cli_version: env!("CARGO_PKG_VERSION").to_string(), instructions, }), diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index de468493..bdf03650 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -38,9 +38,14 @@ use tracing_subscriber::prelude::*; use crate::cli::Command as ExecCommand; use crate::event_processor::CodexStatus; use crate::event_processor::EventProcessor; +use codex_core::default_client::set_default_originator; use codex_core::find_conversation_path_by_id_str; pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option) -> anyhow::Result<()> { + if let Err(err) = set_default_originator("codex_exec") { + tracing::warn!(?err, "Failed to set codex exec originator override {err:?}"); + } + let Cli { command, images, diff --git a/codex-rs/login/src/server.rs b/codex-rs/login/src/server.rs index 7df9038b..275e9890 100644 --- a/codex-rs/login/src/server.rs +++ b/codex-rs/login/src/server.rs @@ -16,7 +16,7 @@ use base64::Engine; use chrono::Utc; use codex_core::auth::AuthDotJson; use codex_core::auth::get_auth_file; -use codex_core::default_client::ORIGINATOR; +use codex_core::default_client::originator; use codex_core::token_data::TokenData; use codex_core::token_data::parse_id_token; use rand::RngCore; @@ -315,7 +315,7 @@ fn build_authorize_url( ("id_token_add_organizations", "true"), ("codex_cli_simplified_flow", "true"), ("state", state), - ("originator", ORIGINATOR.value.as_str()), + ("originator", originator().value.as_str()), ]; let qs = query .into_iter()