mirror of
https://github.com/openai/codex.git
synced 2026-05-22 12:04:19 +00:00
Compare commits
1 Commits
dev/efraze
...
owen/origi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
caaae96609 |
@@ -124,7 +124,7 @@ use codex_app_server_protocol::TurnSteerParams;
|
||||
use codex_app_server_protocol::TurnSteerResponse;
|
||||
use codex_app_server_protocol::UserInput;
|
||||
use codex_login::default_client::DEFAULT_ORIGINATOR;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_plugin::AppConnectorId;
|
||||
use codex_plugin::PluginCapabilitySummary;
|
||||
use codex_plugin::PluginId;
|
||||
@@ -965,7 +965,7 @@ fn app_mentioned_event_serializes_expected_shape() {
|
||||
"thread_id": "thread-1",
|
||||
"turn_id": "turn-1",
|
||||
"app_name": "Calendar",
|
||||
"product_client_id": originator().value,
|
||||
"product_client_id": Originator::process_default().value().to_string(),
|
||||
"invoke_type": "explicit",
|
||||
"model_slug": "gpt-5"
|
||||
}
|
||||
@@ -1003,7 +1003,7 @@ fn app_used_event_serializes_expected_shape() {
|
||||
"thread_id": "thread-2",
|
||||
"turn_id": "turn-2",
|
||||
"app_name": "Google Drive",
|
||||
"product_client_id": originator().value,
|
||||
"product_client_id": Originator::process_default().value().to_string(),
|
||||
"invoke_type": "implicit",
|
||||
"model_slug": "gpt-5"
|
||||
}
|
||||
@@ -2774,7 +2774,7 @@ fn plugin_used_event_serializes_expected_shape() {
|
||||
"has_skills": true,
|
||||
"mcp_server_count": 2,
|
||||
"connector_ids": ["calendar", "drive"],
|
||||
"product_client_id": originator().value,
|
||||
"product_client_id": Originator::process_default().value().to_string(),
|
||||
"thread_id": "thread-3",
|
||||
"turn_id": "turn-3",
|
||||
"model_slug": "gpt-5"
|
||||
@@ -2803,7 +2803,7 @@ fn plugin_management_event_serializes_expected_shape() {
|
||||
"has_skills": true,
|
||||
"mcp_server_count": 2,
|
||||
"connector_ids": ["calendar", "drive"],
|
||||
"product_client_id": originator().value
|
||||
"product_client_id": Originator::process_default().value().to_string()
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -3009,7 +3009,7 @@ async fn reducer_ingests_skill_invoked_fact() {
|
||||
"skill_id": expected_skill_id,
|
||||
"skill_name": "doc",
|
||||
"event_params": {
|
||||
"product_client_id": originator().value,
|
||||
"product_client_id": Originator::process_default().value().to_string(),
|
||||
"skill_scope": "user",
|
||||
"plugin_id": null,
|
||||
"repo_url": null,
|
||||
@@ -3170,7 +3170,7 @@ async fn reducer_ingests_plugin_state_changed_fact() {
|
||||
"has_skills": true,
|
||||
"mcp_server_count": 2,
|
||||
"connector_ids": ["calendar", "drive"],
|
||||
"product_client_id": originator().value
|
||||
"product_client_id": Originator::process_default().value().to_string()
|
||||
}
|
||||
}])
|
||||
);
|
||||
|
||||
@@ -31,6 +31,7 @@ use codex_app_server_protocol::ServerRequest;
|
||||
use codex_app_server_protocol::ServerResponse;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::create_client;
|
||||
use codex_plugin::PluginTelemetryMetadata;
|
||||
use codex_protocol::request_permissions::RequestPermissionsResponse;
|
||||
@@ -440,7 +441,8 @@ async fn send_track_events_request(auth: &CodexAuth, url: &str, events: Vec<Trac
|
||||
|
||||
let payload = TrackEventsRequest { events };
|
||||
|
||||
let response = create_client()
|
||||
let originator = Originator::process_default();
|
||||
let response = create_client(&originator)
|
||||
.post(url)
|
||||
.timeout(ANALYTICS_EVENTS_TIMEOUT)
|
||||
.headers(codex_model_provider::auth_provider_from_auth(auth).to_auth_headers())
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::facts::TurnSubmissionType;
|
||||
use crate::now_unix_millis;
|
||||
use codex_app_server_protocol::CodexErrorInfo;
|
||||
use codex_app_server_protocol::CommandExecutionSource;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_plugin::PluginTelemetryMetadata;
|
||||
use codex_protocol::approvals::NetworkApprovalProtocol;
|
||||
use codex_protocol::models::AdditionalPermissionProfile;
|
||||
@@ -890,7 +890,7 @@ pub(crate) fn codex_app_metadata(
|
||||
thread_id: Some(tracking.thread_id.clone()),
|
||||
turn_id: Some(tracking.turn_id.clone()),
|
||||
app_name: app.app_name,
|
||||
product_client_id: Some(originator().value),
|
||||
product_client_id: Some(Originator::process_default().value().to_string()),
|
||||
invoke_type: app.invocation_type,
|
||||
model_slug: Some(tracking.model_slug.clone()),
|
||||
}
|
||||
@@ -920,7 +920,7 @@ pub(crate) fn codex_plugin_metadata(plugin: PluginTelemetryMetadata) -> CodexPlu
|
||||
.map(|connector_id| connector_id.0)
|
||||
.collect()
|
||||
}),
|
||||
product_client_id: Some(originator().value),
|
||||
product_client_id: Some(Originator::process_default().value().to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ use codex_app_server_protocol::UserInput;
|
||||
use codex_app_server_protocol::WebSearchAction;
|
||||
use codex_git_utils::collect_git_info;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
@@ -680,7 +680,7 @@ impl AnalyticsReducer {
|
||||
turn_id: Some(tracking.turn_id.clone()),
|
||||
invoke_type: Some(invocation.invocation_type),
|
||||
model_slug: Some(tracking.model_slug.clone()),
|
||||
product_client_id: Some(originator().value),
|
||||
product_client_id: Some(Originator::process_default().value().to_string()),
|
||||
repo_url,
|
||||
skill_scope: Some(skill_scope.to_string()),
|
||||
plugin_id: invocation.plugin_id,
|
||||
|
||||
@@ -3,6 +3,7 @@ use super::protocol::EnrollRemoteServerResponse;
|
||||
use super::protocol::RemoteControlTarget;
|
||||
use axum::http::HeaderMap;
|
||||
use codex_api::SharedAuthProvider;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_state::RemoteControlEnrollmentRecord;
|
||||
use codex_state::StateRuntime;
|
||||
@@ -204,7 +205,8 @@ pub(super) async fn enroll_remote_control_server(
|
||||
app_server_version: env!("CARGO_PKG_VERSION"),
|
||||
installation_id: installation_id.to_string(),
|
||||
};
|
||||
let client = build_reqwest_client();
|
||||
let originator = Originator::process_default();
|
||||
let client = build_reqwest_client(&originator);
|
||||
let mut auth_headers = HeaderMap::new();
|
||||
auth.auth_provider.add_auth_headers(&mut auth_headers);
|
||||
let http_request = client
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use axum::http::HeaderValue;
|
||||
use codex_analytics::AppServerRpcTransport;
|
||||
use codex_login::default_client::SetOriginatorError;
|
||||
use codex_login::default_client::USER_AGENT_SUFFIX;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::get_codex_user_agent;
|
||||
use codex_login::default_client::set_default_client_residency_requirement;
|
||||
use codex_login::default_client::set_default_originator;
|
||||
|
||||
use super::*;
|
||||
use crate::message_processor::ConnectionSessionState;
|
||||
use crate::message_processor::InitializedConnectionSessionState;
|
||||
|
||||
const NON_ORIGINATING_CLIENT_NAMES: &[&str] = &["codex_app_server_daemon", "codex-backend"];
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct InitializeRequestProcessor {
|
||||
outgoing: Arc<OutgoingMessageSender>,
|
||||
@@ -83,23 +78,19 @@ impl InitializeRequestProcessor {
|
||||
title: _title,
|
||||
version,
|
||||
} = params.client_info;
|
||||
// Validate before committing; set_default_originator validates while
|
||||
// mutating process-global metadata.
|
||||
if HeaderValue::from_str(&name).is_err() {
|
||||
return Err(invalid_request(format!(
|
||||
"Invalid clientInfo.name: '{name}'. Must be a valid HTTP header value."
|
||||
)));
|
||||
}
|
||||
let originator = name.clone();
|
||||
let user_agent_suffix = format!("{name}; {version}");
|
||||
let mutates_global_identity = !NON_ORIGINATING_CLIENT_NAMES.contains(&name.as_str());
|
||||
let originator = Originator::from_app_server_client(name.clone(), version.clone())
|
||||
.map_err(|_| {
|
||||
invalid_request(format!(
|
||||
"Invalid clientInfo.name: '{name}'. Must be a valid HTTP header value."
|
||||
))
|
||||
})?;
|
||||
let codex_home = self.config.codex_home.clone();
|
||||
if session
|
||||
.initialize(InitializedConnectionSessionState {
|
||||
experimental_api_enabled,
|
||||
opted_out_notification_methods: opt_out_notification_methods.into_iter().collect(),
|
||||
app_server_client_name: name.clone(),
|
||||
client_version: version,
|
||||
client_version: version.clone(),
|
||||
request_attestation,
|
||||
})
|
||||
.is_err()
|
||||
@@ -107,37 +98,15 @@ impl InitializeRequestProcessor {
|
||||
return Err(invalid_request("Already initialized"));
|
||||
}
|
||||
|
||||
if mutates_global_identity {
|
||||
// Only real client initialization may mutate process-global client metadata.
|
||||
if let Err(error) = set_default_originator(originator.clone()) {
|
||||
match error {
|
||||
SetOriginatorError::InvalidHeaderValue => {
|
||||
tracing::warn!(
|
||||
client_info_name = %name,
|
||||
"validated clientInfo.name was rejected while setting originator"
|
||||
);
|
||||
}
|
||||
SetOriginatorError::AlreadyInitialized => {
|
||||
// No-op. This is expected to happen if the originator is already set via env var.
|
||||
// TODO(owen): Once we remove support for CODEX_INTERNAL_ORIGINATOR_OVERRIDE,
|
||||
// this will be an unexpected state and we can return a JSON-RPC error indicating
|
||||
// internal server error.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.analytics_events_client.track_initialize(
|
||||
connection_id.0,
|
||||
analytics_initialize_params,
|
||||
originator,
|
||||
name,
|
||||
self.rpc_transport,
|
||||
);
|
||||
set_default_client_residency_requirement(self.config.enforce_residency.value());
|
||||
if mutates_global_identity && let Ok(mut suffix) = USER_AGENT_SUFFIX.lock() {
|
||||
*suffix = Some(user_agent_suffix);
|
||||
}
|
||||
|
||||
let user_agent = get_codex_user_agent();
|
||||
let user_agent = get_codex_user_agent(&originator);
|
||||
let response = InitializeResponse {
|
||||
user_agent,
|
||||
codex_home,
|
||||
|
||||
@@ -63,7 +63,7 @@ async fn initialize_uses_client_info_name_as_originator() -> Result<()> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn initialize_probe_does_not_override_originator() -> Result<()> {
|
||||
async fn initialize_probe_uses_connection_scoped_originator() -> Result<()> {
|
||||
let responses = Vec::new();
|
||||
let server = create_mock_responses_server_sequence_unchecked(responses).await;
|
||||
let codex_home = TempDir::new()?;
|
||||
@@ -85,12 +85,12 @@ async fn initialize_probe_does_not_override_originator() -> Result<()> {
|
||||
};
|
||||
let InitializeResponse { user_agent, .. } = to_response::<InitializeResponse>(response)?;
|
||||
|
||||
assert!(user_agent.starts_with("codex_cli_rs/"));
|
||||
assert!(user_agent.starts_with("codex_app_server_daemon/"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn initialize_codex_backend_does_not_override_originator() -> Result<()> {
|
||||
async fn initialize_codex_backend_uses_connection_scoped_originator() -> Result<()> {
|
||||
let responses = Vec::new();
|
||||
let server = create_mock_responses_server_sequence_unchecked(responses).await;
|
||||
let codex_home = TempDir::new()?;
|
||||
@@ -112,12 +112,12 @@ async fn initialize_codex_backend_does_not_override_originator() -> Result<()> {
|
||||
};
|
||||
let InitializeResponse { user_agent, .. } = to_response::<InitializeResponse>(response)?;
|
||||
|
||||
assert!(user_agent.starts_with("codex_cli_rs/"));
|
||||
assert!(user_agent.starts_with("codex-backend/"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn initialize_respects_originator_override_env_var() -> Result<()> {
|
||||
async fn initialize_ignores_process_originator_override_env_var() -> Result<()> {
|
||||
let responses = Vec::new();
|
||||
let server = create_mock_responses_server_sequence_unchecked(responses).await;
|
||||
let codex_home = TempDir::new()?;
|
||||
@@ -152,7 +152,7 @@ async fn initialize_respects_originator_override_env_var() -> Result<()> {
|
||||
platform_os,
|
||||
} = to_response::<InitializeResponse>(response)?;
|
||||
|
||||
assert!(user_agent.starts_with("codex_originator_via_env_var/"));
|
||||
assert!(user_agent.starts_with("codex_vscode/"));
|
||||
assert_eq!(response_codex_home, expected_codex_home);
|
||||
assert_eq!(platform_family, std::env::consts::FAMILY);
|
||||
assert_eq!(platform_os, std::env::consts::OS);
|
||||
|
||||
@@ -9,6 +9,7 @@ use codex_api::SharedAuthProvider;
|
||||
use codex_client::build_reqwest_client_with_custom_ca;
|
||||
use codex_client::with_chatgpt_cloudflare_cookie_store;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::get_codex_user_agent;
|
||||
use codex_protocol::account::PlanType as AccountPlanType;
|
||||
use codex_protocol::protocol::CreditsSnapshot;
|
||||
@@ -170,8 +171,9 @@ impl Client {
|
||||
}
|
||||
|
||||
pub fn from_auth(base_url: impl Into<String>, auth: &CodexAuth) -> Result<Self> {
|
||||
let originator = Originator::process_default();
|
||||
Ok(Self::new(base_url)?
|
||||
.with_user_agent(get_codex_user_agent())
|
||||
.with_user_agent(get_codex_user_agent(&originator))
|
||||
.with_auth_provider(codex_model_provider::auth_provider_from_auth(auth)))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use codex_core::config::Config;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::create_client;
|
||||
|
||||
use anyhow::Context;
|
||||
@@ -39,7 +40,8 @@ pub(crate) async fn chatgpt_get_request_with_timeout<T: DeserializeOwned>(
|
||||
);
|
||||
|
||||
// Make direct HTTP request to ChatGPT backend API with the token
|
||||
let client = create_client();
|
||||
let originator = Originator::process_default();
|
||||
let client = create_client(&originator);
|
||||
let url = format!(
|
||||
"{}/{}",
|
||||
chatgpt_base_url.trim_end_matches('/'),
|
||||
|
||||
@@ -20,7 +20,7 @@ pub use codex_core::connectors::with_app_enabled_state;
|
||||
use codex_core_plugins::PluginsManager;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_plugin::AppConnectorId;
|
||||
|
||||
const DIRECTORY_CONNECTORS_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
@@ -85,10 +85,8 @@ pub async fn list_cached_all_connectors(config: &Config) -> Option<Vec<AppInfo>>
|
||||
.into_iter()
|
||||
.map(|connector_id| connector_id.0),
|
||||
);
|
||||
Some(filter_disallowed_connectors(
|
||||
connectors,
|
||||
originator().value.as_str(),
|
||||
))
|
||||
let originator = Originator::process_default();
|
||||
Some(filter_disallowed_connectors(connectors, originator.value()))
|
||||
}
|
||||
|
||||
pub async fn list_all_connectors_with_options(
|
||||
@@ -121,10 +119,8 @@ pub async fn list_all_connectors_with_options(
|
||||
.into_iter()
|
||||
.map(|connector_id| connector_id.0),
|
||||
);
|
||||
Ok(filter_disallowed_connectors(
|
||||
connectors,
|
||||
originator().value.as_str(),
|
||||
))
|
||||
let originator = Originator::process_default();
|
||||
Ok(filter_disallowed_connectors(connectors, originator.value()))
|
||||
}
|
||||
|
||||
fn connector_directory_cache_context(
|
||||
@@ -165,7 +161,8 @@ pub fn connectors_for_plugin_apps(
|
||||
.iter()
|
||||
.map(|connector_id| connector_id.0.clone()),
|
||||
);
|
||||
filter_disallowed_connectors(connectors, originator().value.as_str())
|
||||
let originator = Originator::process_default();
|
||||
filter_disallowed_connectors(connectors, originator.value())
|
||||
.into_iter()
|
||||
.filter(|connector| plugin_app_ids.contains(connector.id.as_str()))
|
||||
.collect()
|
||||
@@ -189,7 +186,8 @@ pub fn merge_connectors_with_accessible(
|
||||
accessible_connectors
|
||||
};
|
||||
let merged = merge_connectors(connectors, accessible_connectors);
|
||||
filter_disallowed_connectors(merged, originator().value.as_str())
|
||||
let originator = Originator::process_default();
|
||||
filter_disallowed_connectors(merged, originator.value())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -47,6 +47,7 @@ use codex_login::CODEX_ACCESS_TOKEN_ENV_VAR;
|
||||
use codex_login::CODEX_API_KEY_ENV_VAR;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::OPENAI_API_KEY_ENV_VAR;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_login::default_client::default_headers;
|
||||
use codex_login::load_auth_dot_json;
|
||||
@@ -2164,12 +2165,13 @@ async fn websocket_reachability_check(
|
||||
OPENAI_BETA_HEADER,
|
||||
HeaderValue::from_static(RESPONSES_WEBSOCKETS_V2_BETA_HEADER_VALUE),
|
||||
);
|
||||
let originator = Originator::process_default();
|
||||
let client = ResponsesWebsocketClient::new(api_provider, api_auth);
|
||||
match tokio::time::timeout(
|
||||
provider.websocket_connect_timeout(),
|
||||
client.probe_handshake(
|
||||
extra_headers,
|
||||
default_headers(),
|
||||
default_headers(&originator),
|
||||
WEBSOCKET_IMMEDIATE_CLOSE_GRACE,
|
||||
),
|
||||
)
|
||||
@@ -2657,7 +2659,8 @@ async fn mcp_http_probe_url_with_timeout(url: &str, timeout: Duration) -> Result
|
||||
}
|
||||
|
||||
async fn http_probe_url_with_timeout(url: &str, timeout: Duration) -> Result<String, String> {
|
||||
let response = build_reqwest_client()
|
||||
let originator = Originator::process_default();
|
||||
let response = build_reqwest_client(&originator)
|
||||
.head(url)
|
||||
.timeout(timeout)
|
||||
.send()
|
||||
@@ -2683,7 +2686,8 @@ async fn http_get_probe_url_with_timeout(url: &str, timeout: Duration) -> Result
|
||||
}
|
||||
|
||||
async fn http_get_probe_status_with_timeout(url: &str, timeout: Duration) -> Result<u16, String> {
|
||||
let response = build_reqwest_client()
|
||||
let originator = Originator::process_default();
|
||||
let response = build_reqwest_client(&originator)
|
||||
.get(url)
|
||||
.timeout(timeout)
|
||||
.send()
|
||||
|
||||
@@ -12,6 +12,7 @@ use chrono::Utc;
|
||||
use codex_cloud_tasks_client::TaskStatus;
|
||||
use codex_git_utils::current_branch_name;
|
||||
use codex_git_utils::default_branch_name;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::get_codex_user_agent;
|
||||
use owo_colors::OwoColorize;
|
||||
use owo_colors::Stream;
|
||||
@@ -28,7 +29,6 @@ use tracing::info;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use util::append_error_log;
|
||||
use util::format_relative_time;
|
||||
use util::set_user_agent_suffix;
|
||||
|
||||
struct ApplyJob {
|
||||
task_id: codex_cloud_tasks_client::TaskId,
|
||||
@@ -49,8 +49,6 @@ async fn init_backend(user_agent_suffix: &str) -> anyhow::Result<BackendContext>
|
||||
let base_url = std::env::var("CODEX_CLOUD_TASKS_BASE_URL")
|
||||
.unwrap_or_else(|_| "https://chatgpt.com/backend-api".to_string());
|
||||
|
||||
set_user_agent_suffix(user_agent_suffix);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if use_mock {
|
||||
return Ok(BackendContext {
|
||||
@@ -59,7 +57,9 @@ async fn init_backend(user_agent_suffix: &str) -> anyhow::Result<BackendContext>
|
||||
});
|
||||
}
|
||||
|
||||
let ua = get_codex_user_agent();
|
||||
let originator = Originator::for_process(user_agent_suffix.to_string())
|
||||
.expect("cloud task originator should be a valid header value");
|
||||
let ua = get_codex_user_agent(&originator);
|
||||
let mut http = codex_cloud_tasks_client::HttpClient::new(base_url.clone())?.with_user_agent(ua);
|
||||
let style = if base_url.contains("/backend-api") {
|
||||
"wham"
|
||||
@@ -799,7 +799,10 @@ pub async fn run_main(cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> an
|
||||
append_error_log(format!(
|
||||
"startup: wham_force_internal={} ua={}",
|
||||
force_internal,
|
||||
get_codex_user_agent()
|
||||
get_codex_user_agent(
|
||||
&Originator::for_process("codex_cloud_tasks_tui".to_string())
|
||||
.expect("codex_cloud_tasks_tui should be a valid originator header value")
|
||||
)
|
||||
));
|
||||
// Non-blocking initial load so the in-box spinner can animate
|
||||
app.status = "Loading tasks…".to_string();
|
||||
|
||||
@@ -5,12 +5,7 @@ use reqwest::header::HeaderMap;
|
||||
|
||||
use codex_core::config::Config;
|
||||
use codex_login::AuthManager;
|
||||
|
||||
pub fn set_user_agent_suffix(suffix: &str) {
|
||||
if let Ok(mut guard) = codex_login::default_client::USER_AGENT_SUFFIX.lock() {
|
||||
guard.replace(suffix.to_string());
|
||||
}
|
||||
}
|
||||
use codex_login::default_client::Originator;
|
||||
|
||||
pub fn append_error_log(message: impl AsRef<str>) {
|
||||
let ts = Utc::now().to_rfc3339();
|
||||
@@ -61,8 +56,9 @@ pub async fn build_chatgpt_headers() -> HeaderMap {
|
||||
use reqwest::header::HeaderValue;
|
||||
use reqwest::header::USER_AGENT;
|
||||
|
||||
set_user_agent_suffix("codex_cloud_tasks_tui");
|
||||
let ua = codex_login::default_client::get_codex_user_agent();
|
||||
let originator = Originator::for_process("codex_cloud_tasks_tui".to_string())
|
||||
.expect("codex_cloud_tasks_tui should be a valid originator header value");
|
||||
let ua = codex_login::default_client::get_codex_user_agent(&originator);
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
USER_AGENT,
|
||||
|
||||
@@ -50,7 +50,7 @@ pub use codex_extension_api::empty_extension_registry;
|
||||
pub use codex_features::Feature;
|
||||
pub use codex_features::Features;
|
||||
pub use codex_login::AuthManager;
|
||||
pub use codex_login::default_client::set_default_originator;
|
||||
pub use codex_login::default_client::Originator;
|
||||
pub use codex_model_provider_info::OPENAI_PROVIDER_ID;
|
||||
pub use codex_model_provider_info::built_in_model_providers;
|
||||
pub use codex_models_manager::manager::RefreshStrategy;
|
||||
|
||||
@@ -7,6 +7,7 @@ use codex_app_server_protocol::PluginInstallPolicy;
|
||||
use codex_app_server_protocol::PluginInterface;
|
||||
use codex_app_server_protocol::SkillInterface;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_plugin::PluginId;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
@@ -23,6 +24,11 @@ use url::Url;
|
||||
mod remote_installed_plugin_sync;
|
||||
mod share;
|
||||
|
||||
fn build_process_reqwest_client() -> reqwest::Client {
|
||||
let originator = Originator::process_default();
|
||||
build_reqwest_client(&originator)
|
||||
}
|
||||
|
||||
pub use remote_installed_plugin_sync::RemoteInstalledPluginBundleSyncError;
|
||||
pub use remote_installed_plugin_sync::RemoteInstalledPluginBundleSyncOutcome;
|
||||
pub use remote_installed_plugin_sync::RemotePluginCacheMutationGuard;
|
||||
@@ -782,7 +788,7 @@ pub async fn fetch_remote_plugin_skill_detail(
|
||||
}
|
||||
|
||||
let url = remote_plugin_skill_detail_url(config, plugin_id, skill_name)?;
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let request = authenticated_request(client.get(&url), auth)?;
|
||||
let response: RemotePluginSkillDetailResponse = send_and_decode(request, &url).await?;
|
||||
if response.plugin_id != plugin_id {
|
||||
@@ -883,7 +889,7 @@ pub async fn install_remote_plugin(
|
||||
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/ps/plugins/{plugin_id}/install");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let request = authenticated_request(client.post(&url), auth)?;
|
||||
let response: RemotePluginMutationResponse = send_and_decode(request, &url).await?;
|
||||
if response.id != plugin_id {
|
||||
@@ -919,7 +925,7 @@ pub async fn uninstall_remote_plugin(
|
||||
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/plugins/{plugin_id}/uninstall");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let request = authenticated_request(client.post(&url), auth)?;
|
||||
let response: RemotePluginMutationResponse = send_and_decode(request, &url).await?;
|
||||
if response.id != plugin_id {
|
||||
@@ -1252,7 +1258,7 @@ async fn get_remote_plugin_list_page(
|
||||
) -> Result<RemotePluginListResponse, RemotePluginCatalogError> {
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/ps/plugins/list");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let mut request = authenticated_request(client.get(&url), auth)?;
|
||||
request = request.query(&[("scope", scope.api_value())]);
|
||||
request = request.query(&[("limit", REMOTE_PLUGIN_LIST_PAGE_LIMIT)]);
|
||||
@@ -1269,7 +1275,7 @@ async fn get_remote_shared_workspace_plugins_page(
|
||||
) -> Result<RemotePluginListResponse, RemotePluginCatalogError> {
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/ps/plugins/workspace/shared");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let mut request = authenticated_request(client.get(&url), auth)?;
|
||||
request = request.query(&[("limit", REMOTE_PLUGIN_LIST_PAGE_LIMIT)]);
|
||||
if let Some(page_token) = page_token {
|
||||
@@ -1287,7 +1293,7 @@ async fn get_remote_plugin_installed_page(
|
||||
) -> Result<RemotePluginInstalledResponse, RemotePluginCatalogError> {
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/ps/plugins/installed");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let mut request = authenticated_request(client.get(&url), auth)?;
|
||||
request = request.query(&[("scope", scope.api_value())]);
|
||||
if include_download_urls {
|
||||
@@ -1307,7 +1313,7 @@ async fn fetch_plugin_detail(
|
||||
) -> Result<RemotePluginDirectoryItem, RemotePluginCatalogError> {
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/ps/plugins/{plugin_id}");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let mut request = authenticated_request(client.get(&url), auth)?;
|
||||
if include_download_urls {
|
||||
request = request.query(&[("includeDownloadUrls", true)]);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use super::*;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use flate2::Compression;
|
||||
use flate2::write::GzEncoder;
|
||||
@@ -282,7 +281,7 @@ pub async fn delete_remote_plugin_share(
|
||||
let auth = ensure_chatgpt_auth(auth)?;
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/public/plugins/workspace/{remote_plugin_id}");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let request = authenticated_request(client.delete(&url), auth)?;
|
||||
send_and_expect_status(request, &url, &[StatusCode::NO_CONTENT]).await?;
|
||||
if let Err(err) = local_paths::remove_plugin_share_local_path(codex_home, remote_plugin_id) {
|
||||
@@ -315,7 +314,7 @@ pub async fn update_remote_plugin_share_targets(
|
||||
.unwrap_or_default();
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/ps/plugins/{remote_plugin_id}/shares");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let request = authenticated_request(client.put(&url), auth)?.json(
|
||||
&RemotePluginShareUpdateTargetsRequest {
|
||||
discoverability,
|
||||
@@ -381,7 +380,7 @@ async fn get_created_workspace_plugins_page(
|
||||
) -> Result<RemotePluginListResponse, RemotePluginCatalogError> {
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/ps/plugins/workspace/created");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let mut request = authenticated_request(client.get(&url), auth)?;
|
||||
request = request.query(&[("limit", REMOTE_PLUGIN_LIST_PAGE_LIMIT)]);
|
||||
if let Some(page_token) = page_token {
|
||||
@@ -399,7 +398,7 @@ async fn create_workspace_plugin_upload(
|
||||
) -> Result<RemoteWorkspacePluginUploadUrlResponse, RemotePluginCatalogError> {
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/public/plugins/workspace/upload-url");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let request = authenticated_request(client.post(&url), auth)?.json(
|
||||
&RemoteWorkspacePluginUploadUrlRequest {
|
||||
filename,
|
||||
@@ -415,7 +414,7 @@ async fn put_workspace_plugin_upload(
|
||||
upload_url: &str,
|
||||
archive_bytes: Vec<u8>,
|
||||
) -> Result<(), RemotePluginCatalogError> {
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let request = client
|
||||
.put(upload_url)
|
||||
.timeout(REMOTE_PLUGIN_CATALOG_TIMEOUT)
|
||||
@@ -453,7 +452,7 @@ async fn finalize_workspace_plugin_upload(
|
||||
} else {
|
||||
format!("{base_url}/public/plugins/workspace")
|
||||
};
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let request = authenticated_request(client.post(&url), auth)?.json(&body);
|
||||
send_and_decode(request, &url).await
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::store::PluginInstallResult;
|
||||
use crate::store::PluginStore;
|
||||
use crate::store::PluginStoreError;
|
||||
use crate::store::validate_plugin_version_segment;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_plugin::PluginId;
|
||||
use codex_plugin::PluginIdError;
|
||||
@@ -29,6 +30,11 @@ const REMOTE_PLUGIN_INSTALL_STAGING_DIR: &str = "plugins/.remote-plugin-install-
|
||||
const TEST_ALLOW_LOOPBACK_HTTP_REMOTE_PLUGIN_BUNDLES_ENV: &str =
|
||||
"CODEX_TEST_ALLOW_HTTP_REMOTE_PLUGIN_BUNDLE_DOWNLOADS";
|
||||
|
||||
fn build_process_reqwest_client() -> reqwest::Client {
|
||||
let originator = Originator::process_default();
|
||||
build_reqwest_client(&originator)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValidatedRemotePluginBundle {
|
||||
pub plugin_id: PluginId,
|
||||
@@ -263,7 +269,7 @@ async fn download_remote_plugin_bundle_with_limit(
|
||||
bundle_download_url: &str,
|
||||
max_bytes: u64,
|
||||
) -> Result<Vec<u8>, RemotePluginBundleInstallError> {
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let response = client
|
||||
.get(bundle_download_url)
|
||||
.timeout(REMOTE_PLUGIN_BUNDLE_DOWNLOAD_TIMEOUT)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::remote::RemotePluginServiceConfig;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_protocol::protocol::Product;
|
||||
use serde::Deserialize;
|
||||
@@ -11,6 +12,11 @@ const REMOTE_PLUGIN_FETCH_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
const REMOTE_FEATURED_PLUGIN_FETCH_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
const REMOTE_PLUGIN_MUTATION_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
fn build_process_reqwest_client() -> reqwest::Client {
|
||||
let originator = Originator::process_default();
|
||||
build_reqwest_client(&originator)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
pub struct RemotePluginStatusSummary {
|
||||
pub name: String,
|
||||
@@ -129,7 +135,7 @@ pub async fn fetch_remote_plugin_status(
|
||||
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/plugins/list");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let request = client
|
||||
.get(&url)
|
||||
.timeout(REMOTE_PLUGIN_FETCH_TIMEOUT)
|
||||
@@ -161,7 +167,7 @@ pub async fn fetch_remote_featured_plugin_ids(
|
||||
) -> Result<Vec<String>, RemotePluginFetchError> {
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/plugins/featured");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let mut request = client
|
||||
.get(&url)
|
||||
.query(&[(
|
||||
@@ -236,7 +242,7 @@ async fn post_remote_plugin_mutation(
|
||||
) -> Result<RemotePluginMutationResponse, RemotePluginMutationError> {
|
||||
let auth = ensure_codex_backend_auth(auth)?;
|
||||
let url = remote_plugin_mutation_url(config, plugin_id, action)?;
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let request = client
|
||||
.post(url.clone())
|
||||
.timeout(REMOTE_PLUGIN_MUTATION_TIMEOUT)
|
||||
|
||||
@@ -13,6 +13,7 @@ use tempfile::TempDir;
|
||||
use tracing::warn;
|
||||
use zip::ZipArchive;
|
||||
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
|
||||
const GITHUB_API_BASE_URL: &str = "https://api.github.com";
|
||||
@@ -31,6 +32,11 @@ const CURATED_PLUGINS_BACKUP_ARCHIVE_TIMEOUT: Duration = Duration::from_secs(30)
|
||||
// Keep this comfortably above a normal sync attempt so we do not race another Codex process.
|
||||
const CURATED_PLUGINS_STALE_TEMP_DIR_MAX_AGE: Duration = Duration::from_secs(10 * 60);
|
||||
|
||||
fn build_process_reqwest_client() -> Client {
|
||||
let originator = Originator::process_default();
|
||||
build_reqwest_client(&originator)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GitHubRepositorySummary {
|
||||
default_branch: String,
|
||||
@@ -590,7 +596,7 @@ fn ensure_git_success(output: &Output, context: &str) -> Result<(), String> {
|
||||
async fn fetch_curated_repo_remote_sha(api_base_url: &str) -> Result<String, String> {
|
||||
let api_base_url = api_base_url.trim_end_matches('/');
|
||||
let repo_url = format!("{api_base_url}/repos/{OPENAI_PLUGINS_OWNER}/{OPENAI_PLUGINS_REPO}");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let repo_body = fetch_github_text(&client, &repo_url, "get curated plugins repository").await?;
|
||||
let repo_summary: GitHubRepositorySummary =
|
||||
serde_json::from_str(&repo_body).map_err(|err| {
|
||||
@@ -624,14 +630,14 @@ async fn fetch_curated_repo_zipball(
|
||||
let api_base_url = api_base_url.trim_end_matches('/');
|
||||
let repo_url = format!("{api_base_url}/repos/{OPENAI_PLUGINS_OWNER}/{OPENAI_PLUGINS_REPO}");
|
||||
let zipball_url = format!("{repo_url}/zipball/{remote_sha}");
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
fetch_github_bytes(&client, &zipball_url, "download curated plugins archive").await
|
||||
}
|
||||
|
||||
async fn fetch_curated_repo_backup_archive_zip(
|
||||
backup_archive_api_url: &str,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
let client = build_reqwest_client();
|
||||
let client = build_process_reqwest_client();
|
||||
let export_body = fetch_public_text(
|
||||
&client,
|
||||
backup_archive_api_url,
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
|
||||
const REMOTE_SKILLS_API_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
@@ -107,7 +108,8 @@ pub async fn list_remote_skills(
|
||||
query_params.push(("enabled", enabled));
|
||||
}
|
||||
|
||||
let client = build_reqwest_client();
|
||||
let originator = Originator::process_default();
|
||||
let client = build_reqwest_client(&originator);
|
||||
let request = client
|
||||
.get(&url)
|
||||
.timeout(REMOTE_SKILLS_API_TIMEOUT)
|
||||
@@ -146,7 +148,8 @@ pub async fn export_remote_skill(
|
||||
) -> Result<RemoteSkillDownloadResult> {
|
||||
let auth = ensure_codex_backend_auth(auth)?;
|
||||
|
||||
let client = build_reqwest_client();
|
||||
let originator = Originator::process_default();
|
||||
let client = build_reqwest_client(&originator);
|
||||
let base_url = chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/hazelnuts/{skill_id}/export");
|
||||
let request = client
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::compact::content_items_to_text;
|
||||
use crate::event_mapping::is_contextual_user_message_content;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_protocol::models::MessagePhase;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
@@ -128,7 +129,8 @@ pub(crate) async fn monitor_action(
|
||||
};
|
||||
let body =
|
||||
build_arc_monitor_request(sess, turn_context, action, protection_client_callsite).await;
|
||||
let client = build_reqwest_client();
|
||||
let originator = Originator::process_default();
|
||||
let client = build_reqwest_client(&originator);
|
||||
let mut request = client.post(&url).timeout(ARC_MONITOR_TIMEOUT).json(&body);
|
||||
if let Some(token) = env_token {
|
||||
request = request.bearer_auth(token);
|
||||
|
||||
@@ -66,7 +66,9 @@ use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::RefreshTokenError;
|
||||
use codex_login::UnauthorizedRecovery;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_login::default_client::default_headers;
|
||||
use codex_otel::SessionTelemetry;
|
||||
use codex_otel::current_span_w3c_trace_context;
|
||||
|
||||
@@ -151,6 +153,16 @@ const MEMORIES_SUMMARIZE_ENDPOINT: &str = "/memories/trace_summarize";
|
||||
pub(crate) const WEBSOCKET_CONNECT_TIMEOUT: Duration =
|
||||
Duration::from_millis(DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS);
|
||||
|
||||
fn process_reqwest_client() -> reqwest::Client {
|
||||
let originator = Originator::process_default();
|
||||
build_reqwest_client(&originator)
|
||||
}
|
||||
|
||||
fn process_default_headers() -> ApiHeaderMap {
|
||||
let originator = Originator::process_default();
|
||||
default_headers(&originator)
|
||||
}
|
||||
|
||||
pub(crate) struct CompactConversationRequestSettings {
|
||||
pub(crate) effort: Option<ReasoningEffortConfig>,
|
||||
pub(crate) summary: ReasoningSummaryConfig,
|
||||
@@ -442,7 +454,7 @@ impl ModelClient {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let client_setup = self.current_client_setup().await?;
|
||||
let transport = ReqwestTransport::new(build_reqwest_client());
|
||||
let transport = ReqwestTransport::new(process_reqwest_client());
|
||||
let request_telemetry = Self::build_request_telemetry(
|
||||
session_telemetry,
|
||||
AuthRequestTelemetryContext::new(
|
||||
@@ -530,7 +542,7 @@ impl ModelClient {
|
||||
sideband_headers.extend(sideband_websocket_auth_headers(
|
||||
client_setup.api_auth.as_ref(),
|
||||
));
|
||||
let transport = ReqwestTransport::new(build_reqwest_client());
|
||||
let transport = ReqwestTransport::new(process_reqwest_client());
|
||||
let response =
|
||||
ApiRealtimeCallClient::new(transport, client_setup.api_provider, client_setup.api_auth)
|
||||
.create_with_session_and_headers(sdp, session_config, extra_headers)
|
||||
@@ -561,7 +573,7 @@ impl ModelClient {
|
||||
}
|
||||
|
||||
let client_setup = self.current_client_setup().await?;
|
||||
let transport = ReqwestTransport::new(build_reqwest_client());
|
||||
let transport = ReqwestTransport::new(process_reqwest_client());
|
||||
let request_telemetry = Self::build_request_telemetry(
|
||||
session_telemetry,
|
||||
AuthRequestTelemetryContext::new(
|
||||
@@ -823,7 +835,7 @@ impl ModelClient {
|
||||
websocket_connect_timeout,
|
||||
ApiWebSocketResponsesClient::new(api_provider, api_auth).connect(
|
||||
headers,
|
||||
codex_login::default_client::default_headers(),
|
||||
process_default_headers(),
|
||||
turn_state,
|
||||
Some(websocket_telemetry),
|
||||
),
|
||||
@@ -1225,7 +1237,7 @@ impl ModelClientSession {
|
||||
let mut pending_retry = PendingUnauthorizedRetry::default();
|
||||
loop {
|
||||
let client_setup = self.client.current_client_setup().await?;
|
||||
let transport = ReqwestTransport::new(build_reqwest_client());
|
||||
let transport = ReqwestTransport::new(process_reqwest_client());
|
||||
let request_auth_context = AuthRequestTelemetryContext::new(
|
||||
client_setup.auth.as_ref().map(CodexAuth::auth_mode),
|
||||
client_setup.api_auth.as_ref(),
|
||||
|
||||
@@ -32,7 +32,7 @@ use codex_core_plugins::PluginsManager;
|
||||
use codex_features::Feature;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;
|
||||
use codex_mcp::McpConnectionManager;
|
||||
use codex_mcp::McpRuntimeEnvironment;
|
||||
@@ -124,7 +124,7 @@ pub(crate) async fn list_tool_suggest_discoverable_tools_with_auth(
|
||||
directory_connectors,
|
||||
accessible_connectors,
|
||||
&connector_ids,
|
||||
originator().value.as_str(),
|
||||
Originator::process_default().value(),
|
||||
)
|
||||
.into_iter()
|
||||
.map(DiscoverableTool::from);
|
||||
@@ -153,7 +153,7 @@ pub async fn list_cached_accessible_connectors_from_mcp_tools(
|
||||
read_cached_accessible_connectors(&cache_key).map(|connectors| {
|
||||
codex_connectors::filter::filter_disallowed_connectors(
|
||||
connectors,
|
||||
originator().value.as_str(),
|
||||
Originator::process_default().value(),
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -170,7 +170,7 @@ pub(crate) fn refresh_accessible_connectors_cache_from_mcp_tools(
|
||||
let cache_key = accessible_connectors_cache_key(config, auth);
|
||||
let accessible_connectors = codex_connectors::filter::filter_disallowed_connectors(
|
||||
accessible_connectors_from_mcp_tools(mcp_tools),
|
||||
originator().value.as_str(),
|
||||
Originator::process_default().value(),
|
||||
);
|
||||
write_cached_accessible_connectors(cache_key, &accessible_connectors);
|
||||
}
|
||||
@@ -232,7 +232,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_environment_manager(
|
||||
{
|
||||
let cached_connectors = codex_connectors::filter::filter_disallowed_connectors(
|
||||
cached_connectors,
|
||||
originator().value.as_str(),
|
||||
Originator::process_default().value(),
|
||||
);
|
||||
let cached_connectors = with_app_plugin_sources(cached_connectors, &tool_plugin_provenance);
|
||||
return Ok(AccessibleConnectorsStatus {
|
||||
@@ -341,7 +341,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_environment_manager(
|
||||
|
||||
let accessible_connectors = codex_connectors::filter::filter_disallowed_connectors(
|
||||
accessible_connectors_from_mcp_tools(&tools),
|
||||
originator().value.as_str(),
|
||||
Originator::process_default().value(),
|
||||
);
|
||||
if codex_apps_ready || !accessible_connectors.is_empty() {
|
||||
write_cached_accessible_connectors(cache_key, &accessible_connectors);
|
||||
|
||||
@@ -5,8 +5,8 @@ use codex_config::ConfigEditsBuilder;
|
||||
use codex_config::McpServerConfig;
|
||||
use codex_config::McpServerTransportConfig;
|
||||
use codex_config::load_global_mcp_servers;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::is_first_party_originator;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_protocol::request_user_input::RequestUserInputArgs;
|
||||
use codex_protocol::request_user_input::RequestUserInputQuestion;
|
||||
use codex_protocol::request_user_input::RequestUserInputQuestionOption;
|
||||
@@ -38,8 +38,8 @@ pub(crate) async fn maybe_prompt_and_install_mcp_dependencies(
|
||||
mentioned_skills: &[SkillMetadata],
|
||||
elicitation_reviewer: Option<ElicitationReviewerHandle>,
|
||||
) {
|
||||
let originator_value = originator().value;
|
||||
if !is_first_party_originator(originator_value.as_str()) {
|
||||
let originator = Originator::process_default();
|
||||
if !is_first_party_originator(originator.value()) {
|
||||
// Only support first-party clients for now.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::config::Config;
|
||||
use codex_config::types::OtelExporterKind as Kind;
|
||||
use codex_config::types::OtelHttpProtocol as Protocol;
|
||||
use codex_features::Feature;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_otel::OtelExporter;
|
||||
use codex_otel::OtelHttpProtocol;
|
||||
use codex_otel::OtelProvider;
|
||||
@@ -76,8 +76,8 @@ pub fn build_provider(
|
||||
OtelExporter::None
|
||||
};
|
||||
|
||||
let originator = originator();
|
||||
let service_name = service_name_override.unwrap_or(originator.value.as_str());
|
||||
let originator = Originator::process_default();
|
||||
let service_name = service_name_override.unwrap_or(originator.value());
|
||||
let runtime_metrics = config.features.enabled(Feature::RuntimeMetrics);
|
||||
|
||||
OtelProvider::from(&OtelSettings {
|
||||
|
||||
@@ -24,6 +24,7 @@ use codex_app_server_protocol::AuthMode;
|
||||
use codex_config::config_toml::RealtimeWsMode;
|
||||
use codex_config::config_toml::RealtimeWsVersion;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::default_headers;
|
||||
use codex_login::read_openai_api_key_from_env;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
@@ -74,6 +75,11 @@ const REALTIME_V2_STEER_ACKNOWLEDGEMENT: &str =
|
||||
const REALTIME_ACTIVE_RESPONSE_ERROR_PREFIX: &str =
|
||||
"Conversation already has an active response in progress:";
|
||||
|
||||
fn process_default_headers() -> HeaderMap {
|
||||
let originator = Originator::process_default();
|
||||
default_headers(&originator)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum RealtimeConversationEnd {
|
||||
Requested,
|
||||
@@ -333,7 +339,7 @@ impl RealtimeConversationManager {
|
||||
.connect(
|
||||
session_config,
|
||||
extra_headers.unwrap_or_default(),
|
||||
default_headers(),
|
||||
process_default_headers(),
|
||||
)
|
||||
.await
|
||||
.map_err(map_api_error)?;
|
||||
@@ -1054,7 +1060,7 @@ fn spawn_webrtc_sideband_input_task(input: RealtimeWebrtcSidebandInputTask) -> J
|
||||
session_config,
|
||||
&call_id,
|
||||
sideband_headers,
|
||||
default_headers(),
|
||||
process_default_headers(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
||||
@@ -59,7 +59,6 @@ use codex_hooks::HooksConfig;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::auth_env_telemetry::collect_auth_env_telemetry;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_mcp::McpConnectionManager;
|
||||
use codex_mcp::McpRuntimeEnvironment;
|
||||
use codex_mcp::codex_apps_tools_cache_key;
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::input_queue::InputQueue;
|
||||
use super::*;
|
||||
use crate::goals::GoalRuntimeState;
|
||||
use crate::state::ActiveTurn;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_protocol::SessionId;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
@@ -670,7 +671,7 @@ impl Session {
|
||||
let auth_mode = auth.map(CodexAuth::auth_mode).map(TelemetryAuthMode::from);
|
||||
let account_id = auth.and_then(CodexAuth::get_account_id);
|
||||
let account_email = auth.and_then(CodexAuth::get_account_email);
|
||||
let originator = originator().value;
|
||||
let originator = Originator::process_default().value().to_string();
|
||||
let terminal_type = user_agent();
|
||||
let session_model = session_configuration.collaboration_mode.model().to_string();
|
||||
let auth_env_telemetry = collect_auth_env_telemetry(
|
||||
|
||||
@@ -6,7 +6,7 @@ use codex_config::types::WindowsSandboxModeToml;
|
||||
use codex_features::Feature;
|
||||
use codex_features::Features;
|
||||
use codex_features::FeaturesToml;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_otel::sanitize_metric_tag_value;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
@@ -283,7 +283,8 @@ pub struct WindowsSandboxSetupRequest {
|
||||
pub async fn run_windows_sandbox_setup(request: WindowsSandboxSetupRequest) -> anyhow::Result<()> {
|
||||
let start = Instant::now();
|
||||
let mode = request.mode;
|
||||
let originator_tag = sanitize_metric_tag_value(originator().value.as_str());
|
||||
let originator = Originator::process_default();
|
||||
let originator_tag = sanitize_metric_tag_value(originator.value());
|
||||
let result = run_windows_sandbox_setup_and_persist(request).await;
|
||||
|
||||
match result {
|
||||
|
||||
@@ -11,7 +11,7 @@ use codex_extension_api::empty_extension_registry;
|
||||
use codex_features::Feature;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_model_provider_info::WireApi;
|
||||
use codex_model_provider_info::built_in_model_providers;
|
||||
@@ -781,7 +781,7 @@ async fn includes_session_id_thread_id_and_model_headers_in_request() {
|
||||
|
||||
assert_eq!(request_session_id, expected_session_id.to_string());
|
||||
assert_eq!(request_thread_id, thread_id_string.as_str());
|
||||
assert_eq!(request_originator, originator().value);
|
||||
assert_eq!(request_originator, Originator::process_default().value());
|
||||
assert_eq!(request_authorization, "Bearer Test API Key");
|
||||
assert_eq!(
|
||||
request_body["prompt_cache_key"].as_str(),
|
||||
@@ -1052,7 +1052,7 @@ async fn chatgpt_auth_sends_correct_request() {
|
||||
assert_eq!(request_session_id, expected_session_id.to_string());
|
||||
assert_eq!(request_thread_id, expected_thread_id.to_string());
|
||||
|
||||
assert_eq!(request_originator, originator().value);
|
||||
assert_eq!(request_originator, Originator::process_default().value());
|
||||
assert_eq!(request_authorization, "Bearer Access Token");
|
||||
assert_eq!(request_chatgpt_account_id, "account_id");
|
||||
assert_eq!(
|
||||
|
||||
@@ -8,6 +8,7 @@ use codex_core::ResponseEvent;
|
||||
use codex_core::X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER;
|
||||
use codex_features::Feature;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_model_provider_info::WireApi;
|
||||
use codex_otel::MetricsClient;
|
||||
@@ -61,6 +62,11 @@ const TEST_INSTALLATION_ID: &str = "11111111-1111-4111-8111-111111111111";
|
||||
const X_CODEX_WS_STREAM_REQUEST_START_MS_CLIENT_METADATA_KEY: &str =
|
||||
"x-codex-ws-stream-request-start-ms";
|
||||
|
||||
fn default_user_agent() -> String {
|
||||
let originator = Originator::process_default();
|
||||
codex_login::default_client::get_codex_user_agent(&originator)
|
||||
}
|
||||
|
||||
fn assert_request_trace_matches(body: &serde_json::Value, expected_trace: &W3cTraceContext) {
|
||||
let client_metadata = body["client_metadata"]
|
||||
.as_object()
|
||||
@@ -141,7 +147,7 @@ async fn responses_websocket_streams_request() {
|
||||
);
|
||||
assert_eq!(
|
||||
handshake.header(USER_AGENT_HEADER),
|
||||
Some(codex_login::default_client::get_codex_user_agent())
|
||||
Some(default_user_agent())
|
||||
);
|
||||
assert_eq!(
|
||||
body["client_metadata"]["x-codex-installation-id"].as_str(),
|
||||
@@ -385,7 +391,7 @@ async fn responses_websocket_reuses_connection_with_per_turn_trace_payloads() {
|
||||
assert_eq!(server.handshakes().len(), 1);
|
||||
assert_eq!(
|
||||
server.single_handshake().header(USER_AGENT_HEADER),
|
||||
Some(codex_login::default_client::get_codex_user_agent())
|
||||
Some(default_user_agent())
|
||||
);
|
||||
let connection = server.single_connection();
|
||||
assert_eq!(connection.len(), 2);
|
||||
@@ -474,7 +480,7 @@ async fn responses_websocket_preconnect_reuses_connection() {
|
||||
assert_eq!(server.handshakes().len(), 1);
|
||||
assert_eq!(
|
||||
server.single_handshake().header(USER_AGENT_HEADER),
|
||||
Some(codex_login::default_client::get_codex_user_agent())
|
||||
Some(default_user_agent())
|
||||
);
|
||||
let connection = server.single_connection();
|
||||
assert_eq!(connection.len(), 1);
|
||||
@@ -512,7 +518,7 @@ async fn responses_websocket_request_prewarm_reuses_connection() {
|
||||
assert_eq!(server.handshakes().len(), 1);
|
||||
assert_eq!(
|
||||
server.single_handshake().header(USER_AGENT_HEADER),
|
||||
Some(codex_login::default_client::get_codex_user_agent())
|
||||
Some(default_user_agent())
|
||||
);
|
||||
let connection = server.single_connection();
|
||||
assert_eq!(connection.len(), 2);
|
||||
@@ -1359,10 +1365,7 @@ async fn responses_websocket_connection_limit_error_reconnects_and_completes() {
|
||||
.collect();
|
||||
assert_eq!(
|
||||
handshake_user_agents,
|
||||
vec![
|
||||
Some(codex_login::default_client::get_codex_user_agent()),
|
||||
Some(codex_login::default_client::get_codex_user_agent()),
|
||||
]
|
||||
vec![Some(default_user_agent()), Some(default_user_agent()),]
|
||||
);
|
||||
|
||||
server.shutdown().await;
|
||||
|
||||
@@ -429,7 +429,11 @@ async fn explicit_plugin_mentions_track_plugin_used_analytics() -> Result<()> {
|
||||
);
|
||||
assert_eq!(
|
||||
event["event_params"]["product_client_id"],
|
||||
serde_json::json!(codex_login::default_client::originator().value)
|
||||
serde_json::json!(
|
||||
codex_login::default_client::Originator::process_default()
|
||||
.value()
|
||||
.to_string()
|
||||
)
|
||||
);
|
||||
assert_eq!(event["event_params"]["model_slug"], "gpt-5.2");
|
||||
assert!(event["event_params"]["thread_id"].as_str().is_some());
|
||||
|
||||
@@ -72,7 +72,6 @@ use codex_feedback::CodexFeedback;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_login::AuthConfig;
|
||||
use codex_login::default_client::set_default_client_residency_requirement;
|
||||
use codex_login::default_client::set_default_originator;
|
||||
use codex_login::enforce_login_restrictions;
|
||||
use codex_model_provider_info::LMSTUDIO_OSS_PROVIDER_ID;
|
||||
use codex_model_provider_info::OLLAMA_OSS_PROVIDER_ID;
|
||||
@@ -236,10 +235,6 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
eprintln!("{message}");
|
||||
}
|
||||
|
||||
if let Err(err) = set_default_originator("codex_exec".to_string()) {
|
||||
tracing::warn!(?err, "Failed to set codex exec originator override {err:?}");
|
||||
}
|
||||
|
||||
let Cli {
|
||||
command,
|
||||
strict_config,
|
||||
|
||||
@@ -3,6 +3,7 @@ use codex_agent_identity::register_agent_task;
|
||||
use codex_protocol::account::PlanType as AccountPlanType;
|
||||
use std::env;
|
||||
|
||||
use crate::default_client::Originator;
|
||||
use crate::default_client::build_reqwest_client;
|
||||
|
||||
use super::storage::AgentIdentityAuthRecord;
|
||||
@@ -19,8 +20,9 @@ pub struct AgentIdentityAuth {
|
||||
impl AgentIdentityAuth {
|
||||
pub async fn load(record: AgentIdentityAuthRecord) -> std::io::Result<Self> {
|
||||
let agent_identity_authapi_base_url = agent_identity_authapi_base_url();
|
||||
let originator = Originator::process_default();
|
||||
let process_task_id = register_agent_task(
|
||||
&build_reqwest_client(),
|
||||
&build_reqwest_client(&originator),
|
||||
&agent_identity_authapi_base_url,
|
||||
key(&record),
|
||||
)
|
||||
|
||||
@@ -14,79 +14,111 @@ use reqwest::header::HeaderMap;
|
||||
use reqwest::header::HeaderValue;
|
||||
use reqwest::header::USER_AGENT;
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::RwLock;
|
||||
|
||||
/// Set this to add a suffix to the User-Agent string.
|
||||
///
|
||||
/// It is not ideal that we're using a global singleton for this.
|
||||
/// This is primarily designed to differentiate MCP clients from each other.
|
||||
/// Because there can only be one MCP server per process, it should be safe for this to be a global static.
|
||||
/// However, future users of this should use this with caution as a result.
|
||||
/// In addition, we want to be confident that this value is used for ALL clients and doing that requires a
|
||||
/// lot of wiring and it's easy to miss code paths by doing so.
|
||||
/// See https://github.com/openai/codex/pull/3388/files for an example of what that would look like.
|
||||
/// Finally, we want to make sure this is set for ALL mcp clients without needing to know a special env var
|
||||
/// or having to set data that they already specified in the mcp initialize request somewhere else.
|
||||
///
|
||||
/// A space is automatically added between the suffix and the rest of the User-Agent string.
|
||||
/// The full user agent string is returned from the mcp initialize response.
|
||||
/// Parenthesis will be added by Codex. This should only specify what goes inside of the parenthesis.
|
||||
pub static USER_AGENT_SUFFIX: LazyLock<Mutex<Option<String>>> = LazyLock::new(|| Mutex::new(None));
|
||||
pub const DEFAULT_ORIGINATOR: &str = "codex_cli_rs";
|
||||
pub const CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR: &str = "CODEX_INTERNAL_ORIGINATOR_OVERRIDE";
|
||||
pub const RESIDENCY_HEADER_NAME: &str = "x-openai-internal-codex-residency";
|
||||
|
||||
pub use codex_config::ResidencyRequirement;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Originator {
|
||||
pub value: String,
|
||||
pub header_value: HeaderValue,
|
||||
}
|
||||
static ORIGINATOR: LazyLock<RwLock<Option<Originator>>> = LazyLock::new(|| RwLock::new(None));
|
||||
static REQUIREMENTS_RESIDENCY: LazyLock<RwLock<Option<ResidencyRequirement>>> =
|
||||
LazyLock::new(|| RwLock::new(None));
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SetOriginatorError {
|
||||
InvalidHeaderValue,
|
||||
AlreadyInitialized,
|
||||
kind: OriginatorKind,
|
||||
}
|
||||
|
||||
fn get_originator_value(provided: Option<String>) -> Originator {
|
||||
let value = std::env::var(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR)
|
||||
.ok()
|
||||
.or(provided)
|
||||
.unwrap_or(DEFAULT_ORIGINATOR.to_string());
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum OriginatorKind {
|
||||
Process { value: String },
|
||||
AppServerClient { client: AppServerClient },
|
||||
}
|
||||
|
||||
match HeaderValue::from_str(&value) {
|
||||
Ok(header_value) => Originator {
|
||||
value,
|
||||
header_value,
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!("Unable to turn originator override {value} into header value: {e}");
|
||||
Originator {
|
||||
value: DEFAULT_ORIGINATOR.to_string(),
|
||||
header_value: HeaderValue::from_static(DEFAULT_ORIGINATOR),
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AppServerClient {
|
||||
name: String,
|
||||
version: String,
|
||||
}
|
||||
|
||||
impl Originator {
|
||||
pub fn process_default() -> Self {
|
||||
let value = std::env::var(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR)
|
||||
.unwrap_or_else(|_| DEFAULT_ORIGINATOR.to_string());
|
||||
|
||||
match Self::for_process(value.clone()) {
|
||||
Ok(originator) => originator,
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
"Unable to turn originator override {value} into header value: {e}"
|
||||
);
|
||||
Self::for_process(DEFAULT_ORIGINATOR.to_string())
|
||||
.expect("default originator should be a valid HTTP header value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_process(value: String) -> Result<Self, InvalidOriginator> {
|
||||
validate_originator_value(&value)?;
|
||||
Ok(Self {
|
||||
kind: OriginatorKind::Process { value },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_app_server_client(
|
||||
name: String,
|
||||
version: String,
|
||||
) -> Result<Self, InvalidOriginator> {
|
||||
validate_originator_value(&name)?;
|
||||
Ok(Self {
|
||||
kind: OriginatorKind::AppServerClient {
|
||||
client: AppServerClient { name, version },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &str {
|
||||
match &self.kind {
|
||||
OriginatorKind::Process { value } => value,
|
||||
OriginatorKind::AppServerClient { client } => client.name(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn app_server_client(&self) -> Option<&AppServerClient> {
|
||||
match &self.kind {
|
||||
OriginatorKind::Process { .. } => None,
|
||||
OriginatorKind::AppServerClient { client } => Some(client),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_default_originator(value: String) -> Result<(), SetOriginatorError> {
|
||||
if HeaderValue::from_str(&value).is_err() {
|
||||
return Err(SetOriginatorError::InvalidHeaderValue);
|
||||
impl AppServerClient {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
let originator = get_originator_value(Some(value));
|
||||
let Ok(mut guard) = ORIGINATOR.write() else {
|
||||
return Err(SetOriginatorError::AlreadyInitialized);
|
||||
};
|
||||
if guard.is_some() {
|
||||
return Err(SetOriginatorError::AlreadyInitialized);
|
||||
|
||||
pub fn version(&self) -> &str {
|
||||
&self.version
|
||||
}
|
||||
*guard = Some(originator);
|
||||
}
|
||||
|
||||
static REQUIREMENTS_RESIDENCY: LazyLock<RwLock<Option<ResidencyRequirement>>> =
|
||||
LazyLock::new(|| RwLock::new(None));
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum InvalidOriginator {
|
||||
InvalidHeaderValue,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InvalidOriginator {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::InvalidHeaderValue => f.write_str("invalid HTTP header value"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for InvalidOriginator {}
|
||||
|
||||
fn validate_originator_value(value: &str) -> Result<(), InvalidOriginator> {
|
||||
HeaderValue::from_str(value).map_err(|_| InvalidOriginator::InvalidHeaderValue)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -98,27 +130,6 @@ pub fn set_default_client_residency_requirement(enforce_residency: Option<Reside
|
||||
*guard = enforce_residency;
|
||||
}
|
||||
|
||||
pub fn originator() -> Originator {
|
||||
if let Ok(guard) = ORIGINATOR.read()
|
||||
&& let Some(originator) = guard.as_ref()
|
||||
{
|
||||
return originator.clone();
|
||||
}
|
||||
|
||||
if std::env::var(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR).is_ok() {
|
||||
let originator = get_originator_value(/*provided*/ None);
|
||||
if let Ok(mut guard) = ORIGINATOR.write() {
|
||||
match guard.as_ref() {
|
||||
Some(originator) => return originator.clone(),
|
||||
None => *guard = Some(originator.clone()),
|
||||
}
|
||||
}
|
||||
return originator;
|
||||
}
|
||||
|
||||
get_originator_value(/*provided*/ None)
|
||||
}
|
||||
|
||||
pub fn is_first_party_originator(originator_value: &str) -> bool {
|
||||
originator_value == DEFAULT_ORIGINATOR
|
||||
|| originator_value == "codex-tui"
|
||||
@@ -130,23 +141,18 @@ pub fn is_first_party_chat_originator(originator_value: &str) -> bool {
|
||||
originator_value == "codex_atlas" || originator_value == "codex_chatgpt_desktop"
|
||||
}
|
||||
|
||||
pub fn get_codex_user_agent() -> String {
|
||||
pub fn get_codex_user_agent(originator: &Originator) -> String {
|
||||
let build_version = env!("CARGO_PKG_VERSION");
|
||||
let os_info = os_info::get();
|
||||
let originator = originator();
|
||||
let prefix = format!(
|
||||
"{}/{build_version} ({} {}; {}) {}",
|
||||
originator.value.as_str(),
|
||||
originator.value(),
|
||||
os_info.os_type(),
|
||||
os_info.version(),
|
||||
os_info.architecture().unwrap_or("unknown"),
|
||||
user_agent()
|
||||
);
|
||||
let suffix = USER_AGENT_SUFFIX
|
||||
.lock()
|
||||
.ok()
|
||||
.and_then(|guard| guard.clone());
|
||||
let suffix = suffix
|
||||
let suffix = user_agent_suffix(originator)
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
@@ -184,13 +190,19 @@ 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
|
||||
DEFAULT_ORIGINATOR.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn user_agent_suffix(originator: &Originator) -> Option<String> {
|
||||
originator
|
||||
.app_server_client()
|
||||
.map(|client| format!("{}; {}", client.name(), client.version()))
|
||||
}
|
||||
|
||||
/// Create an HTTP client with default `originator` and `User-Agent` headers set.
|
||||
pub fn create_client() -> CodexHttpClient {
|
||||
let inner = build_reqwest_client();
|
||||
pub fn create_client(originator: &Originator) -> CodexHttpClient {
|
||||
let inner = build_reqwest_client(originator);
|
||||
CodexHttpClient::new(inner)
|
||||
}
|
||||
|
||||
@@ -200,8 +212,12 @@ pub fn create_client() -> CodexHttpClient {
|
||||
/// policy, then layers in shared custom CA handling from `CODEX_CA_CERTIFICATE` /
|
||||
/// `SSL_CERT_FILE`. The function remains infallible for compatibility with existing call sites, so
|
||||
/// a custom-CA or builder failure is logged and falls back to `reqwest::Client::new()`.
|
||||
pub fn build_reqwest_client() -> reqwest::Client {
|
||||
try_build_reqwest_client().unwrap_or_else(|error| {
|
||||
pub fn build_reqwest_client(originator: &Originator) -> reqwest::Client {
|
||||
build_reqwest_client_with_headers(default_headers(originator))
|
||||
}
|
||||
|
||||
fn build_reqwest_client_with_headers(headers: HeaderMap) -> reqwest::Client {
|
||||
try_build_reqwest_client_with_headers(headers).unwrap_or_else(|error| {
|
||||
tracing::warn!(error = %error, "failed to build default reqwest client");
|
||||
with_chatgpt_cloudflare_cookie_store(reqwest::Client::builder())
|
||||
.build()
|
||||
@@ -219,8 +235,16 @@ pub fn build_reqwest_client() -> reqwest::Client {
|
||||
///
|
||||
/// Callers that need a structured CA-loading failure instead of the legacy logged fallback can use
|
||||
/// this method directly.
|
||||
pub fn try_build_reqwest_client() -> Result<reqwest::Client, BuildCustomCaTransportError> {
|
||||
let mut builder = reqwest::Client::builder().default_headers(default_headers());
|
||||
pub fn try_build_reqwest_client(
|
||||
originator: &Originator,
|
||||
) -> Result<reqwest::Client, BuildCustomCaTransportError> {
|
||||
try_build_reqwest_client_with_headers(default_headers(originator))
|
||||
}
|
||||
|
||||
fn try_build_reqwest_client_with_headers(
|
||||
headers: HeaderMap,
|
||||
) -> Result<reqwest::Client, BuildCustomCaTransportError> {
|
||||
let mut builder = reqwest::Client::builder().default_headers(headers);
|
||||
if is_sandboxed() {
|
||||
builder = builder.no_proxy();
|
||||
}
|
||||
@@ -229,10 +253,13 @@ pub fn try_build_reqwest_client() -> Result<reqwest::Client, BuildCustomCaTransp
|
||||
build_reqwest_client_with_custom_ca(builder)
|
||||
}
|
||||
|
||||
pub fn default_headers() -> HeaderMap {
|
||||
pub fn default_headers(originator: &Originator) -> HeaderMap {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("originator", originator().header_value);
|
||||
if let Ok(user_agent) = HeaderValue::from_str(&get_codex_user_agent()) {
|
||||
let originator_header = HeaderValue::from_str(originator.value())
|
||||
.expect("originator should have been validated as a header value");
|
||||
headers.insert("originator", originator_header);
|
||||
let user_agent = get_codex_user_agent(originator);
|
||||
if let Ok(user_agent) = HeaderValue::from_str(&user_agent) {
|
||||
headers.insert(USER_AGENT, user_agent);
|
||||
}
|
||||
if let Ok(guard) = REQUIREMENTS_RESIDENCY.read()
|
||||
|
||||
@@ -2,12 +2,13 @@ use super::sanitize_user_agent;
|
||||
use super::*;
|
||||
use core_test_support::skip_if_no_network;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serial_test::serial;
|
||||
|
||||
#[test]
|
||||
fn test_get_codex_user_agent() {
|
||||
let user_agent = get_codex_user_agent();
|
||||
let originator = originator().value;
|
||||
let prefix = format!("{originator}/");
|
||||
let originator = Originator::process_default();
|
||||
let user_agent = get_codex_user_agent(&originator);
|
||||
let prefix = format!("{}/", originator.value());
|
||||
assert!(user_agent.starts_with(&prefix));
|
||||
}
|
||||
|
||||
@@ -44,7 +45,8 @@ async fn test_create_client_sets_default_headers() {
|
||||
use wiremock::matchers::method;
|
||||
use wiremock::matchers::path;
|
||||
|
||||
let client = create_client();
|
||||
let originator = Originator::process_default();
|
||||
let client = create_client(&originator);
|
||||
|
||||
// Spin up a local mock server and capture a request.
|
||||
let server = MockServer::start().await;
|
||||
@@ -72,10 +74,10 @@ async fn test_create_client_sets_default_headers() {
|
||||
let originator_header = headers
|
||||
.get("originator")
|
||||
.expect("originator header missing");
|
||||
assert_eq!(originator_header.to_str().unwrap(), originator().value);
|
||||
assert_eq!(originator_header.to_str().unwrap(), originator.value());
|
||||
|
||||
// User-Agent matches the computed Codex UA for that originator
|
||||
let expected_ua = get_codex_user_agent();
|
||||
let expected_ua = get_codex_user_agent(&originator);
|
||||
let ua_header = headers
|
||||
.get("user-agent")
|
||||
.expect("user-agent header missing");
|
||||
@@ -89,6 +91,94 @@ async fn test_create_client_sets_default_headers() {
|
||||
set_default_client_residency_requirement(/*enforce_residency*/ None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_server_originator_builds_explicit_headers() {
|
||||
let originator =
|
||||
Originator::from_app_server_client("codex_ios".to_string(), "1.2.3".to_string())
|
||||
.expect("originator should be valid");
|
||||
|
||||
let headers = default_headers(&originator);
|
||||
assert_eq!(
|
||||
headers
|
||||
.get("originator")
|
||||
.expect("originator header missing")
|
||||
.to_str()
|
||||
.expect("originator should be valid"),
|
||||
"codex_ios"
|
||||
);
|
||||
assert_eq!(
|
||||
originator.app_server_client().map(AppServerClient::name),
|
||||
Some("codex_ios")
|
||||
);
|
||||
assert_eq!(
|
||||
originator.app_server_client().map(AppServerClient::version),
|
||||
Some("1.2.3")
|
||||
);
|
||||
assert!(
|
||||
headers
|
||||
.get("user-agent")
|
||||
.expect("user-agent header missing")
|
||||
.to_str()
|
||||
.expect("user-agent should be valid")
|
||||
.starts_with("codex_ios/")
|
||||
);
|
||||
assert!(get_codex_user_agent(&originator).contains("(codex_ios; 1.2.3)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn process_originator_does_not_add_user_agent_suffix() {
|
||||
let originator =
|
||||
Originator::for_process("codex_exec".to_string()).expect("originator should be valid");
|
||||
|
||||
assert_eq!(originator.value(), "codex_exec");
|
||||
assert_eq!(originator.app_server_client(), None);
|
||||
assert!(!get_codex_user_agent(&originator).contains("(codex_exec"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(originator_env)]
|
||||
fn process_default_reads_originator_override() {
|
||||
let _guard = EnvVarGuard::set(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR, "codex_override");
|
||||
|
||||
let originator = Originator::process_default();
|
||||
|
||||
assert_eq!(originator.value(), "codex_override");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(originator_env)]
|
||||
fn app_server_originator_ignores_originator_override() {
|
||||
let _guard = EnvVarGuard::set(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR, "codex_override");
|
||||
|
||||
let originator =
|
||||
Originator::from_app_server_client("codex_ios".to_string(), "1.2.3".to_string())
|
||||
.expect("originator should be valid");
|
||||
|
||||
assert_eq!(originator.value(), "codex_ios");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(originator_env)]
|
||||
fn invalid_process_default_override_falls_back() {
|
||||
let _guard = EnvVarGuard::set(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR, "bad\rvalue");
|
||||
|
||||
let originator = Originator::process_default();
|
||||
|
||||
assert_eq!(originator.value(), DEFAULT_ORIGINATOR);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_originator_values_are_rejected() {
|
||||
assert_eq!(
|
||||
Originator::for_process("bad\rvalue".to_string()),
|
||||
Err(InvalidOriginator::InvalidHeaderValue)
|
||||
);
|
||||
assert_eq!(
|
||||
Originator::from_app_server_client("bad\rvalue".to_string(), "1.2.3".to_string()),
|
||||
Err(InvalidOriginator::InvalidHeaderValue)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_suffix_is_sanitized() {
|
||||
let prefix = "codex_cli_rs/0.0.0";
|
||||
@@ -115,11 +205,38 @@ fn test_invalid_suffix_is_sanitized2() {
|
||||
#[cfg(target_os = "macos")]
|
||||
fn test_macos() {
|
||||
use regex_lite::Regex;
|
||||
let user_agent = get_codex_user_agent();
|
||||
let originator = regex_lite::escape(originator().value.as_str());
|
||||
let originator = Originator::process_default();
|
||||
let user_agent = get_codex_user_agent(&originator);
|
||||
let originator = regex_lite::escape(originator.value());
|
||||
let re = Regex::new(&format!(
|
||||
r"^{originator}/\d+\.\d+\.\d+ \(Mac OS \d+\.\d+\.\d+; (x86_64|arm64)\) (\S+)$"
|
||||
))
|
||||
.unwrap();
|
||||
assert!(re.is_match(&user_agent));
|
||||
}
|
||||
|
||||
struct EnvVarGuard {
|
||||
key: &'static str,
|
||||
original: Option<std::ffi::OsString>,
|
||||
}
|
||||
|
||||
impl EnvVarGuard {
|
||||
fn set(key: &'static str, value: &str) -> Self {
|
||||
let original = std::env::var_os(key);
|
||||
unsafe {
|
||||
std::env::set_var(key, value);
|
||||
}
|
||||
Self { key, original }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EnvVarGuard {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
match &self.original {
|
||||
Some(value) => std::env::set_var(self.key, value),
|
||||
None => std::env::remove_var(self.key),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ pub use crate::auth::storage::AuthDotJson;
|
||||
use crate::auth::storage::AuthStorageBackend;
|
||||
use crate::auth::storage::create_auth_storage;
|
||||
use crate::auth::util::try_parse_error_message;
|
||||
use crate::default_client::Originator;
|
||||
use crate::default_client::build_reqwest_client;
|
||||
use crate::default_client::create_client;
|
||||
use crate::token_data::TokenData;
|
||||
@@ -203,7 +204,8 @@ impl CodexAuth {
|
||||
chatgpt_base_url: Option<&str>,
|
||||
) -> std::io::Result<Self> {
|
||||
let auth_mode = auth_dot_json.resolved_mode();
|
||||
let client = create_client();
|
||||
let originator = Originator::process_default();
|
||||
let client = create_client(&originator);
|
||||
if auth_mode == ApiAuthMode::ApiKey {
|
||||
let Some(api_key) = auth_dot_json.openai_api_key.as_deref() else {
|
||||
return Err(std::io::Error::other("API key auth is missing a key."));
|
||||
@@ -423,7 +425,8 @@ impl CodexAuth {
|
||||
agent_identity: None,
|
||||
};
|
||||
|
||||
let client = create_client();
|
||||
let originator = Originator::process_default();
|
||||
let client = create_client(&originator);
|
||||
let state = ChatgptAuthState {
|
||||
auth_dot_json: Arc::new(Mutex::new(Some(auth_dot_json))),
|
||||
client,
|
||||
@@ -493,7 +496,8 @@ async fn verified_agent_identity_record(
|
||||
chatgpt_base_url: &str,
|
||||
) -> std::io::Result<AgentIdentityAuthRecord> {
|
||||
AgentIdentityAuthRecord::from_agent_identity_jwt(jwt)?;
|
||||
let jwks = fetch_agent_identity_jwks(&build_reqwest_client(), chatgpt_base_url)
|
||||
let originator = Originator::process_default();
|
||||
let jwks = fetch_agent_identity_jwks(&build_reqwest_client(&originator), chatgpt_base_url)
|
||||
.await
|
||||
.map_err(std::io::Error::other)?;
|
||||
let claims = decode_agent_identity_jwt(jwt, Some(&jwks)).map_err(std::io::Error::other)?;
|
||||
|
||||
@@ -17,6 +17,7 @@ use super::manager::REVOKE_TOKEN_URL;
|
||||
use super::manager::REVOKE_TOKEN_URL_OVERRIDE_ENV_VAR;
|
||||
use super::storage::AuthDotJson;
|
||||
use super::util::try_parse_error_message;
|
||||
use crate::default_client::Originator;
|
||||
use crate::default_client::create_client;
|
||||
use crate::token_data::TokenData;
|
||||
|
||||
@@ -59,7 +60,8 @@ pub(crate) async fn revoke_auth_tokens(
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let client = create_client();
|
||||
let originator = Originator::process_default();
|
||||
let client = create_client(&originator);
|
||||
let endpoint = revoke_token_endpoint();
|
||||
revoke_oauth_token(&client, endpoint.as_str(), token, kind, REVOKE_HTTP_TIMEOUT).await
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ use crate::auth::load_auth_dot_json;
|
||||
use crate::auth::revoke_auth_tokens;
|
||||
use crate::auth::save_auth;
|
||||
use crate::auth::should_revoke_auth_tokens;
|
||||
use crate::default_client::originator;
|
||||
use crate::default_client::Originator;
|
||||
use crate::pkce::PkceCodes;
|
||||
use crate::pkce::generate_pkce;
|
||||
use crate::token_data::TokenData;
|
||||
@@ -505,7 +505,10 @@ fn build_authorize_url(
|
||||
("id_token_add_organizations".to_string(), "true".to_string()),
|
||||
("codex_cli_simplified_flow".to_string(), "true".to_string()),
|
||||
("state".to_string(), state.to_string()),
|
||||
("originator".to_string(), originator().value),
|
||||
(
|
||||
"originator".to_string(),
|
||||
Originator::process_default().value().to_string(),
|
||||
),
|
||||
];
|
||||
if let Some(workspace_ids) = forced_chatgpt_workspace_ids {
|
||||
query.push(("allowed_workspace_id".to_string(), workspace_ids.join(",")));
|
||||
|
||||
@@ -8,7 +8,7 @@ use codex_core::config::Config;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_extension_api::empty_extension_registry;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::default_client::USER_AGENT_SUFFIX;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::get_codex_user_agent;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
@@ -210,14 +210,6 @@ impl MessageProcessor {
|
||||
return;
|
||||
}
|
||||
|
||||
let client_info = params.client_info;
|
||||
let name = client_info.name;
|
||||
let version = client_info.version;
|
||||
let user_agent_suffix = format!("{name}; {version}");
|
||||
if let Ok(mut suffix) = USER_AGENT_SUFFIX.lock() {
|
||||
*suffix = Some(user_agent_suffix);
|
||||
}
|
||||
|
||||
let server_info = Implementation {
|
||||
name: "codex-mcp-server".to_string(),
|
||||
title: Some("Codex".to_string()),
|
||||
@@ -244,7 +236,11 @@ impl MessageProcessor {
|
||||
}
|
||||
};
|
||||
if let serde_json::Value::Object(ref mut obj) = server_info_value {
|
||||
obj.insert("user_agent".to_string(), json!(get_codex_user_agent()));
|
||||
let originator = Originator::process_default();
|
||||
obj.insert(
|
||||
"user_agent".to_string(),
|
||||
json!(get_codex_user_agent(&originator)),
|
||||
);
|
||||
}
|
||||
|
||||
let mut result_value = match serde_json::to_value(InitializeResult {
|
||||
|
||||
@@ -11,7 +11,6 @@ use tokio::process::ChildStdout;
|
||||
|
||||
use anyhow::Context;
|
||||
use codex_mcp_server::CodexToolCallParam;
|
||||
use codex_terminal_detection::user_agent;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
use rmcp::model::CallToolRequestParams;
|
||||
@@ -149,16 +148,8 @@ impl McpProcess {
|
||||
.await?;
|
||||
|
||||
let initialized = self.read_jsonrpc_message().await?;
|
||||
let os_info = os_info::get();
|
||||
let build_version = env!("CARGO_PKG_VERSION");
|
||||
let originator = codex_login::default_client::originator().value;
|
||||
let user_agent = format!(
|
||||
"{originator}/{build_version} ({} {}; {}) {} (elicitation test; 0.0.0)",
|
||||
os_info.os_type(),
|
||||
os_info.version(),
|
||||
os_info.architecture().unwrap_or("unknown"),
|
||||
user_agent()
|
||||
);
|
||||
let originator = codex_login::default_client::Originator::process_default();
|
||||
let user_agent = codex_login::default_client::get_codex_user_agent(&originator);
|
||||
let JsonRpcMessage::Response(JsonRpcResponse {
|
||||
jsonrpc,
|
||||
id,
|
||||
|
||||
@@ -12,7 +12,7 @@ use codex_features::Feature;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::auth_env_telemetry::collect_auth_env_telemetry;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_otel::SessionTelemetry;
|
||||
use codex_otel::TelemetryAuthMode;
|
||||
use codex_protocol::SessionId;
|
||||
@@ -86,6 +86,7 @@ impl MemoryStartupContext {
|
||||
let account_id = auth.and_then(CodexAuth::get_account_id);
|
||||
let account_email = auth.and_then(CodexAuth::get_account_email);
|
||||
let model = config.model.as_deref().unwrap_or("unknown");
|
||||
let originator = Originator::process_default();
|
||||
let auth_env_telemetry = collect_auth_env_telemetry(
|
||||
&config.model_provider,
|
||||
auth_manager.codex_api_key_env_enabled(),
|
||||
@@ -97,7 +98,7 @@ impl MemoryStartupContext {
|
||||
account_id,
|
||||
account_email,
|
||||
auth_mode,
|
||||
originator().value,
|
||||
originator.value().to_string(),
|
||||
config.otel.log_user_prompt,
|
||||
user_agent(),
|
||||
source,
|
||||
|
||||
@@ -14,6 +14,7 @@ use codex_login::AuthEnvTelemetry;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::collect_auth_env_telemetry;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_models_manager::manager::ModelsEndpointClient;
|
||||
@@ -88,7 +89,8 @@ impl ModelsEndpointClient for OpenAiModelsEndpoint {
|
||||
let auth_mode = auth.as_ref().map(CodexAuth::auth_mode);
|
||||
let api_provider = self.provider_info.to_api_provider(auth_mode)?;
|
||||
let api_auth = resolve_provider_auth(auth.as_ref(), &self.provider_info)?;
|
||||
let transport = ReqwestTransport::new(build_reqwest_client());
|
||||
let originator = Originator::process_default();
|
||||
let transport = ReqwestTransport::new(build_reqwest_client(&originator));
|
||||
let auth_telemetry = auth_header_telemetry(api_auth.as_ref());
|
||||
let request_telemetry: Arc<dyn RequestTelemetry> = Arc::new(ModelsRequestTelemetry {
|
||||
auth_mode: auth_mode.map(|mode| TelemetryAuthMode::from(mode).to_string()),
|
||||
|
||||
@@ -44,7 +44,7 @@ use super::list::parse_timestamp_uuid_from_filename;
|
||||
use super::metadata;
|
||||
use super::session_index::find_thread_names_by_ids;
|
||||
use crate::config::RolloutConfigView;
|
||||
use crate::default_client::originator;
|
||||
use crate::default_client::Originator;
|
||||
use crate::state_db;
|
||||
use crate::state_db::StateDbHandle;
|
||||
use codex_git_utils::collect_git_info;
|
||||
@@ -675,7 +675,7 @@ impl RolloutRecorder {
|
||||
forked_from_id,
|
||||
timestamp,
|
||||
cwd: config.cwd().to_path_buf(),
|
||||
originator: originator().value,
|
||||
originator: Originator::process_default().value().to_string(),
|
||||
cli_version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
agent_nickname: source.get_nickname(),
|
||||
agent_role: source.get_agent_role(),
|
||||
|
||||
@@ -59,7 +59,6 @@ use codex_core_api::find_codex_home;
|
||||
use codex_core_api::init_state_db;
|
||||
use codex_core_api::item_event_to_server_notification;
|
||||
use codex_core_api::resolve_installation_id;
|
||||
use codex_core_api::set_default_originator;
|
||||
use codex_core_api::thread_store_from_config;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -82,10 +81,6 @@ fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
async fn run_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
|
||||
if let Err(err) = set_default_originator("codex_thread_manager_sample".to_string()) {
|
||||
tracing::warn!("failed to set originator: {err:?}");
|
||||
}
|
||||
|
||||
let args = Args::parse();
|
||||
let prompt = if args.prompt.is_empty() {
|
||||
if std::io::stdin().is_terminal() {
|
||||
|
||||
@@ -744,7 +744,9 @@ impl App {
|
||||
/*account_id*/ None,
|
||||
bootstrap.account_email.clone(),
|
||||
auth_mode,
|
||||
codex_login::default_client::originator().value,
|
||||
codex_login::default_client::Originator::process_default()
|
||||
.value()
|
||||
.to_string(),
|
||||
config.otel.log_user_prompt,
|
||||
user_agent(),
|
||||
serde_json::from_value(serde_json::json!("cli"))
|
||||
|
||||
@@ -44,7 +44,7 @@ use codex_config::format_config_error_with_source;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_login::AuthConfig;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::set_default_client_residency_requirement;
|
||||
use codex_login::enforce_login_restrictions;
|
||||
use codex_protocol::ThreadId;
|
||||
@@ -977,7 +977,7 @@ pub async fn run_main(
|
||||
)
|
||||
.await;
|
||||
|
||||
let otel_originator = originator().value;
|
||||
let otel_originator = Originator::process_default().value().to_string();
|
||||
let otel = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
crate::legacy_core::otel_init::build_provider(
|
||||
&config,
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::update_versions::is_source_build_version;
|
||||
use chrono::DateTime;
|
||||
use chrono::Duration;
|
||||
use chrono::Utc;
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::create_client;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
@@ -85,9 +86,10 @@ fn read_version_info(version_file: &Path) -> anyhow::Result<VersionInfo> {
|
||||
}
|
||||
|
||||
async fn check_for_update(version_file: &Path, action: Option<UpdateAction>) -> anyhow::Result<()> {
|
||||
let originator = Originator::process_default();
|
||||
let latest_version = match action {
|
||||
Some(UpdateAction::BrewUpgrade) => {
|
||||
let HomebrewCaskInfo { version } = create_client()
|
||||
let HomebrewCaskInfo { version } = create_client(&originator)
|
||||
.get(HOMEBREW_CASK_API_URL)
|
||||
.send()
|
||||
.await?
|
||||
@@ -98,7 +100,7 @@ async fn check_for_update(version_file: &Path, action: Option<UpdateAction>) ->
|
||||
}
|
||||
Some(UpdateAction::NpmGlobalLatest) | Some(UpdateAction::BunGlobalLatest) => {
|
||||
let latest_version = fetch_latest_github_release_version().await?;
|
||||
let package_info = create_client()
|
||||
let package_info = create_client(&originator)
|
||||
.get(npm_registry::PACKAGE_URL)
|
||||
.send()
|
||||
.await?
|
||||
@@ -130,9 +132,10 @@ async fn check_for_update(version_file: &Path, action: Option<UpdateAction>) ->
|
||||
}
|
||||
|
||||
async fn fetch_latest_github_release_version() -> anyhow::Result<String> {
|
||||
let originator = Originator::process_default();
|
||||
let ReleaseInfo {
|
||||
tag_name: latest_tag_name,
|
||||
} = create_client()
|
||||
} = create_client(&originator)
|
||||
.get(LATEST_RELEASE_URL)
|
||||
.send()
|
||||
.await?
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use codex_login::default_client::Originator;
|
||||
use codex_login::default_client::is_first_party_chat_originator;
|
||||
use codex_login::default_client::originator;
|
||||
|
||||
const DISALLOWED_CONNECTOR_IDS: &[&str] = &[
|
||||
"asdk_app_6938a94a61d881918ef32cb999ff937c",
|
||||
@@ -13,7 +13,8 @@ const FIRST_PARTY_CHAT_DISALLOWED_CONNECTOR_IDS: &[&str] =
|
||||
&["connector_0f9c9d4592e54d0a9a12b3f44a1e2010"];
|
||||
|
||||
pub fn is_connector_id_allowed(connector_id: &str) -> bool {
|
||||
is_connector_id_allowed_for_originator(connector_id, originator().value.as_str())
|
||||
let originator = Originator::process_default();
|
||||
is_connector_id_allowed_for_originator(connector_id, originator.value())
|
||||
}
|
||||
|
||||
fn is_connector_id_allowed_for_originator(connector_id: &str, originator_value: &str) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user