mirror of
https://github.com/openai/codex.git
synced 2026-11-09 16:41:37 +00:00
Revert "fix(app-server): set originator header from initialize JSON-RPC request" (#8986)
Reverts openai/codex#8873
This commit is contained in:
1 parent
46b0c4acbb
commit
5c380d5b1e
15 files changed
+27
-294
No files matched your search
@@ -52,10 +52,6 @@ Clients must send a single `initialize` request before invoking any other method
|
|||||||
|
|
||||||
Applications building on top of `codex app-server` should identify themselves via the `clientInfo` parameter.
|
Applications building on top of `codex app-server` should identify themselves via the `clientInfo` parameter.
|
||||||
|
|
||||||
**Important**: `clientInfo.name` is used to identify the client for the OpenAI Compliance Logs Platform. If
|
|
||||||
you are developing a new Codex integration that is intended for enterprise use, please contact us to get it
|
|
||||||
added to a known clients list. For more context: https://chatgpt.com/admin/api-reference#tag/Logs:-Codex
|
|
||||||
|
|
||||||
Example (from OpenAI's official VSCode extension):
|
Example (from OpenAI's official VSCode extension):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -64,7 +60,7 @@ Example (from OpenAI's official VSCode extension):
|
|||||||
"id": 0,
|
"id": 0,
|
||||||
"params": {
|
"params": {
|
||||||
"clientInfo": {
|
"clientInfo": {
|
||||||
"name": "codex_vscode",
|
"name": "codex-vscode",
|
||||||
"title": "Codex VS Code Extension",
|
"title": "Codex VS Code Extension",
|
||||||
"version": "0.1.0"
|
"version": "0.1.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,17 +92,13 @@ pub async fn run_main(
|
|||||||
|
|
||||||
let feedback = CodexFeedback::new();
|
let feedback = CodexFeedback::new();
|
||||||
|
|
||||||
let otel = codex_core::otel_init::build_provider(
|
let otel =
|
||||||
&config,
|
codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION")).map_err(|e| {
|
||||||
env!("CARGO_PKG_VERSION"),
|
std::io::Error::new(
|
||||||
Some("codex_app_server"),
|
ErrorKind::InvalidData,
|
||||||
)
|
format!("error loading otel config: {e}"),
|
||||||
.map_err(|e| {
|
)
|
||||||
std::io::Error::new(
|
})?;
|
||||||
ErrorKind::InvalidData,
|
|
||||||
format!("error loading otel config: {e}"),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Install a simple subscriber so `tracing` output is visible. Users can
|
// Install a simple subscriber so `tracing` output is visible. Users can
|
||||||
// control the log level with `RUST_LOG`.
|
// control the log level with `RUST_LOG`.
|
||||||
|
|||||||
@@ -21,10 +21,8 @@ use codex_core::AuthManager;
|
|||||||
use codex_core::ThreadManager;
|
use codex_core::ThreadManager;
|
||||||
use codex_core::config::Config;
|
use codex_core::config::Config;
|
||||||
use codex_core::config_loader::LoaderOverrides;
|
use codex_core::config_loader::LoaderOverrides;
|
||||||
use codex_core::default_client::SetOriginatorError;
|
|
||||||
use codex_core::default_client::USER_AGENT_SUFFIX;
|
use codex_core::default_client::USER_AGENT_SUFFIX;
|
||||||
use codex_core::default_client::get_codex_user_agent;
|
use codex_core::default_client::get_codex_user_agent;
|
||||||
use codex_core::default_client::set_default_originator;
|
|
||||||
use codex_feedback::CodexFeedback;
|
use codex_feedback::CodexFeedback;
|
||||||
use codex_protocol::protocol::SessionSource;
|
use codex_protocol::protocol::SessionSource;
|
||||||
use toml::Value as TomlValue;
|
use toml::Value as TomlValue;
|
||||||
@@ -123,27 +121,6 @@ impl MessageProcessor {
|
|||||||
title: _title,
|
title: _title,
|
||||||
version,
|
version,
|
||||||
} = params.client_info;
|
} = params.client_info;
|
||||||
if let Err(error) = set_default_originator(name.clone()) {
|
|
||||||
match error {
|
|
||||||
SetOriginatorError::InvalidHeaderValue => {
|
|
||||||
let error = JSONRPCErrorError {
|
|
||||||
code: INVALID_REQUEST_ERROR_CODE,
|
|
||||||
message: format!(
|
|
||||||
"Invalid clientInfo.name: '{name}'. Must be a valid HTTP header value."
|
|
||||||
),
|
|
||||||
data: None,
|
|
||||||
};
|
|
||||||
self.outgoing.send_error(request_id, error).await;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let user_agent_suffix = format!("{name}; {version}");
|
let user_agent_suffix = format!("{name}; {version}");
|
||||||
if let Ok(mut suffix) = USER_AGENT_SUFFIX.lock() {
|
if let Ok(mut suffix) = USER_AGENT_SUFFIX.lock() {
|
||||||
*suffix = Some(user_agent_suffix);
|
*suffix = Some(user_agent_suffix);
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ pub use core_test_support::format_with_current_shell_non_login;
|
|||||||
pub use core_test_support::test_path_buf_with_windows;
|
pub use core_test_support::test_path_buf_with_windows;
|
||||||
pub use core_test_support::test_tmp_path;
|
pub use core_test_support::test_tmp_path;
|
||||||
pub use core_test_support::test_tmp_path_buf;
|
pub use core_test_support::test_tmp_path_buf;
|
||||||
pub use mcp_process::DEFAULT_CLIENT_NAME;
|
|
||||||
pub use mcp_process::McpProcess;
|
pub use mcp_process::McpProcess;
|
||||||
pub use mock_model_server::create_mock_responses_server_repeating_assistant;
|
pub use mock_model_server::create_mock_responses_server_repeating_assistant;
|
||||||
pub use mock_model_server::create_mock_responses_server_sequence;
|
pub use mock_model_server::create_mock_responses_server_sequence;
|
||||||
|
|||||||
@@ -66,8 +66,6 @@ pub struct McpProcess {
|
|||||||
pending_messages: VecDeque<JSONRPCMessage>,
|
pending_messages: VecDeque<JSONRPCMessage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_CLIENT_NAME: &str = "codex-app-server-tests";
|
|
||||||
|
|
||||||
impl McpProcess {
|
impl McpProcess {
|
||||||
pub async fn new(codex_home: &Path) -> anyhow::Result<Self> {
|
pub async fn new(codex_home: &Path) -> anyhow::Result<Self> {
|
||||||
Self::new_with_env(codex_home, &[]).await
|
Self::new_with_env(codex_home, &[]).await
|
||||||
@@ -140,7 +138,7 @@ impl McpProcess {
|
|||||||
pub async fn initialize(&mut self) -> anyhow::Result<()> {
|
pub async fn initialize(&mut self) -> anyhow::Result<()> {
|
||||||
let params = Some(serde_json::to_value(InitializeParams {
|
let params = Some(serde_json::to_value(InitializeParams {
|
||||||
client_info: ClientInfo {
|
client_info: ClientInfo {
|
||||||
name: DEFAULT_CLIENT_NAME.to_string(),
|
name: "codex-app-server-tests".to_string(),
|
||||||
title: None,
|
title: None,
|
||||||
version: "0.1.0".to_string(),
|
version: "0.1.0".to_string(),
|
||||||
},
|
},
|
||||||
@@ -165,38 +163,6 @@ impl McpProcess {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends initialize with the provided client info and returns the response/error message.
|
|
||||||
pub async fn initialize_with_client_info(
|
|
||||||
&mut self,
|
|
||||||
client_info: ClientInfo,
|
|
||||||
) -> anyhow::Result<JSONRPCMessage> {
|
|
||||||
let params = Some(serde_json::to_value(InitializeParams { client_info })?);
|
|
||||||
let request_id = self.send_request("initialize", params).await?;
|
|
||||||
let request_id = RequestId::Integer(request_id);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let message = self.read_jsonrpc_message().await?;
|
|
||||||
match message {
|
|
||||||
JSONRPCMessage::Notification(notification) => {
|
|
||||||
self.enqueue_user_message(notification);
|
|
||||||
}
|
|
||||||
JSONRPCMessage::Response(response) => {
|
|
||||||
if response.id == request_id {
|
|
||||||
return Ok(JSONRPCMessage::Response(response));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JSONRPCMessage::Error(error) => {
|
|
||||||
if error.id == request_id {
|
|
||||||
return Ok(JSONRPCMessage::Error(error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JSONRPCMessage::Request(_) => {
|
|
||||||
anyhow::bail!("unexpected JSONRPCMessage::Request: {message:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a `newConversation` JSON-RPC request.
|
/// Send a `newConversation` JSON-RPC request.
|
||||||
pub async fn send_new_conversation_request(
|
pub async fn send_new_conversation_request(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use app_test_support::DEFAULT_CLIENT_NAME;
|
|
||||||
use app_test_support::McpProcess;
|
use app_test_support::McpProcess;
|
||||||
use app_test_support::to_response;
|
use app_test_support::to_response;
|
||||||
use codex_app_server_protocol::GetUserAgentResponse;
|
use codex_app_server_protocol::GetUserAgentResponse;
|
||||||
@@ -26,13 +25,13 @@ async fn get_user_agent_returns_current_codex_user_agent() -> Result<()> {
|
|||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let os_info = os_info::get();
|
let os_info = os_info::get();
|
||||||
let originator = DEFAULT_CLIENT_NAME;
|
let originator = codex_core::default_client::originator().value.as_str();
|
||||||
let os_type = os_info.os_type();
|
let os_type = os_info.os_type();
|
||||||
let os_version = os_info.version();
|
let os_version = os_info.version();
|
||||||
let architecture = os_info.architecture().unwrap_or("unknown");
|
let architecture = os_info.architecture().unwrap_or("unknown");
|
||||||
let terminal_ua = codex_core::terminal::user_agent();
|
let terminal_ua = codex_core::terminal::user_agent();
|
||||||
let user_agent = format!(
|
let user_agent = format!(
|
||||||
"{originator}/0.0.0 ({os_type} {os_version}; {architecture}) {terminal_ua} ({DEFAULT_CLIENT_NAME}; 0.1.0)"
|
"{originator}/0.0.0 ({os_type} {os_version}; {architecture}) {terminal_ua} (codex-app-server-tests; 0.1.0)"
|
||||||
);
|
);
|
||||||
|
|
||||||
let received: GetUserAgentResponse = to_response(response)?;
|
let received: GetUserAgentResponse = to_response(response)?;
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use app_test_support::McpProcess;
|
|
||||||
use app_test_support::to_response;
|
|
||||||
use codex_app_server_protocol::ClientInfo;
|
|
||||||
use codex_app_server_protocol::InitializeResponse;
|
|
||||||
use codex_app_server_protocol::JSONRPCMessage;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
use tempfile::TempDir;
|
|
||||||
use tokio::time::timeout;
|
|
||||||
|
|
||||||
const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
||||||
async fn initialize_uses_client_info_name_as_originator() -> Result<()> {
|
|
||||||
let codex_home = TempDir::new()?;
|
|
||||||
let mut mcp = McpProcess::new(codex_home.path()).await?;
|
|
||||||
|
|
||||||
let message = timeout(
|
|
||||||
DEFAULT_READ_TIMEOUT,
|
|
||||||
mcp.initialize_with_client_info(ClientInfo {
|
|
||||||
name: "codex_vscode".to_string(),
|
|
||||||
title: Some("Codex VS Code Extension".to_string()),
|
|
||||||
version: "0.1.0".to_string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
let JSONRPCMessage::Response(response) = message else {
|
|
||||||
anyhow::bail!("expected initialize response, got {message:?}");
|
|
||||||
};
|
|
||||||
let InitializeResponse { user_agent } = to_response::<InitializeResponse>(response)?;
|
|
||||||
|
|
||||||
assert!(user_agent.starts_with("codex_vscode/"));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
||||||
async fn initialize_respects_originator_override_env_var() -> Result<()> {
|
|
||||||
let codex_home = TempDir::new()?;
|
|
||||||
let mut mcp = McpProcess::new_with_env(
|
|
||||||
codex_home.path(),
|
|
||||||
&[(
|
|
||||||
"CODEX_INTERNAL_ORIGINATOR_OVERRIDE",
|
|
||||||
Some("codex_originator_via_env_var"),
|
|
||||||
)],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let message = timeout(
|
|
||||||
DEFAULT_READ_TIMEOUT,
|
|
||||||
mcp.initialize_with_client_info(ClientInfo {
|
|
||||||
name: "codex_vscode".to_string(),
|
|
||||||
title: Some("Codex VS Code Extension".to_string()),
|
|
||||||
version: "0.1.0".to_string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
let JSONRPCMessage::Response(response) = message else {
|
|
||||||
anyhow::bail!("expected initialize response, got {message:?}");
|
|
||||||
};
|
|
||||||
let InitializeResponse { user_agent } = to_response::<InitializeResponse>(response)?;
|
|
||||||
|
|
||||||
assert!(user_agent.starts_with("codex_originator_via_env_var/"));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
||||||
async fn initialize_rejects_invalid_client_name() -> Result<()> {
|
|
||||||
let codex_home = TempDir::new()?;
|
|
||||||
let mut mcp = McpProcess::new_with_env(
|
|
||||||
codex_home.path(),
|
|
||||||
&[("CODEX_INTERNAL_ORIGINATOR_OVERRIDE", None)],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let message = timeout(
|
|
||||||
DEFAULT_READ_TIMEOUT,
|
|
||||||
mcp.initialize_with_client_info(ClientInfo {
|
|
||||||
name: "bad\rname".to_string(),
|
|
||||||
title: Some("Bad Client".to_string()),
|
|
||||||
version: "0.1.0".to_string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
let JSONRPCMessage::Error(error) = message else {
|
|
||||||
anyhow::bail!("expected initialize error, got {message:?}");
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(error.error.code, -32600);
|
|
||||||
assert_eq!(
|
|
||||||
error.error.message,
|
|
||||||
"Invalid clientInfo.name: 'bad\rname'. Must be a valid HTTP header value."
|
|
||||||
);
|
|
||||||
assert_eq!(error.error.data, None);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
mod account;
|
mod account;
|
||||||
mod config_rpc;
|
mod config_rpc;
|
||||||
mod initialize;
|
|
||||||
mod model_list;
|
mod model_list;
|
||||||
mod output_schema;
|
mod output_schema;
|
||||||
mod rate_limits;
|
mod rate_limits;
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use app_test_support::create_mock_responses_server_sequence_unchecked;
|
|||||||
use app_test_support::create_shell_command_sse_response;
|
use app_test_support::create_shell_command_sse_response;
|
||||||
use app_test_support::format_with_current_shell_display;
|
use app_test_support::format_with_current_shell_display;
|
||||||
use app_test_support::to_response;
|
use app_test_support::to_response;
|
||||||
use codex_app_server_protocol::ClientInfo;
|
|
||||||
use codex_app_server_protocol::CommandExecutionApprovalDecision;
|
use codex_app_server_protocol::CommandExecutionApprovalDecision;
|
||||||
use codex_app_server_protocol::CommandExecutionRequestApprovalResponse;
|
use codex_app_server_protocol::CommandExecutionRequestApprovalResponse;
|
||||||
use codex_app_server_protocol::CommandExecutionStatus;
|
use codex_app_server_protocol::CommandExecutionStatus;
|
||||||
@@ -41,76 +40,6 @@ use tempfile::TempDir;
|
|||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
|
|
||||||
const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
|
const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
|
||||||
const TEST_ORIGINATOR: &str = "codex_vscode";
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn turn_start_sends_originator_header() -> Result<()> {
|
|
||||||
let responses = vec![create_final_assistant_message_sse_response("Done")?];
|
|
||||||
let server = create_mock_chat_completions_server_unchecked(responses).await;
|
|
||||||
|
|
||||||
let codex_home = TempDir::new()?;
|
|
||||||
create_config_toml(codex_home.path(), &server.uri(), "never")?;
|
|
||||||
|
|
||||||
let mut mcp = McpProcess::new(codex_home.path()).await?;
|
|
||||||
timeout(
|
|
||||||
DEFAULT_READ_TIMEOUT,
|
|
||||||
mcp.initialize_with_client_info(ClientInfo {
|
|
||||||
name: TEST_ORIGINATOR.to_string(),
|
|
||||||
title: Some("Codex VS Code Extension".to_string()),
|
|
||||||
version: "0.1.0".to_string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
let thread_req = mcp
|
|
||||||
.send_thread_start_request(ThreadStartParams {
|
|
||||||
model: Some("mock-model".to_string()),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
let thread_resp: JSONRPCResponse = timeout(
|
|
||||||
DEFAULT_READ_TIMEOUT,
|
|
||||||
mcp.read_stream_until_response_message(RequestId::Integer(thread_req)),
|
|
||||||
)
|
|
||||||
.await??;
|
|
||||||
let ThreadStartResponse { thread, .. } = to_response::<ThreadStartResponse>(thread_resp)?;
|
|
||||||
|
|
||||||
let turn_req = mcp
|
|
||||||
.send_turn_start_request(TurnStartParams {
|
|
||||||
thread_id: thread.id.clone(),
|
|
||||||
input: vec![V2UserInput::Text {
|
|
||||||
text: "Hello".to_string(),
|
|
||||||
}],
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
timeout(
|
|
||||||
DEFAULT_READ_TIMEOUT,
|
|
||||||
mcp.read_stream_until_response_message(RequestId::Integer(turn_req)),
|
|
||||||
)
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
timeout(
|
|
||||||
DEFAULT_READ_TIMEOUT,
|
|
||||||
mcp.read_stream_until_notification_message("turn/completed"),
|
|
||||||
)
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
let requests = server
|
|
||||||
.received_requests()
|
|
||||||
.await
|
|
||||||
.expect("failed to fetch received requests");
|
|
||||||
assert!(!requests.is_empty());
|
|
||||||
for request in requests {
|
|
||||||
let originator = request
|
|
||||||
.headers
|
|
||||||
.get("originator")
|
|
||||||
.expect("originator header missing");
|
|
||||||
assert_eq!(originator.to_str()?, TEST_ORIGINATOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn turn_start_emits_notifications_and_accepts_model_override() -> Result<()> {
|
async fn turn_start_emits_notifications_and_accepts_model_override() -> Result<()> {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ pub use codex_client::CodexRequestBuilder;
|
|||||||
use reqwest::header::HeaderValue;
|
use reqwest::header::HeaderValue;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::sync::RwLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
/// Set this to add a suffix to the User-Agent string.
|
/// Set this to add a suffix to the User-Agent string.
|
||||||
///
|
///
|
||||||
@@ -30,7 +30,7 @@ pub struct Originator {
|
|||||||
pub value: String,
|
pub value: String,
|
||||||
pub header_value: HeaderValue,
|
pub header_value: HeaderValue,
|
||||||
}
|
}
|
||||||
static ORIGINATOR: LazyLock<RwLock<Option<Originator>>> = LazyLock::new(|| RwLock::new(None));
|
static ORIGINATOR: OnceLock<Originator> = OnceLock::new();
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SetOriginatorError {
|
pub enum SetOriginatorError {
|
||||||
@@ -60,48 +60,22 @@ fn get_originator_value(provided: Option<String>) -> Originator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_default_originator(value: String) -> Result<(), SetOriginatorError> {
|
pub fn set_default_originator(value: String) -> Result<(), SetOriginatorError> {
|
||||||
if HeaderValue::from_str(&value).is_err() {
|
|
||||||
return Err(SetOriginatorError::InvalidHeaderValue);
|
|
||||||
}
|
|
||||||
let originator = get_originator_value(Some(value));
|
let originator = get_originator_value(Some(value));
|
||||||
let Ok(mut guard) = ORIGINATOR.write() else {
|
ORIGINATOR
|
||||||
return Err(SetOriginatorError::AlreadyInitialized);
|
.set(originator)
|
||||||
};
|
.map_err(|_| SetOriginatorError::AlreadyInitialized)
|
||||||
if guard.is_some() {
|
|
||||||
return Err(SetOriginatorError::AlreadyInitialized);
|
|
||||||
}
|
|
||||||
*guard = Some(originator);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn originator() -> Originator {
|
pub fn originator() -> &'static Originator {
|
||||||
if let Ok(guard) = ORIGINATOR.read()
|
ORIGINATOR.get_or_init(|| get_originator_value(None))
|
||||||
&& 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(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(None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_codex_user_agent() -> String {
|
pub fn get_codex_user_agent() -> String {
|
||||||
let build_version = env!("CARGO_PKG_VERSION");
|
let build_version = env!("CARGO_PKG_VERSION");
|
||||||
let os_info = os_info::get();
|
let os_info = os_info::get();
|
||||||
let originator = originator();
|
|
||||||
let prefix = format!(
|
let prefix = format!(
|
||||||
"{}/{build_version} ({} {}; {}) {}",
|
"{}/{build_version} ({} {}; {}) {}",
|
||||||
originator.value.as_str(),
|
originator().value.as_str(),
|
||||||
os_info.os_type(),
|
os_info.os_type(),
|
||||||
os_info.version(),
|
os_info.version(),
|
||||||
os_info.architecture().unwrap_or("unknown"),
|
os_info.architecture().unwrap_or("unknown"),
|
||||||
@@ -149,7 +123,7 @@ fn sanitize_user_agent(candidate: String, fallback: &str) -> String {
|
|||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
"Falling back to default Codex originator because base user agent string is invalid"
|
"Falling back to default Codex originator because base user agent string is invalid"
|
||||||
);
|
);
|
||||||
originator().value
|
originator().value.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +137,7 @@ pub fn build_reqwest_client() -> reqwest::Client {
|
|||||||
use reqwest::header::HeaderMap;
|
use reqwest::header::HeaderMap;
|
||||||
|
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
headers.insert("originator", originator().header_value);
|
headers.insert("originator", originator().header_value.clone());
|
||||||
let ua = get_codex_user_agent();
|
let ua = get_codex_user_agent();
|
||||||
|
|
||||||
let mut builder = reqwest::Client::builder()
|
let mut builder = reqwest::Client::builder()
|
||||||
@@ -189,7 +163,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_get_codex_user_agent() {
|
fn test_get_codex_user_agent() {
|
||||||
let user_agent = get_codex_user_agent();
|
let user_agent = get_codex_user_agent();
|
||||||
let originator = originator().value;
|
let originator = originator().value.as_str();
|
||||||
let prefix = format!("{originator}/");
|
let prefix = format!("{originator}/");
|
||||||
assert!(user_agent.starts_with(&prefix));
|
assert!(user_agent.starts_with(&prefix));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ use std::error::Error;
|
|||||||
pub fn build_provider(
|
pub fn build_provider(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
service_version: &str,
|
service_version: &str,
|
||||||
service_name_override: Option<&str>,
|
|
||||||
) -> Result<Option<OtelProvider>, Box<dyn Error>> {
|
) -> Result<Option<OtelProvider>, Box<dyn Error>> {
|
||||||
let to_otel_exporter = |kind: &Kind| match kind {
|
let to_otel_exporter = |kind: &Kind| match kind {
|
||||||
Kind::None => OtelExporter::None,
|
Kind::None => OtelExporter::None,
|
||||||
@@ -71,11 +70,8 @@ pub fn build_provider(
|
|||||||
OtelExporter::None
|
OtelExporter::None
|
||||||
};
|
};
|
||||||
|
|
||||||
let originator = originator();
|
|
||||||
let service_name = service_name_override.unwrap_or(originator.value.as_str());
|
|
||||||
|
|
||||||
OtelProvider::from(&OtelSettings {
|
OtelProvider::from(&OtelSettings {
|
||||||
service_name: service_name.to_string(),
|
service_name: originator().value.to_owned(),
|
||||||
service_version: service_version.to_string(),
|
service_version: service_version.to_string(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
environment: config.otel.environment.to_string(),
|
environment: config.otel.environment.to_string(),
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ impl RolloutRecorder {
|
|||||||
id: session_id,
|
id: session_id,
|
||||||
timestamp,
|
timestamp,
|
||||||
cwd: config.cwd.clone(),
|
cwd: config.cwd.clone(),
|
||||||
originator: originator().value,
|
originator: originator().value.clone(),
|
||||||
cli_version: env!("CARGO_PKG_VERSION").to_string(),
|
cli_version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
instructions,
|
instructions,
|
||||||
source,
|
source,
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let otel = codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), None);
|
let otel = codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"));
|
||||||
|
|
||||||
#[allow(clippy::print_stderr)]
|
#[allow(clippy::print_stderr)]
|
||||||
let otel = match otel {
|
let otel = match otel {
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ pub async fn run_main(
|
|||||||
ensure_oss_provider_ready(provider_id, &config).await?;
|
ensure_oss_provider_ready(provider_id, &config).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let otel = codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), None);
|
let otel = codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"));
|
||||||
|
|
||||||
#[allow(clippy::print_stderr)]
|
#[allow(clippy::print_stderr)]
|
||||||
let otel = match otel {
|
let otel = match otel {
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ pub async fn run_main(
|
|||||||
ensure_oss_provider_ready(provider_id, &config).await?;
|
ensure_oss_provider_ready(provider_id, &config).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let otel = codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), None);
|
let otel = codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"));
|
||||||
|
|
||||||
#[allow(clippy::print_stderr)]
|
#[allow(clippy::print_stderr)]
|
||||||
let otel = match otel {
|
let otel = match otel {
|
||||||
|
|||||||
Reference in new issue
Block a user