mirror of
https://github.com/openai/codex.git
synced 2026-11-14 21:40:39 +00:00
[codex] cover app-owned HTTP requests [ci changed_files]
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use codex_core::config::Config;
|
||||
use codex_http_state::HttpStateContext;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::default_client::create_client;
|
||||
|
||||
@@ -9,26 +10,34 @@ use std::time::Duration;
|
||||
const OAI_PRODUCT_SKU_HEADER: &str = "OAI-Product-Sku";
|
||||
const CODEX_PRODUCT_SKU: &str = "codex";
|
||||
|
||||
/// Make a GET request to the ChatGPT backend API.
|
||||
pub(crate) async fn chatgpt_get_request<T: DeserializeOwned>(
|
||||
pub(crate) async fn chatgpt_get_request_with_http_state<T: DeserializeOwned>(
|
||||
config: &Config,
|
||||
path: String,
|
||||
http_state: HttpStateContext,
|
||||
) -> anyhow::Result<T> {
|
||||
chatgpt_get_request_with_timeout(config, path, /*timeout*/ None).await
|
||||
chatgpt_get_request_with_timeout_and_http_state(
|
||||
config,
|
||||
path,
|
||||
/*timeout*/ None,
|
||||
Some(http_state),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn chatgpt_get_request_with_timeout<T: DeserializeOwned>(
|
||||
pub(crate) async fn chatgpt_get_request_with_timeout_and_http_state<T: DeserializeOwned>(
|
||||
config: &Config,
|
||||
path: String,
|
||||
timeout: Option<Duration>,
|
||||
http_state: Option<HttpStateContext>,
|
||||
) -> anyhow::Result<T> {
|
||||
let chatgpt_base_url = &config.chatgpt_base_url;
|
||||
let auth_manager =
|
||||
AuthManager::shared_from_config(config, /*enable_codex_api_key_env*/ false).await;
|
||||
let auth = auth_manager
|
||||
.auth()
|
||||
.await
|
||||
.ok_or_else(|| anyhow::anyhow!("ChatGPT auth not available"))?;
|
||||
let auth = match http_state.as_ref() {
|
||||
Some(http_state) => auth_manager.auth_for_surface(http_state.surface()).await,
|
||||
None => auth_manager.auth().await,
|
||||
}
|
||||
.ok_or_else(|| anyhow::anyhow!("ChatGPT auth not available"))?;
|
||||
anyhow::ensure!(
|
||||
auth.uses_codex_backend(),
|
||||
"ChatGPT backend requests require Codex backend auth"
|
||||
@@ -46,9 +55,16 @@ pub(crate) async fn chatgpt_get_request_with_timeout<T: DeserializeOwned>(
|
||||
path.trim_start_matches('/')
|
||||
);
|
||||
|
||||
let auth_provider = codex_model_provider::with_native_integrity_state(
|
||||
codex_model_provider::auth_provider_from_auth(&auth),
|
||||
Some(&auth),
|
||||
http_state,
|
||||
);
|
||||
let mut request_headers = Default::default();
|
||||
auth_provider.add_auth_headers_for_url(&url, &mut request_headers);
|
||||
let mut request = client
|
||||
.get(&url)
|
||||
.headers(codex_model_provider::auth_provider_from_auth(&auth).to_auth_headers())
|
||||
.headers(request_headers.clone())
|
||||
.header(OAI_PRODUCT_SKU_HEADER, CODEX_PRODUCT_SKU)
|
||||
.header("Content-Type", "application/json");
|
||||
|
||||
@@ -57,6 +73,7 @@ pub(crate) async fn chatgpt_get_request_with_timeout<T: DeserializeOwned>(
|
||||
}
|
||||
|
||||
let response = request.send().await.context("Failed to send request")?;
|
||||
auth_provider.observe_response_headers(&url, &request_headers, response.headers());
|
||||
|
||||
if response.status().is_success() {
|
||||
let result: T = response
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::chatgpt_client::chatgpt_get_request_with_timeout;
|
||||
use crate::chatgpt_client::chatgpt_get_request_with_timeout_and_http_state;
|
||||
|
||||
use codex_app_server_protocol::AppInfo;
|
||||
use codex_connectors::ConnectorDirectoryCacheContext;
|
||||
@@ -14,11 +14,14 @@ use codex_connectors::merge::merge_plugin_connectors;
|
||||
use codex_core::config::Config;
|
||||
pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools;
|
||||
pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager;
|
||||
pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager_and_http_state;
|
||||
pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools_with_options;
|
||||
pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools_with_options_and_status;
|
||||
pub use codex_core::connectors::list_cached_accessible_connectors_from_mcp_tools;
|
||||
pub use codex_core::connectors::list_cached_accessible_connectors_from_mcp_tools_with_http_state;
|
||||
pub use codex_core::connectors::with_app_enabled_state;
|
||||
use codex_core_plugins::PluginsManager;
|
||||
use codex_http_state::HttpStateContext;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::originator;
|
||||
@@ -26,20 +29,27 @@ use codex_plugin::AppConnectorId;
|
||||
|
||||
const DIRECTORY_CONNECTORS_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
|
||||
async fn apps_enabled(config: &Config) -> bool {
|
||||
async fn auth(config: &Config, http_state: Option<&HttpStateContext>) -> Option<CodexAuth> {
|
||||
let auth_manager =
|
||||
AuthManager::shared_from_config(config, /*enable_codex_api_key_env*/ false).await;
|
||||
let auth = auth_manager.auth().await;
|
||||
match http_state {
|
||||
Some(http_state) => auth_manager.auth_for_surface(http_state.surface()).await,
|
||||
None => auth_manager.auth().await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn apps_enabled(config: &Config, http_state: Option<&HttpStateContext>) -> bool {
|
||||
let auth = auth(config, http_state).await;
|
||||
config
|
||||
.features
|
||||
.apps_enabled_for_auth(auth.as_ref().is_some_and(CodexAuth::uses_codex_backend))
|
||||
}
|
||||
|
||||
async fn connector_auth(config: &Config) -> anyhow::Result<CodexAuth> {
|
||||
let auth_manager =
|
||||
AuthManager::shared_from_config(config, /*enable_codex_api_key_env*/ false).await;
|
||||
let auth = auth_manager
|
||||
.auth()
|
||||
async fn connector_auth(
|
||||
config: &Config,
|
||||
http_state: Option<&HttpStateContext>,
|
||||
) -> anyhow::Result<CodexAuth> {
|
||||
let auth = auth(config, http_state)
|
||||
.await
|
||||
.ok_or_else(|| anyhow::anyhow!("ChatGPT auth not available"))?;
|
||||
anyhow::ensure!(
|
||||
@@ -50,7 +60,7 @@ async fn connector_auth(config: &Config) -> anyhow::Result<CodexAuth> {
|
||||
}
|
||||
|
||||
pub async fn list_connectors(config: &Config) -> anyhow::Result<Vec<AppInfo>> {
|
||||
if !apps_enabled(config).await {
|
||||
if !apps_enabled(config, /*http_state*/ None).await {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let (connectors_result, accessible_result) = tokio::join!(
|
||||
@@ -72,11 +82,18 @@ pub async fn list_all_connectors(config: &Config) -> anyhow::Result<Vec<AppInfo>
|
||||
}
|
||||
|
||||
pub async fn list_cached_all_connectors(config: &Config) -> Option<Vec<AppInfo>> {
|
||||
if !apps_enabled(config).await {
|
||||
list_cached_all_connectors_with_http_state(config, /*http_state*/ None).await
|
||||
}
|
||||
|
||||
pub async fn list_cached_all_connectors_with_http_state(
|
||||
config: &Config,
|
||||
http_state: Option<&HttpStateContext>,
|
||||
) -> Option<Vec<AppInfo>> {
|
||||
if !apps_enabled(config, http_state).await {
|
||||
return Some(Vec::new());
|
||||
}
|
||||
|
||||
let auth = connector_auth(config).await.ok()?;
|
||||
let auth = connector_auth(config, http_state).await.ok()?;
|
||||
let cache_context = connector_directory_cache_context(config, &auth);
|
||||
let connectors = codex_connectors::cached_directory_connectors(&cache_context)?;
|
||||
let connectors = merge_plugin_connectors(
|
||||
@@ -96,22 +113,35 @@ pub async fn list_all_connectors_with_options(
|
||||
config: &Config,
|
||||
force_refetch: bool,
|
||||
) -> anyhow::Result<Vec<AppInfo>> {
|
||||
if !apps_enabled(config).await {
|
||||
list_all_connectors_with_options_and_http_state(config, force_refetch, /*http_state*/ None)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_all_connectors_with_options_and_http_state(
|
||||
config: &Config,
|
||||
force_refetch: bool,
|
||||
http_state: Option<HttpStateContext>,
|
||||
) -> anyhow::Result<Vec<AppInfo>> {
|
||||
if !apps_enabled(config, http_state.as_ref()).await {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let auth = connector_auth(config).await?;
|
||||
let auth = connector_auth(config, http_state.as_ref()).await?;
|
||||
let cache_context = connector_directory_cache_context(config, &auth);
|
||||
let connectors = codex_connectors::list_all_connectors_with_options(
|
||||
cache_context,
|
||||
auth.is_workspace_account(),
|
||||
force_refetch,
|
||||
|path| async move {
|
||||
chatgpt_get_request_with_timeout::<DirectoryListResponse>(
|
||||
config,
|
||||
path,
|
||||
Some(DIRECTORY_CONNECTORS_TIMEOUT),
|
||||
)
|
||||
.await
|
||||
|path| {
|
||||
let http_state = http_state.clone();
|
||||
async move {
|
||||
chatgpt_get_request_with_timeout_and_http_state::<DirectoryListResponse>(
|
||||
config,
|
||||
path,
|
||||
Some(DIRECTORY_CONNECTORS_TIMEOUT),
|
||||
http_state,
|
||||
)
|
||||
.await
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use codex_core::config::Config;
|
||||
use codex_http_state::HttpStateContext;
|
||||
use codex_http_state::HttpStateSurface;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::chatgpt_client::chatgpt_get_request;
|
||||
use crate::chatgpt_client::chatgpt_get_request_with_http_state;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct GetTaskResponse {
|
||||
@@ -36,5 +38,10 @@ pub struct OutputDiff {
|
||||
|
||||
pub(crate) async fn get_task(config: &Config, task_id: String) -> anyhow::Result<GetTaskResponse> {
|
||||
let path = format!("/wham/tasks/{task_id}");
|
||||
chatgpt_get_request(config, path).await
|
||||
chatgpt_get_request_with_http_state(
|
||||
config,
|
||||
path,
|
||||
HttpStateContext::new(config.codex_home.to_path_buf(), HttpStateSurface::CodexCli),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -5,10 +5,11 @@ use std::time::Instant;
|
||||
|
||||
use anyhow::Context;
|
||||
use codex_core::config::Config;
|
||||
use codex_http_state::HttpStateContext;
|
||||
use codex_login::CodexAuth;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::chatgpt_client::chatgpt_get_request_with_timeout;
|
||||
use crate::chatgpt_client::chatgpt_get_request_with_timeout_and_http_state;
|
||||
|
||||
const WORKSPACE_SETTINGS_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
const WORKSPACE_SETTINGS_CACHE_TTL: Duration = Duration::from_secs(15 * 60);
|
||||
@@ -85,6 +86,18 @@ pub async fn codex_plugins_enabled_for_workspace(
|
||||
config: &Config,
|
||||
auth: Option<&CodexAuth>,
|
||||
cache: Option<&WorkspaceSettingsCache>,
|
||||
) -> anyhow::Result<bool> {
|
||||
codex_plugins_enabled_for_workspace_with_http_state(
|
||||
config, auth, cache, /*http_state*/ None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn codex_plugins_enabled_for_workspace_with_http_state(
|
||||
config: &Config,
|
||||
auth: Option<&CodexAuth>,
|
||||
cache: Option<&WorkspaceSettingsCache>,
|
||||
http_state: Option<HttpStateContext>,
|
||||
) -> anyhow::Result<bool> {
|
||||
let Some(auth) = auth else {
|
||||
return Ok(true);
|
||||
@@ -115,10 +128,11 @@ pub async fn codex_plugins_enabled_for_workspace(
|
||||
}
|
||||
|
||||
let encoded_account_id = encode_path_segment(account_id);
|
||||
let settings: WorkspaceSettingsResponse = chatgpt_get_request_with_timeout(
|
||||
let settings: WorkspaceSettingsResponse = chatgpt_get_request_with_timeout_and_http_state(
|
||||
config,
|
||||
format!("/accounts/{encoded_account_id}/settings"),
|
||||
Some(WORKSPACE_SETTINGS_TIMEOUT),
|
||||
http_state,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user