From cb78f2333e30a5ca2ec0d0f013ecca894cb9b137 Mon Sep 17 00:00:00 2001 From: pakrym-oai Date: Tue, 12 Aug 2025 09:40:04 -0700 Subject: [PATCH] Set user-agent (#2230) Use the same well-defined value in all cases when sending user-agent header --- codex-rs/Cargo.lock | 14 ++++++++++ codex-rs/chatgpt/src/chatgpt_client.rs | 3 ++- codex-rs/core/Cargo.toml | 4 ++- codex-rs/core/src/client.rs | 2 ++ codex-rs/core/src/lib.rs | 1 + codex-rs/core/src/user_agent.rs | 36 ++++++++++++++++++++++++++ codex-rs/tui/src/updates.rs | 8 +----- 7 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 codex-rs/core/src/user_agent.rs diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 5ec773ad..a211bf16 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -689,9 +689,11 @@ dependencies = [ "mcp-types", "mime_guess", "openssl-sys", + "os_info", "predicates", "pretty_assertions", "rand 0.9.2", + "regex-lite", "reqwest", "seccompiler", "serde", @@ -3044,6 +3046,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "os_info" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" +dependencies = [ + "log", + "plist", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "overload" version = "0.1.1" diff --git a/codex-rs/chatgpt/src/chatgpt_client.rs b/codex-rs/chatgpt/src/chatgpt_client.rs index 907783bb..db756321 100644 --- a/codex-rs/chatgpt/src/chatgpt_client.rs +++ b/codex-rs/chatgpt/src/chatgpt_client.rs @@ -1,4 +1,5 @@ use codex_core::config::Config; +use codex_core::user_agent::get_codex_user_agent; use crate::chatgpt_token::get_chatgpt_token_data; use crate::chatgpt_token::init_chatgpt_token_from_auth; @@ -30,7 +31,7 @@ pub(crate) async fn chatgpt_get_request( .bearer_auth(&token.access_token) .header("chatgpt-account-id", account_id?) .header("Content-Type", "application/json") - .header("User-Agent", "codex-cli") + .header("User-Agent", get_codex_user_agent(None)) .send() .await .context("Failed to send request")?; diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index ee527f3b..1ea1422b 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -27,11 +27,12 @@ futures = "0.3" libc = "0.2.174" mcp-types = { path = "../mcp-types" } mime_guess = "2.0" +os_info = "3.12.0" rand = "0.9" reqwest = { version = "0.12", features = ["json", "stream"] } serde = { version = "1", features = ["derive"] } -serde_json = "1" serde_bytes = "0.11" +serde_json = "1" sha1 = "0.10.6" shlex = "1.3.0" similar = "2.7.0" @@ -75,6 +76,7 @@ core_test_support = { path = "tests/common" } maplit = "1.0.2" predicates = "3" pretty_assertions = "1.4.1" +regex-lite = "0.1.6" tempfile = "3" tokio-test = "0.4" walkdir = "2.5.0" diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 8ab5ad96..4e31df2f 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -38,6 +38,7 @@ use crate::model_provider_info::WireApi; use crate::models::ResponseItem; use crate::openai_tools::create_tools_json_for_responses_api; use crate::protocol::TokenUsage; +use crate::user_agent::get_codex_user_agent; use crate::util::backoff; use std::sync::Arc; @@ -208,6 +209,7 @@ impl ModelClient { .as_deref() .unwrap_or("codex_cli_rs"); req_builder = req_builder.header("originator", originator); + req_builder = req_builder.header("User-Agent", get_codex_user_agent(Some(originator))); let res = req_builder.send().await; if let Ok(resp) = &res { diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index b36689f0..f3247c38 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -47,6 +47,7 @@ pub mod seatbelt; pub mod shell; pub mod spawn; pub mod turn_diff_tracker; +pub mod user_agent; mod user_notification; pub mod util; pub use apply_patch::CODEX_APPLY_PATCH_ARG1; diff --git a/codex-rs/core/src/user_agent.rs b/codex-rs/core/src/user_agent.rs new file mode 100644 index 00000000..a0cf3870 --- /dev/null +++ b/codex-rs/core/src/user_agent.rs @@ -0,0 +1,36 @@ +const DEFAULT_ORIGINATOR: &str = "codex_cli_rs"; + +pub fn get_codex_user_agent(originator: Option<&str>) -> String { + let build_version = env!("CARGO_PKG_VERSION"); + let os_info = os_info::get(); + format!( + "{}/{build_version} ({} {}; {})", + originator.unwrap_or(DEFAULT_ORIGINATOR), + os_info.os_type(), + os_info.version(), + os_info.architecture().unwrap_or("unknown"), + ) +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + use super::*; + + #[test] + fn test_get_codex_user_agent() { + let user_agent = get_codex_user_agent(None); + assert!(user_agent.starts_with("codex_cli_rs/")); + } + + #[test] + #[cfg(target_os = "macos")] + fn test_macos() { + use regex_lite::Regex; + let user_agent = get_codex_user_agent(None); + let re = + Regex::new(r"^codex_cli_rs/\d+\.\d+\.\d+ \(Mac OS \d+\.\d+\.\d+; (x86_64|arm64)\)$") + .unwrap(); + assert!(re.is_match(&user_agent)); + } +} diff --git a/codex-rs/tui/src/updates.rs b/codex-rs/tui/src/updates.rs index c7f7afd2..ef1cf3e9 100644 --- a/codex-rs/tui/src/updates.rs +++ b/codex-rs/tui/src/updates.rs @@ -67,13 +67,7 @@ async fn check_for_update(version_file: &Path) -> anyhow::Result<()> { tag_name: latest_tag_name, } = reqwest::Client::new() .get(LATEST_RELEASE_URL) - .header( - "User-Agent", - format!( - "codex/{} (+https://github.com/openai/codex)", - env!("CARGO_PKG_VERSION") - ), - ) + .header("User-Agent", get_codex_user_agent(None)) .send() .await? .error_for_status()?