mirror of
https://github.com/openai/codex.git
synced 2026-11-03 19:53:25 +00:00
[codex] add remote plugin HTTP state adapter [ci changed_files]
This commit is contained in:
Generated
+2
@@ -2660,12 +2660,14 @@ dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"codex-analytics",
|
||||
"codex-api",
|
||||
"codex-app-server-protocol",
|
||||
"codex-config",
|
||||
"codex-core-skills",
|
||||
"codex-exec-server",
|
||||
"codex-git-utils",
|
||||
"codex-hooks",
|
||||
"codex-http-state",
|
||||
"codex-login",
|
||||
"codex-model-provider",
|
||||
"codex-otel",
|
||||
|
||||
@@ -616,9 +616,8 @@ impl PluginRequestProcessor {
|
||||
// TODO(remote plugins): Remove this once remote plugins are ready and vertical plugins are
|
||||
// served directly from the normal remote catalog.
|
||||
if include_vertical && !config.features.enabled(Feature::RemotePlugin) {
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
match codex_core_plugins::remote::fetch_openai_curated_remote_collection_marketplace(
|
||||
&remote_plugin_service_config,
|
||||
auth.as_ref(),
|
||||
@@ -655,9 +654,8 @@ impl PluginRequestProcessor {
|
||||
remote_sources.push(RemoteMarketplaceSource::SharedWithMe);
|
||||
}
|
||||
if !remote_sources.is_empty() {
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
match codex_core_plugins::remote::fetch_remote_marketplaces(
|
||||
&remote_plugin_service_config,
|
||||
auth.as_ref(),
|
||||
@@ -966,9 +964,8 @@ impl PluginRequestProcessor {
|
||||
let share_context = match share_context {
|
||||
Some(context) => {
|
||||
let auth = self.auth_manager.auth().await;
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
match codex_core_plugins::remote::fetch_remote_plugin_share_context(
|
||||
&remote_plugin_service_config,
|
||||
auth.as_ref(),
|
||||
@@ -1072,9 +1069,8 @@ impl PluginRequestProcessor {
|
||||
)));
|
||||
}
|
||||
let auth = self.auth_manager.auth().await;
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
validate_remote_plugin_id(&plugin_name)?;
|
||||
let remote_detail = codex_core_plugins::remote::fetch_remote_plugin_detail(
|
||||
&remote_plugin_service_config,
|
||||
@@ -1130,9 +1126,8 @@ impl PluginRequestProcessor {
|
||||
}
|
||||
|
||||
let auth = self.auth_manager.auth().await;
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
let remote_skill_detail = codex_core_plugins::remote::fetch_remote_plugin_skill_detail(
|
||||
&remote_plugin_service_config,
|
||||
auth.as_ref(),
|
||||
@@ -1183,9 +1178,8 @@ impl PluginRequestProcessor {
|
||||
validate_client_plugin_share_targets(share_targets)?;
|
||||
}
|
||||
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
let access_policy = codex_core_plugins::remote::RemotePluginShareAccessPolicy {
|
||||
discoverability: discoverability.map(remote_plugin_share_discoverability),
|
||||
share_targets: share_targets.map(remote_plugin_share_targets),
|
||||
@@ -1226,9 +1220,8 @@ impl PluginRequestProcessor {
|
||||
}
|
||||
validate_client_plugin_share_targets(&share_targets)?;
|
||||
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
let result = codex_core_plugins::remote::update_remote_plugin_share_targets(
|
||||
&remote_plugin_service_config,
|
||||
auth.as_ref(),
|
||||
@@ -1256,9 +1249,8 @@ impl PluginRequestProcessor {
|
||||
_params: PluginShareListParams,
|
||||
) -> Result<PluginShareListResponse, JSONRPCErrorError> {
|
||||
let (config, auth) = self.load_plugin_share_config_and_auth().await?;
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
let data = codex_core_plugins::remote::list_remote_plugin_shares(
|
||||
&remote_plugin_service_config,
|
||||
auth.as_ref(),
|
||||
@@ -1295,9 +1287,8 @@ impl PluginRequestProcessor {
|
||||
return Err(invalid_request("invalid remote plugin id"));
|
||||
}
|
||||
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
let result = codex_core_plugins::remote::checkout_remote_plugin_share(
|
||||
&remote_plugin_service_config,
|
||||
auth.as_ref(),
|
||||
@@ -1328,9 +1319,8 @@ impl PluginRequestProcessor {
|
||||
return Err(invalid_request("invalid remote plugin id"));
|
||||
}
|
||||
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
codex_core_plugins::remote::delete_remote_plugin_share(
|
||||
&remote_plugin_service_config,
|
||||
auth.as_ref(),
|
||||
@@ -1448,9 +1438,8 @@ impl PluginRequestProcessor {
|
||||
validate_remote_plugin_id(&remote_plugin_id)?;
|
||||
|
||||
let auth = self.auth_manager.auth().await;
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
let remote_detail =
|
||||
codex_core_plugins::remote::fetch_remote_plugin_detail_with_download_urls(
|
||||
&remote_plugin_service_config,
|
||||
@@ -1796,9 +1785,8 @@ impl PluginRequestProcessor {
|
||||
validate_remote_plugin_id(&plugin_id)?;
|
||||
|
||||
let auth = self.auth_manager.auth().await;
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
let uninstall_result = codex_core_plugins::remote::uninstall_remote_plugin(
|
||||
&remote_plugin_service_config,
|
||||
auth.as_ref(),
|
||||
|
||||
@@ -15,12 +15,14 @@ workspace = true
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
codex-analytics = { workspace = true }
|
||||
codex-api = { workspace = true }
|
||||
codex-app-server-protocol = { workspace = true }
|
||||
codex-config = { workspace = true }
|
||||
codex-core-skills = { workspace = true }
|
||||
codex-exec-server = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-hooks = { workspace = true }
|
||||
codex-http-state = { workspace = true }
|
||||
codex-login = { workspace = true }
|
||||
codex-model-provider = { workspace = true }
|
||||
codex-otel = { workspace = true }
|
||||
|
||||
@@ -173,9 +173,7 @@ struct ConfiguredMarketplaceUpgradeState {
|
||||
}
|
||||
|
||||
fn remote_plugin_service_config(config: &PluginsConfigInput) -> RemotePluginServiceConfig {
|
||||
RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
}
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone())
|
||||
}
|
||||
|
||||
fn featured_plugin_ids_cache_key(
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
use crate::store::PLUGINS_CACHE_DIR;
|
||||
use crate::store::PluginStore;
|
||||
use codex_api::SharedAuthProvider;
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use codex_app_server_protocol::PluginAuthPolicy;
|
||||
use codex_app_server_protocol::PluginAvailability;
|
||||
use codex_app_server_protocol::PluginInstallPolicy;
|
||||
use codex_app_server_protocol::PluginInterface;
|
||||
use codex_app_server_protocol::SkillInterface;
|
||||
use codex_http_state::HttpStateContext;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_plugin::PluginId;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use reqwest::RequestBuilder;
|
||||
use reqwest::header::HeaderMap;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
@@ -24,6 +27,7 @@ use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
mod catalog_cache;
|
||||
pub(crate) mod http_state;
|
||||
mod remote_installed_plugin_sync;
|
||||
mod share;
|
||||
|
||||
@@ -91,9 +95,24 @@ const REMOTE_INSTALLED_MARKETPLACE_DISPLAY_ORDER: [(&str, &str); 5] = [
|
||||
),
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RemotePluginServiceConfig {
|
||||
pub chatgpt_base_url: String,
|
||||
http_state: Option<HttpStateContext>,
|
||||
}
|
||||
|
||||
impl RemotePluginServiceConfig {
|
||||
pub fn new(chatgpt_base_url: String) -> Self {
|
||||
Self {
|
||||
chatgpt_base_url,
|
||||
http_state: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_http_state(mut self, http_state: HttpStateContext) -> Self {
|
||||
self.http_state = Some(http_state);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -871,7 +890,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 request = authenticated_request(client.get(&url), auth)?;
|
||||
let request = authenticated_request(config, client.get(&url), auth, &url);
|
||||
let response: RemotePluginSkillDetailResponse = send_and_decode(request, &url).await?;
|
||||
if response.plugin_id != plugin_id {
|
||||
return Err(RemotePluginCatalogError::UnexpectedPluginId {
|
||||
@@ -973,7 +992,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 request = authenticated_request(client.post(&url), auth)?;
|
||||
let request = authenticated_request(config, client.post(&url), auth, &url);
|
||||
let response: RemotePluginMutationResponse = send_and_decode(request, &url).await?;
|
||||
if response.id != plugin_id {
|
||||
return Err(RemotePluginCatalogError::UnexpectedPluginId {
|
||||
@@ -1009,7 +1028,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 request = authenticated_request(client.post(&url), auth)?;
|
||||
let request = authenticated_request(config, client.post(&url), auth, &url);
|
||||
let response: RemotePluginMutationResponse = send_and_decode(request, &url).await?;
|
||||
if response.id != plugin_id {
|
||||
return Err(RemotePluginCatalogError::UnexpectedPluginId {
|
||||
@@ -1399,7 +1418,7 @@ async fn get_remote_plugin_list_page(
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/ps/plugins/list");
|
||||
let client = build_reqwest_client();
|
||||
let mut request = authenticated_request(client.get(&url), auth)?;
|
||||
let mut request = authenticated_request(config, client.get(&url), auth, &url);
|
||||
request = request.query(&[("scope", scope.api_value())]);
|
||||
request = request.query(&[("limit", REMOTE_PLUGIN_LIST_PAGE_LIMIT)]);
|
||||
if let Some(collection) = collection {
|
||||
@@ -1419,7 +1438,7 @@ async fn get_remote_shared_workspace_plugins_page(
|
||||
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 mut request = authenticated_request(client.get(&url), auth)?;
|
||||
let mut request = authenticated_request(config, client.get(&url), auth, &url);
|
||||
request = request.query(&[("limit", REMOTE_PLUGIN_LIST_PAGE_LIMIT)]);
|
||||
if let Some(page_token) = page_token {
|
||||
request = request.query(&[("pageToken", page_token)]);
|
||||
@@ -1437,7 +1456,7 @@ async fn get_remote_plugin_installed_page(
|
||||
let base_url = config.chatgpt_base_url.trim_end_matches('/');
|
||||
let url = format!("{base_url}/ps/plugins/installed");
|
||||
let client = build_reqwest_client();
|
||||
let mut request = authenticated_request(client.get(&url), auth)?;
|
||||
let mut request = authenticated_request(config, client.get(&url), auth, &url);
|
||||
request = request.query(&[("scope", scope.api_value())]);
|
||||
if include_download_urls {
|
||||
request = request.query(&[("includeDownloadUrls", true)]);
|
||||
@@ -1457,7 +1476,7 @@ async fn fetch_plugin_detail(
|
||||
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 mut request = authenticated_request(client.get(&url), auth)?;
|
||||
let mut request = authenticated_request(config, client.get(&url), auth, &url);
|
||||
if include_download_urls {
|
||||
request = request.query(&[("includeDownloadUrls", true)]);
|
||||
}
|
||||
@@ -1496,25 +1515,62 @@ fn ensure_chatgpt_auth(auth: Option<&CodexAuth>) -> Result<&CodexAuth, RemotePlu
|
||||
}
|
||||
|
||||
fn authenticated_request(
|
||||
config: &RemotePluginServiceConfig,
|
||||
request: RequestBuilder,
|
||||
auth: &CodexAuth,
|
||||
) -> Result<RequestBuilder, RemotePluginCatalogError> {
|
||||
Ok(request
|
||||
.timeout(REMOTE_PLUGIN_CATALOG_TIMEOUT)
|
||||
.headers(codex_model_provider::auth_provider_from_auth(auth).to_auth_headers()))
|
||||
url: &str,
|
||||
) -> AuthenticatedRequest {
|
||||
let (auth_provider, request_headers) = http_state::request_headers(config, auth, url);
|
||||
AuthenticatedRequest {
|
||||
request: request
|
||||
.timeout(REMOTE_PLUGIN_CATALOG_TIMEOUT)
|
||||
.headers(request_headers.clone()),
|
||||
auth_provider,
|
||||
request_headers,
|
||||
}
|
||||
}
|
||||
|
||||
struct AuthenticatedRequest {
|
||||
request: RequestBuilder,
|
||||
auth_provider: SharedAuthProvider,
|
||||
request_headers: HeaderMap,
|
||||
}
|
||||
|
||||
impl AuthenticatedRequest {
|
||||
fn query<T: Serialize + ?Sized>(mut self, query: &T) -> Self {
|
||||
self.request = self.request.query(query);
|
||||
self
|
||||
}
|
||||
|
||||
fn json<T: Serialize + ?Sized>(mut self, body: &T) -> Self {
|
||||
self.request = self.request.json(body);
|
||||
self
|
||||
}
|
||||
|
||||
async fn send(self, url: &str) -> Result<reqwest::Response, RemotePluginCatalogError> {
|
||||
let response =
|
||||
self.request
|
||||
.send()
|
||||
.await
|
||||
.map_err(|source| RemotePluginCatalogError::Request {
|
||||
url: url.to_string(),
|
||||
source,
|
||||
})?;
|
||||
http_state::observe_response_headers(
|
||||
&self.auth_provider,
|
||||
url,
|
||||
&self.request_headers,
|
||||
response.headers(),
|
||||
);
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_and_decode<T: for<'de> Deserialize<'de>>(
|
||||
request: RequestBuilder,
|
||||
request: AuthenticatedRequest,
|
||||
url: &str,
|
||||
) -> Result<T, RemotePluginCatalogError> {
|
||||
let response = request
|
||||
.send()
|
||||
.await
|
||||
.map_err(|source| RemotePluginCatalogError::Request {
|
||||
url: url.to_string(),
|
||||
source,
|
||||
})?;
|
||||
let response = request.send(url).await?;
|
||||
let status = response.status();
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
if !status.is_success() {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
use super::RemotePluginServiceConfig;
|
||||
use codex_api::SharedAuthProvider;
|
||||
use codex_login::CodexAuth;
|
||||
use reqwest::header::HeaderMap;
|
||||
|
||||
pub(crate) fn request_headers(
|
||||
config: &RemotePluginServiceConfig,
|
||||
auth: &CodexAuth,
|
||||
url: &str,
|
||||
) -> (SharedAuthProvider, HeaderMap) {
|
||||
let auth_provider = codex_model_provider::with_native_integrity_state(
|
||||
codex_model_provider::auth_provider_from_auth(auth),
|
||||
Some(auth),
|
||||
config.http_state.clone(),
|
||||
);
|
||||
let mut request_headers = HeaderMap::new();
|
||||
auth_provider.add_auth_headers_for_url(url, &mut request_headers);
|
||||
(auth_provider, request_headers)
|
||||
}
|
||||
|
||||
pub(crate) fn observe_response_headers(
|
||||
auth_provider: &SharedAuthProvider,
|
||||
url: &str,
|
||||
request_headers: &HeaderMap,
|
||||
response_headers: &HeaderMap,
|
||||
) {
|
||||
auth_provider.observe_response_headers(url, request_headers, response_headers);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "http_state_tests.rs"]
|
||||
mod tests;
|
||||
@@ -0,0 +1,42 @@
|
||||
use super::*;
|
||||
use codex_http_state::HttpStateContext;
|
||||
use codex_http_state::HttpStateSurface;
|
||||
use pretty_assertions::assert_eq;
|
||||
use reqwest::header::HeaderValue;
|
||||
use tempfile::TempDir;
|
||||
|
||||
const REQUEST_URL: &str = "https://chatgpt.com/backend-api/ps/plugins/list";
|
||||
|
||||
#[test]
|
||||
fn plugin_request_headers_attach_and_rotate_native_integrity_state() {
|
||||
let codex_home = TempDir::new().expect("create temp dir");
|
||||
let state = HttpStateContext::new(codex_home.path().to_path_buf(), HttpStateSurface::CodexCli);
|
||||
state
|
||||
.set_for_surface(HttpStateSurface::CodexCli, "state-before".to_string())
|
||||
.expect("seed state");
|
||||
let config = RemotePluginServiceConfig::new("https://chatgpt.com/backend-api".to_string())
|
||||
.with_http_state(state.clone());
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
|
||||
let (auth_provider, request_headers) = request_headers(&config, &auth, REQUEST_URL);
|
||||
assert_eq!(
|
||||
request_headers.get("X-OAI-IS"),
|
||||
Some(&HeaderValue::from_static("state-before"))
|
||||
);
|
||||
|
||||
let mut response_headers = HeaderMap::new();
|
||||
response_headers.insert("X-OAI-IS-Update", HeaderValue::from_static("state-after"));
|
||||
observe_response_headers(
|
||||
&auth_provider,
|
||||
REQUEST_URL,
|
||||
&request_headers,
|
||||
&response_headers,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
state
|
||||
.get_for_surface(HttpStateSurface::CodexCli)
|
||||
.expect("read state"),
|
||||
Some("state-after".to_string())
|
||||
);
|
||||
}
|
||||
@@ -4,7 +4,6 @@ use crate::plugin_bundle_archive::pack_plugin_bundle_tar_gz;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use reqwest::RequestBuilder;
|
||||
use reqwest::StatusCode;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
@@ -280,7 +279,7 @@ pub async fn delete_remote_plugin_share(
|
||||
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 request = authenticated_request(client.delete(&url), auth)?;
|
||||
let request = authenticated_request(config, client.delete(&url), auth, &url);
|
||||
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) {
|
||||
warn!(
|
||||
@@ -313,7 +312,7 @@ pub async fn update_remote_plugin_share_targets(
|
||||
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 request = authenticated_request(client.put(&url), auth)?.json(
|
||||
let request = authenticated_request(config, client.put(&url), auth, &url).json(
|
||||
&RemotePluginShareUpdateTargetsRequest {
|
||||
discoverability,
|
||||
targets,
|
||||
@@ -379,7 +378,7 @@ async fn get_created_workspace_plugins_page(
|
||||
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 mut request = authenticated_request(client.get(&url), auth)?;
|
||||
let mut request = authenticated_request(config, client.get(&url), auth, &url);
|
||||
request = request.query(&[("limit", REMOTE_PLUGIN_LIST_PAGE_LIMIT)]);
|
||||
if let Some(page_token) = page_token {
|
||||
request = request.query(&[("pageToken", page_token)]);
|
||||
@@ -397,7 +396,7 @@ async fn create_workspace_plugin_upload(
|
||||
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 request = authenticated_request(client.post(&url), auth)?.json(
|
||||
let request = authenticated_request(config, client.post(&url), auth, &url).json(
|
||||
&RemoteWorkspacePluginUploadUrlRequest {
|
||||
filename,
|
||||
mime_type: "application/gzip",
|
||||
@@ -451,7 +450,7 @@ async fn finalize_workspace_plugin_upload(
|
||||
format!("{base_url}/public/plugins/workspace")
|
||||
};
|
||||
let client = build_reqwest_client();
|
||||
let request = authenticated_request(client.post(&url), auth)?.json(&body);
|
||||
let request = authenticated_request(config, client.post(&url), auth, &url).json(&body);
|
||||
send_and_decode(request, &url).await
|
||||
}
|
||||
|
||||
@@ -489,17 +488,11 @@ fn archive_plugin_for_upload_with_limit(
|
||||
}
|
||||
|
||||
async fn send_and_expect_status(
|
||||
request: RequestBuilder,
|
||||
request: AuthenticatedRequest,
|
||||
url_for_error: &str,
|
||||
expected_statuses: &[StatusCode],
|
||||
) -> Result<(), RemotePluginCatalogError> {
|
||||
let response = request
|
||||
.send()
|
||||
.await
|
||||
.map_err(|source| RemotePluginCatalogError::Request {
|
||||
url: url_for_error.to_string(),
|
||||
source,
|
||||
})?;
|
||||
let response = request.send(url_for_error).await?;
|
||||
let status = response.status();
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
if !expected_statuses.contains(&status) {
|
||||
|
||||
@@ -23,9 +23,7 @@ use wiremock::matchers::query_param;
|
||||
use wiremock::matchers::query_param_is_missing;
|
||||
|
||||
fn test_config(server: &MockServer) -> RemotePluginServiceConfig {
|
||||
RemotePluginServiceConfig {
|
||||
chatgpt_base_url: format!("{}/backend-api", server.uri()),
|
||||
}
|
||||
RemotePluginServiceConfig::new(format!("{}/backend-api", server.uri()))
|
||||
}
|
||||
|
||||
fn test_auth() -> CodexAuth {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::remote::RemotePluginServiceConfig;
|
||||
use crate::remote::http_state;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::build_reqwest_client;
|
||||
use codex_protocol::protocol::Product;
|
||||
@@ -130,10 +131,11 @@ 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 (auth_provider, request_headers) = http_state::request_headers(config, auth, &url);
|
||||
let request = client
|
||||
.get(&url)
|
||||
.timeout(REMOTE_PLUGIN_FETCH_TIMEOUT)
|
||||
.headers(codex_model_provider::auth_provider_from_auth(auth).to_auth_headers());
|
||||
.headers(request_headers.clone());
|
||||
|
||||
let response = request
|
||||
.send()
|
||||
@@ -142,6 +144,12 @@ pub async fn fetch_remote_plugin_status(
|
||||
url: url.clone(),
|
||||
source,
|
||||
})?;
|
||||
http_state::observe_response_headers(
|
||||
&auth_provider,
|
||||
&url,
|
||||
&request_headers,
|
||||
response.headers(),
|
||||
);
|
||||
let status = response.status();
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
if !status.is_success() {
|
||||
@@ -170,9 +178,11 @@ pub async fn fetch_remote_featured_plugin_ids(
|
||||
)])
|
||||
.timeout(REMOTE_FEATURED_PLUGIN_FETCH_TIMEOUT);
|
||||
|
||||
if let Some(auth) = auth.filter(|auth| auth.uses_codex_backend()) {
|
||||
request =
|
||||
request.headers(codex_model_provider::auth_provider_from_auth(auth).to_auth_headers());
|
||||
let auth_context = auth
|
||||
.filter(|auth| auth.uses_codex_backend())
|
||||
.map(|auth| http_state::request_headers(config, auth, &url));
|
||||
if let Some((_, request_headers)) = &auth_context {
|
||||
request = request.headers(request_headers.clone());
|
||||
}
|
||||
|
||||
let response = request
|
||||
@@ -182,6 +192,14 @@ pub async fn fetch_remote_featured_plugin_ids(
|
||||
url: url.clone(),
|
||||
source,
|
||||
})?;
|
||||
if let Some((auth_provider, request_headers)) = &auth_context {
|
||||
http_state::observe_response_headers(
|
||||
auth_provider,
|
||||
&url,
|
||||
request_headers,
|
||||
response.headers(),
|
||||
);
|
||||
}
|
||||
let status = response.status();
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
if !status.is_success() {
|
||||
@@ -237,10 +255,11 @@ async fn post_remote_plugin_mutation(
|
||||
let auth = ensure_codex_backend_auth(auth)?;
|
||||
let url = remote_plugin_mutation_url(config, plugin_id, action)?;
|
||||
let client = build_reqwest_client();
|
||||
let (auth_provider, request_headers) = http_state::request_headers(config, auth, &url);
|
||||
let request = client
|
||||
.post(url.clone())
|
||||
.timeout(REMOTE_PLUGIN_MUTATION_TIMEOUT)
|
||||
.headers(codex_model_provider::auth_provider_from_auth(auth).to_auth_headers());
|
||||
.headers(request_headers.clone());
|
||||
|
||||
let response = request
|
||||
.send()
|
||||
@@ -249,6 +268,12 @@ async fn post_remote_plugin_mutation(
|
||||
url: url.clone(),
|
||||
source,
|
||||
})?;
|
||||
http_state::observe_response_headers(
|
||||
&auth_provider,
|
||||
&url,
|
||||
&request_headers,
|
||||
response.headers(),
|
||||
);
|
||||
let status = response.status();
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
if !status.is_success() {
|
||||
|
||||
@@ -320,11 +320,11 @@ remote_plugin = true
|
||||
let mut config = load_plugins_config(codex_home.path()).await;
|
||||
config.chatgpt_base_url = format!("{}/backend-api", server.uri());
|
||||
let plugins_manager = PluginsManager::new(config.codex_home.to_path_buf());
|
||||
let remote_plugin_service_config =
|
||||
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone());
|
||||
fetch_and_cache_global_remote_plugin_catalog(
|
||||
codex_home.path(),
|
||||
&RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
},
|
||||
&remote_plugin_service_config,
|
||||
Some(&auth),
|
||||
)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user