From 238b3d20a5092cc6a85405f579a50e8dcab935f8 Mon Sep 17 00:00:00 2001 From: Owen Lin Date: Tue, 19 May 2026 15:16:56 -0700 Subject: [PATCH] Use request originator for remote plugin requests --- codex-rs/app-server/src/message_processor.rs | 38 +++++-- .../src/request_processors/plugins.rs | 105 ++++++++++++------ codex-rs/core-plugins/src/manager.rs | 4 + codex-rs/core-plugins/src/remote.rs | 99 ++++++++++++----- .../remote/remote_installed_plugin_sync.rs | 14 ++- codex-rs/core-plugins/src/remote/share.rs | 34 ++++-- .../core-plugins/src/remote/share/checkout.rs | 3 + .../core-plugins/src/remote/share/tests.rs | 22 +++- 8 files changed, 232 insertions(+), 87 deletions(-) diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index 68cabdd9bb..e5cb37d197 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -1096,33 +1096,49 @@ impl MessageProcessor { self.marketplace_processor.marketplace_upgrade(params).await } ClientRequest::PluginList { params, .. } => { - self.plugin_processor.plugin_list(params).await + self.plugin_processor + .plugin_list(params, &request_context) + .await } ClientRequest::PluginInstalled { params, .. } => { - self.plugin_processor.plugin_installed(params).await + self.plugin_processor + .plugin_installed(params, &request_context) + .await } ClientRequest::PluginRead { params, .. } => { - self.plugin_processor.plugin_read(params).await + self.plugin_processor + .plugin_read(params, &request_context) + .await } ClientRequest::PluginSkillRead { params, .. } => { - self.plugin_processor.plugin_skill_read(params).await + self.plugin_processor + .plugin_skill_read(params, &request_context) + .await } ClientRequest::PluginShareSave { params, .. } => { - self.plugin_processor.plugin_share_save(params).await + self.plugin_processor + .plugin_share_save(params, &request_context) + .await } ClientRequest::PluginShareUpdateTargets { params, .. } => { self.plugin_processor - .plugin_share_update_targets(params) + .plugin_share_update_targets(params, &request_context) .await } ClientRequest::PluginShareList { params, .. } => { - self.plugin_processor.plugin_share_list(params).await + self.plugin_processor + .plugin_share_list(params, &request_context) + .await } ClientRequest::PluginShareCheckout { params, .. } => { - self.plugin_processor.plugin_share_checkout(params).await + self.plugin_processor + .plugin_share_checkout(params, &request_context) + .await } ClientRequest::PluginShareDelete { params, .. } => { - self.plugin_processor.plugin_share_delete(params).await + self.plugin_processor + .plugin_share_delete(params, &request_context) + .await } ClientRequest::AppsList { params, .. } => { self.apps_processor @@ -1134,12 +1150,12 @@ impl MessageProcessor { } ClientRequest::PluginInstall { params, .. } => { self.plugin_processor - .plugin_install(params, request_context.originator()) + .plugin_install(params, &request_context) .await } ClientRequest::PluginUninstall { params, .. } => { self.plugin_processor - .plugin_uninstall(params, request_context.originator()) + .plugin_uninstall(params, &request_context) .await } ClientRequest::ModelList { params, .. } => { diff --git a/codex-rs/app-server/src/request_processors/plugins.rs b/codex-rs/app-server/src/request_processors/plugins.rs index 830d014e69..33f071729f 100644 --- a/codex-rs/app-server/src/request_processors/plugins.rs +++ b/codex-rs/app-server/src/request_processors/plugins.rs @@ -9,7 +9,6 @@ use codex_config::types::McpServerConfig; use codex_core_plugins::remote::RemotePluginScope; use codex_core_plugins::remote::is_valid_remote_plugin_id; use codex_core_plugins::remote::validate_remote_plugin_id; -use codex_login::default_client::Originator; use codex_mcp::McpOAuthLoginSupport; use codex_mcp::oauth_login_support; use codex_mcp::should_retry_without_scopes; @@ -297,8 +296,9 @@ impl PluginRequestProcessor { pub(crate) async fn plugin_list( &self, params: PluginListParams, + request_context: &RequestContext, ) -> Result, JSONRPCErrorError> { - self.plugin_list_response(params) + self.plugin_list_response(params, request_context.originator()) .await .map(|response| Some(response.into())) } @@ -306,8 +306,9 @@ impl PluginRequestProcessor { pub(crate) async fn plugin_installed( &self, params: PluginInstalledParams, + request_context: &RequestContext, ) -> Result, JSONRPCErrorError> { - self.plugin_installed_response(params) + self.plugin_installed_response(params, request_context.originator()) .await .map(|response| Some(response.into())) } @@ -315,8 +316,9 @@ impl PluginRequestProcessor { pub(crate) async fn plugin_read( &self, params: PluginReadParams, + request_context: &RequestContext, ) -> Result, JSONRPCErrorError> { - self.plugin_read_response(params) + self.plugin_read_response(params, request_context.originator()) .await .map(|response| Some(response.into())) } @@ -324,8 +326,9 @@ impl PluginRequestProcessor { pub(crate) async fn plugin_skill_read( &self, params: PluginSkillReadParams, + request_context: &RequestContext, ) -> Result, JSONRPCErrorError> { - self.plugin_skill_read_response(params) + self.plugin_skill_read_response(params, request_context.originator()) .await .map(|response| Some(response.into())) } @@ -333,8 +336,9 @@ impl PluginRequestProcessor { pub(crate) async fn plugin_share_save( &self, params: PluginShareSaveParams, + request_context: &RequestContext, ) -> Result, JSONRPCErrorError> { - self.plugin_share_save_response(params) + self.plugin_share_save_response(params, request_context.originator()) .await .map(|response| Some(response.into())) } @@ -342,8 +346,9 @@ impl PluginRequestProcessor { pub(crate) async fn plugin_share_update_targets( &self, params: PluginShareUpdateTargetsParams, + request_context: &RequestContext, ) -> Result, JSONRPCErrorError> { - self.plugin_share_update_targets_response(params) + self.plugin_share_update_targets_response(params, request_context.originator()) .await .map(|response| Some(response.into())) } @@ -351,8 +356,9 @@ impl PluginRequestProcessor { pub(crate) async fn plugin_share_list( &self, params: PluginShareListParams, + request_context: &RequestContext, ) -> Result, JSONRPCErrorError> { - self.plugin_share_list_response(params) + self.plugin_share_list_response(params, request_context.originator()) .await .map(|response| Some(response.into())) } @@ -360,8 +366,9 @@ impl PluginRequestProcessor { pub(crate) async fn plugin_share_checkout( &self, params: PluginShareCheckoutParams, + request_context: &RequestContext, ) -> Result, JSONRPCErrorError> { - self.plugin_share_checkout_response(params) + self.plugin_share_checkout_response(params, request_context.originator()) .await .map(|response| Some(response.into())) } @@ -369,8 +376,9 @@ impl PluginRequestProcessor { pub(crate) async fn plugin_share_delete( &self, params: PluginShareDeleteParams, + request_context: &RequestContext, ) -> Result, JSONRPCErrorError> { - self.plugin_share_delete_response(params) + self.plugin_share_delete_response(params, request_context.originator()) .await .map(|response| Some(response.into())) } @@ -378,9 +386,9 @@ impl PluginRequestProcessor { pub(crate) async fn plugin_install( &self, params: PluginInstallParams, - originator: &Originator, + request_context: &RequestContext, ) -> Result, JSONRPCErrorError> { - self.plugin_install_response(params, originator) + self.plugin_install_response(params, request_context.originator()) .await .map(|response| Some(response.into())) } @@ -388,9 +396,9 @@ impl PluginRequestProcessor { pub(crate) async fn plugin_uninstall( &self, params: PluginUninstallParams, - originator: &Originator, + request_context: &RequestContext, ) -> Result, JSONRPCErrorError> { - self.plugin_uninstall_response(params, originator) + self.plugin_uninstall_response(params, request_context.originator()) .await .map(|response| Some(response.into())) } @@ -467,6 +475,7 @@ impl PluginRequestProcessor { async fn plugin_list_response( &self, params: PluginListParams, + originator: &Originator, ) -> Result { let plugins_manager = self.thread_manager.plugins_manager(); let PluginListParams { @@ -586,6 +595,7 @@ impl PluginRequestProcessor { match codex_core_plugins::remote::fetch_remote_marketplaces( &remote_plugin_service_config, auth.as_ref(), + originator, &remote_sources, ) .await @@ -664,6 +674,7 @@ impl PluginRequestProcessor { async fn plugin_installed_response( &self, params: PluginInstalledParams, + originator: &Originator, ) -> Result { let plugins_manager = self.thread_manager.plugins_manager(); let PluginInstalledParams { @@ -717,6 +728,7 @@ impl PluginRequestProcessor { &plugins_input, &remote_installed_plugin_visible_scopes, auth.as_ref(), + originator, ) .await, ); @@ -813,6 +825,7 @@ impl PluginRequestProcessor { plugins_input: &codex_core_plugins::PluginsConfigInput, visible_scopes: &[RemotePluginScope], auth: Option<&CodexAuth>, + originator: &Originator, ) -> Vec { let remote_marketplaces = if let Some(remote_marketplaces) = plugins_manager.build_remote_installed_plugin_marketplaces_from_cache(visible_scopes) @@ -823,6 +836,7 @@ impl PluginRequestProcessor { .build_and_cache_remote_installed_plugin_marketplaces( plugins_input, auth, + originator, visible_scopes, Some(self.effective_plugins_changed_callback()), ) @@ -851,6 +865,7 @@ impl PluginRequestProcessor { async fn plugin_read_response( &self, params: PluginReadParams, + originator: &Originator, ) -> Result { let plugins_manager = self.thread_manager.plugins_manager(); let PluginReadParams { @@ -899,6 +914,7 @@ impl PluginRequestProcessor { match codex_core_plugins::remote::fetch_remote_plugin_share_context( &remote_plugin_service_config, auth.as_ref(), + originator, &context.remote_plugin_id, ) .await @@ -939,9 +955,13 @@ impl PluginRequestProcessor { None => None, }; let environment_manager = self.thread_manager.environment_manager(); - let app_summaries = - load_plugin_app_summaries(&config, &outcome.plugin.apps, &environment_manager) - .await; + let app_summaries = load_plugin_app_summaries( + &config, + &outcome.plugin.apps, + &environment_manager, + originator, + ) + .await; let visible_skills = outcome .plugin .skills @@ -1003,6 +1023,7 @@ impl PluginRequestProcessor { let remote_detail = codex_core_plugins::remote::fetch_remote_plugin_detail( &remote_plugin_service_config, auth.as_ref(), + originator, &remote_marketplace_name, &plugin_name, ) @@ -1017,8 +1038,13 @@ impl PluginRequestProcessor { .map(codex_plugin::AppConnectorId) .collect::>(); let environment_manager = self.thread_manager.environment_manager(); - let app_summaries = - load_plugin_app_summaries(&config, &plugin_apps, &environment_manager).await; + let app_summaries = load_plugin_app_summaries( + &config, + &plugin_apps, + &environment_manager, + originator, + ) + .await; remote_plugin_detail_to_info(remote_detail, app_summaries) } }; @@ -1029,6 +1055,7 @@ impl PluginRequestProcessor { async fn plugin_skill_read_response( &self, params: PluginSkillReadParams, + originator: &Originator, ) -> Result { let PluginSkillReadParams { remote_marketplace_name, @@ -1056,6 +1083,7 @@ impl PluginRequestProcessor { let remote_skill_detail = codex_core_plugins::remote::fetch_remote_plugin_skill_detail( &remote_plugin_service_config, auth.as_ref(), + originator, &remote_marketplace_name, &remote_plugin_id, &skill_name, @@ -1073,6 +1101,7 @@ impl PluginRequestProcessor { async fn plugin_share_save_response( &self, params: PluginShareSaveParams, + originator: &Originator, ) -> Result { let (config, auth) = self.load_plugin_share_config_and_auth().await?; if !config.features.enabled(Feature::PluginSharing) { @@ -1113,6 +1142,7 @@ impl PluginRequestProcessor { let result = codex_core_plugins::remote::save_remote_plugin_share( &remote_plugin_service_config, auth.as_ref(), + originator, config.codex_home.as_path(), &plugin_path, remote_plugin_id.as_deref(), @@ -1131,6 +1161,7 @@ impl PluginRequestProcessor { async fn plugin_share_update_targets_response( &self, params: PluginShareUpdateTargetsParams, + originator: &Originator, ) -> Result { let (config, auth) = self.load_plugin_share_config_and_auth().await?; if !config.features.enabled(Feature::PluginSharing) { @@ -1152,6 +1183,7 @@ impl PluginRequestProcessor { let result = codex_core_plugins::remote::update_remote_plugin_share_targets( &remote_plugin_service_config, auth.as_ref(), + originator, &remote_plugin_id, remote_plugin_share_targets(share_targets), remote_plugin_share_update_discoverability(discoverability), @@ -1174,6 +1206,7 @@ impl PluginRequestProcessor { async fn plugin_share_list_response( &self, _params: PluginShareListParams, + originator: &Originator, ) -> Result { let (config, auth) = self.load_plugin_share_config_and_auth().await?; let remote_plugin_service_config = RemotePluginServiceConfig { @@ -1182,6 +1215,7 @@ impl PluginRequestProcessor { let data = codex_core_plugins::remote::list_remote_plugin_shares( &remote_plugin_service_config, auth.as_ref(), + originator, config.codex_home.as_path(), ) .await @@ -1205,6 +1239,7 @@ impl PluginRequestProcessor { async fn plugin_share_checkout_response( &self, params: PluginShareCheckoutParams, + originator: &Originator, ) -> Result { let (config, auth) = self.load_plugin_share_config_and_auth().await?; if !config.features.enabled(Feature::PluginSharing) { @@ -1221,6 +1256,7 @@ impl PluginRequestProcessor { let result = codex_core_plugins::remote::checkout_remote_plugin_share( &remote_plugin_service_config, auth.as_ref(), + originator, config.codex_home.as_path(), &remote_plugin_id, ) @@ -1241,6 +1277,7 @@ impl PluginRequestProcessor { async fn plugin_share_delete_response( &self, params: PluginShareDeleteParams, + originator: &Originator, ) -> Result { let (config, auth) = self.load_plugin_share_config_and_auth().await?; let PluginShareDeleteParams { remote_plugin_id } = params; @@ -1254,6 +1291,7 @@ impl PluginRequestProcessor { codex_core_plugins::remote::delete_remote_plugin_share( &remote_plugin_service_config, auth.as_ref(), + originator, config.codex_home.as_path(), &remote_plugin_id, ) @@ -1350,6 +1388,7 @@ impl PluginRequestProcessor { auth.as_ref().is_some_and(CodexAuth::is_chatgpt_auth), &result.plugin_id.as_key(), &plugin_apps, + originator, ) .await; @@ -1381,6 +1420,7 @@ impl PluginRequestProcessor { codex_core_plugins::remote::fetch_remote_plugin_detail_with_download_urls( &remote_plugin_service_config, auth.as_ref(), + originator, &remote_marketplace_name, &remote_plugin_id, ) @@ -1432,6 +1472,7 @@ impl PluginRequestProcessor { codex_core_plugins::remote::install_remote_plugin( &remote_plugin_service_config, auth.as_ref(), + originator, &actual_remote_marketplace_name, &remote_plugin_id, ) @@ -1465,6 +1506,7 @@ impl PluginRequestProcessor { auth.as_ref().is_some_and(CodexAuth::is_chatgpt_auth), &result.plugin_id.as_key(), &plugin_apps, + originator, ) .await; @@ -1480,25 +1522,23 @@ impl PluginRequestProcessor { is_chatgpt_auth: bool, plugin_id: &str, plugin_apps: &[codex_plugin::AppConnectorId], + originator: &Originator, ) -> Vec { if plugin_apps.is_empty() || !config.features.apps_enabled_for_auth(is_chatgpt_auth) { return Vec::new(); } let environment_manager = self.thread_manager.environment_manager(); - let originator = Originator::process_default(); let originator_value = originator.value().to_string(); let (all_connectors_result, accessible_connectors_result) = tokio::join!( connectors::list_all_connectors_with_options_and_originator( - config, - /*force_refetch*/ true, - &originator, + config, /*force_refetch*/ true, originator, ), connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager( config, /*force_refetch*/ true, &environment_manager, - &originator_value, + &originator_value ), ); @@ -1509,7 +1549,7 @@ impl PluginRequestProcessor { plugin = plugin_id, "failed to load app metadata after plugin install: {err:#}" ); - connectors::list_cached_all_connectors_with_originator(config, &originator) + connectors::list_cached_all_connectors_with_originator(config, originator) .await .unwrap_or_default() } @@ -1645,7 +1685,9 @@ impl PluginRequestProcessor { return Err(invalid_request("invalid remote plugin id")); } if is_valid_remote_plugin_id(&plugin_id) { - return self.remote_plugin_uninstall_response(plugin_id).await; + return self + .remote_plugin_uninstall_response(plugin_id, originator) + .await; } let plugins_manager = self.thread_manager.plugins_manager(); @@ -1728,6 +1770,7 @@ impl PluginRequestProcessor { async fn remote_plugin_uninstall_response( &self, plugin_id: String, + originator: &Originator, ) -> Result { let config = self.load_latest_config(/*fallback_cwd*/ None).await?; if !config.features.enabled(Feature::Plugins) { @@ -1742,6 +1785,7 @@ impl PluginRequestProcessor { let uninstall_result = codex_core_plugins::remote::uninstall_remote_plugin( &remote_plugin_service_config, auth.as_ref(), + originator, config.codex_home.to_path_buf(), &plugin_id, ) @@ -1773,24 +1817,22 @@ async fn load_plugin_app_summaries( config: &Config, plugin_apps: &[codex_plugin::AppConnectorId], environment_manager: &EnvironmentManager, + originator: &Originator, ) -> Vec { if plugin_apps.is_empty() { return Vec::new(); } - let originator = Originator::process_default(); let originator_value = originator.value().to_string(); let connectors = match connectors::list_all_connectors_with_options_and_originator( - config, - /*force_refetch*/ false, - &originator, + config, /*force_refetch*/ false, originator, ) .await { Ok(connectors) => connectors, Err(err) => { warn!("failed to load app metadata for plugin/read: {err:#}"); - connectors::list_cached_all_connectors_with_originator(config, &originator) + connectors::list_cached_all_connectors_with_originator(config, originator) .await .unwrap_or_default() } @@ -1801,7 +1843,6 @@ async fn load_plugin_app_summaries( plugin_apps, &originator_value, ); - let accessible_connectors = match connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager( config, diff --git a/codex-rs/core-plugins/src/manager.rs b/codex-rs/core-plugins/src/manager.rs index a6c008849e..8cac58907f 100644 --- a/codex-rs/core-plugins/src/manager.rs +++ b/codex-rs/core-plugins/src/manager.rs @@ -615,12 +615,14 @@ impl PluginsManager { &self, config: &PluginsConfigInput, auth: Option<&CodexAuth>, + originator: &Originator, visible_scopes: &[RemotePluginScope], on_effective_plugins_changed: Option>, ) -> Result, RemotePluginCatalogError> { let plugins = crate::remote::fetch_remote_installed_plugins( &remote_plugin_service_config(config), auth, + originator, ) .await?; let marketplaces = @@ -1814,9 +1816,11 @@ impl PluginsManager { } }; + let originator = Originator::process_default(); let installed_plugins = crate::remote::fetch_remote_installed_plugins( &request.service_config, request.auth.as_ref(), + &originator, ) .await; match installed_plugins { diff --git a/codex-rs/core-plugins/src/remote.rs b/codex-rs/core-plugins/src/remote.rs index 17943e4e8c..2d80e3abe5 100644 --- a/codex-rs/core-plugins/src/remote.rs +++ b/codex-rs/core-plugins/src/remote.rs @@ -24,11 +24,6 @@ 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; @@ -489,6 +484,7 @@ struct RemotePluginMutationResponse { pub async fn fetch_remote_marketplaces( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, sources: &[RemoteMarketplaceSource], ) -> Result, RemotePluginCatalogError> { let auth = ensure_chatgpt_auth(auth)?; @@ -500,7 +496,15 @@ pub async fn fetch_remote_marketplaces( ) }); let workspace_installed_plugins = if needs_workspace_installed { - Some(fetch_installed_plugins_for_scope(config, auth, RemotePluginScope::Workspace).await?) + Some( + fetch_installed_plugins_for_scope( + config, + auth, + originator, + RemotePluginScope::Workspace, + ) + .await?, + ) } else { None }; @@ -510,8 +514,8 @@ pub async fn fetch_remote_marketplaces( RemoteMarketplaceSource::Global => { let scope = RemotePluginScope::Global; let (directory_plugins, installed_plugins) = tokio::try_join!( - fetch_directory_plugins_for_scope(config, auth, scope), - fetch_installed_plugins_for_scope(config, auth, scope), + fetch_directory_plugins_for_scope(config, auth, originator, scope), + fetch_installed_plugins_for_scope(config, auth, originator, scope), )?; if let Some(marketplace) = build_remote_marketplace( scope.marketplace_name(), @@ -526,7 +530,7 @@ pub async fn fetch_remote_marketplaces( RemoteMarketplaceSource::WorkspaceDirectory => { let scope = RemotePluginScope::Workspace; let directory_plugins = - fetch_directory_plugins_for_scope(config, auth, scope).await?; + fetch_directory_plugins_for_scope(config, auth, originator, scope).await?; if let Some(marketplace) = build_remote_marketplace( scope.marketplace_name(), scope.marketplace_display_name(), @@ -541,7 +545,8 @@ pub async fn fetch_remote_marketplaces( // The shared endpoint is the source of truth for plugins explicitly shared // with the user. Installed unlisted plugins that are not returned there are // link-installed and stay in the separate unlisted bucket. - let shared_plugins = fetch_shared_workspace_plugins(config, auth).await?; + let shared_plugins = + fetch_shared_workspace_plugins(config, auth, originator).await?; let shared_plugin_ids = shared_plugins .iter() .map(|plugin| plugin.id.clone()) @@ -652,16 +657,19 @@ fn build_remote_marketplace( pub(crate) async fn fetch_remote_installed_plugins( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, ) -> Result, RemotePluginCatalogError> { let auth = ensure_chatgpt_auth(auth)?; let global = async { let scope = RemotePluginScope::Global; - let installed_plugins = fetch_installed_plugins_for_scope(config, auth, scope).await?; + let installed_plugins = + fetch_installed_plugins_for_scope(config, auth, originator, scope).await?; Ok::<_, RemotePluginCatalogError>((scope, installed_plugins)) }; let workspace = async { let scope = RemotePluginScope::Workspace; - let installed_plugins = fetch_installed_plugins_for_scope(config, auth, scope).await?; + let installed_plugins = + fetch_installed_plugins_for_scope(config, auth, originator, scope).await?; Ok::<_, RemotePluginCatalogError>((scope, installed_plugins)) }; @@ -731,12 +739,14 @@ pub fn group_remote_installed_plugins_by_marketplaces( pub async fn fetch_remote_plugin_detail( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, marketplace_name: &str, plugin_id: &str, ) -> Result { fetch_remote_plugin_detail_with_download_url_option( config, auth, + originator, marketplace_name, plugin_id, /*include_download_urls*/ false, @@ -747,11 +757,12 @@ pub async fn fetch_remote_plugin_detail( pub async fn fetch_remote_plugin_share_context( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, plugin_id: &str, ) -> Result, RemotePluginCatalogError> { let auth = ensure_chatgpt_auth(auth)?; let plugin = fetch_plugin_detail( - config, auth, plugin_id, /*include_download_urls*/ false, + config, auth, originator, plugin_id, /*include_download_urls*/ false, ) .await?; remote_plugin_share_context(&plugin) @@ -760,12 +771,14 @@ pub async fn fetch_remote_plugin_share_context( pub async fn fetch_remote_plugin_detail_with_download_urls( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, marketplace_name: &str, plugin_id: &str, ) -> Result { fetch_remote_plugin_detail_with_download_url_option( config, auth, + originator, marketplace_name, plugin_id, /*include_download_urls*/ true, @@ -776,6 +789,7 @@ pub async fn fetch_remote_plugin_detail_with_download_urls( pub async fn fetch_remote_plugin_skill_detail( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, marketplace_name: &str, plugin_id: &str, skill_name: &str, @@ -788,7 +802,7 @@ pub async fn fetch_remote_plugin_skill_detail( } let url = remote_plugin_skill_detail_url(config, plugin_id, skill_name)?; - let client = build_process_reqwest_client(); + let client = build_reqwest_client(originator); let request = authenticated_request(client.get(&url), auth)?; let response: RemotePluginSkillDetailResponse = send_and_decode(request, &url).await?; if response.plugin_id != plugin_id { @@ -812,30 +826,42 @@ pub async fn fetch_remote_plugin_skill_detail( async fn fetch_remote_plugin_detail_with_download_url_option( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, _marketplace_name: &str, plugin_id: &str, include_download_urls: bool, ) -> Result { let auth = ensure_chatgpt_auth(auth)?; - let plugin = fetch_plugin_detail(config, auth, plugin_id, include_download_urls).await?; + let plugin = + fetch_plugin_detail(config, auth, originator, plugin_id, include_download_urls).await?; let scope = plugin.scope; let marketplace_name = remote_plugin_canonical_marketplace_name(&plugin)?.to_string(); // Remote plugin IDs uniquely identify remote plugins, so the caller-provided // marketplace name is not validated here. The backend detail response is the // source of truth for the plugin's actual scope/marketplace. - build_remote_plugin_detail(config, auth, scope, marketplace_name, plugin_id, plugin).await + build_remote_plugin_detail( + config, + auth, + originator, + scope, + marketplace_name, + plugin_id, + plugin, + ) + .await } async fn build_remote_plugin_detail( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, scope: RemotePluginScope, marketplace_name: String, plugin_id: &str, plugin: RemotePluginDirectoryItem, ) -> Result { - let installed_plugin = fetch_installed_plugins_for_scope(config, auth, scope) + let installed_plugin = fetch_installed_plugins_for_scope(config, auth, originator, scope) .await? .into_iter() .find(|installed_plugin| installed_plugin.plugin.id == plugin_id); @@ -880,6 +906,7 @@ async fn build_remote_plugin_detail( pub async fn install_remote_plugin( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, _marketplace_name: &str, plugin_id: &str, ) -> Result<(), RemotePluginCatalogError> { @@ -889,7 +916,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_process_reqwest_client(); + let client = build_reqwest_client(originator); let request = authenticated_request(client.post(&url), auth)?; let response: RemotePluginMutationResponse = send_and_decode(request, &url).await?; if response.id != plugin_id { @@ -912,12 +939,13 @@ pub async fn install_remote_plugin( pub async fn uninstall_remote_plugin( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, codex_home: PathBuf, plugin_id: &str, ) -> Result<(), RemotePluginCatalogError> { let auth = ensure_chatgpt_auth(auth)?; let plugin = fetch_plugin_detail( - config, auth, plugin_id, /*include_download_urls*/ false, + config, auth, originator, plugin_id, /*include_download_urls*/ false, ) .await?; let marketplace_name = remote_plugin_canonical_marketplace_name(&plugin)?.to_string(); @@ -925,7 +953,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_process_reqwest_client(); + let client = build_reqwest_client(originator); let request = authenticated_request(client.post(&url), auth)?; let response: RemotePluginMutationResponse = send_and_decode(request, &url).await?; if response.id != plugin_id { @@ -1179,13 +1207,15 @@ fn normalize_remote_default_prompt(prompt: &str) -> Option> { async fn fetch_directory_plugins_for_scope( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, scope: RemotePluginScope, ) -> Result, RemotePluginCatalogError> { let mut plugins = Vec::new(); let mut page_token = None; loop { let response = - get_remote_plugin_list_page(config, auth, scope, page_token.as_deref()).await?; + get_remote_plugin_list_page(config, auth, originator, scope, page_token.as_deref()) + .await?; plugins.extend(response.plugins); let Some(next_page_token) = response.pagination.next_page_token else { break; @@ -1198,12 +1228,18 @@ async fn fetch_directory_plugins_for_scope( async fn fetch_shared_workspace_plugins( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, ) -> Result, RemotePluginCatalogError> { let mut plugins = Vec::new(); let mut page_token = None; loop { - let response = - get_remote_shared_workspace_plugins_page(config, auth, page_token.as_deref()).await?; + let response = get_remote_shared_workspace_plugins_page( + config, + auth, + originator, + page_token.as_deref(), + ) + .await?; plugins.extend(response.plugins); let Some(next_page_token) = response.pagination.next_page_token else { break; @@ -1216,10 +1252,11 @@ async fn fetch_shared_workspace_plugins( async fn fetch_installed_plugins_for_scope( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, scope: RemotePluginScope, ) -> Result, RemotePluginCatalogError> { fetch_installed_plugins_for_scope_with_download_url( - config, auth, scope, /*include_download_urls*/ false, + config, auth, originator, scope, /*include_download_urls*/ false, ) .await } @@ -1227,6 +1264,7 @@ async fn fetch_installed_plugins_for_scope( async fn fetch_installed_plugins_for_scope_with_download_url( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, scope: RemotePluginScope, include_download_urls: bool, ) -> Result, RemotePluginCatalogError> { @@ -1236,6 +1274,7 @@ async fn fetch_installed_plugins_for_scope_with_download_url( let response = get_remote_plugin_installed_page( config, auth, + originator, scope, page_token.as_deref(), include_download_urls, @@ -1253,12 +1292,13 @@ async fn fetch_installed_plugins_for_scope_with_download_url( async fn get_remote_plugin_list_page( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, scope: RemotePluginScope, page_token: Option<&str>, ) -> Result { let base_url = config.chatgpt_base_url.trim_end_matches('/'); let url = format!("{base_url}/ps/plugins/list"); - let client = build_process_reqwest_client(); + let client = build_reqwest_client(originator); 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)]); @@ -1271,11 +1311,12 @@ async fn get_remote_plugin_list_page( async fn get_remote_shared_workspace_plugins_page( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, page_token: Option<&str>, ) -> Result { let base_url = config.chatgpt_base_url.trim_end_matches('/'); let url = format!("{base_url}/ps/plugins/workspace/shared"); - let client = build_process_reqwest_client(); + let client = build_reqwest_client(originator); 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,13 +1328,14 @@ async fn get_remote_shared_workspace_plugins_page( async fn get_remote_plugin_installed_page( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, scope: RemotePluginScope, page_token: Option<&str>, include_download_urls: bool, ) -> Result { let base_url = config.chatgpt_base_url.trim_end_matches('/'); let url = format!("{base_url}/ps/plugins/installed"); - let client = build_process_reqwest_client(); + let client = build_reqwest_client(originator); let mut request = authenticated_request(client.get(&url), auth)?; request = request.query(&[("scope", scope.api_value())]); if include_download_urls { @@ -1308,12 +1350,13 @@ async fn get_remote_plugin_installed_page( async fn fetch_plugin_detail( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, plugin_id: &str, include_download_urls: bool, ) -> Result { let base_url = config.chatgpt_base_url.trim_end_matches('/'); let url = format!("{base_url}/ps/plugins/{plugin_id}"); - let client = build_process_reqwest_client(); + let client = build_reqwest_client(originator); let mut request = authenticated_request(client.get(&url), auth)?; if include_download_urls { request = request.query(&[("includeDownloadUrls", true)]); diff --git a/codex-rs/core-plugins/src/remote/remote_installed_plugin_sync.rs b/codex-rs/core-plugins/src/remote/remote_installed_plugin_sync.rs index 69cbfe2569..731b8471ab 100644 --- a/codex-rs/core-plugins/src/remote/remote_installed_plugin_sync.rs +++ b/codex-rs/core-plugins/src/remote/remote_installed_plugin_sync.rs @@ -13,6 +13,7 @@ use crate::store::PLUGINS_CACHE_DIR; use crate::store::PluginStore; use crate::store::PluginStoreError; use codex_login::CodexAuth; +use codex_login::default_client::Originator; use codex_plugin::PluginId; use std::collections::BTreeMap; use std::collections::BTreeSet; @@ -128,10 +129,15 @@ pub async fn sync_remote_installed_plugin_bundles_once( auth: Option<&CodexAuth>, ) -> Result { let auth = ensure_chatgpt_auth(auth)?; + let originator = Originator::process_default(); let global = async { let scope = RemotePluginScope::Global; let installed_plugins = fetch_installed_plugins_for_scope_with_download_url( - config, auth, scope, /*include_download_urls*/ true, + config, + auth, + &originator, + scope, + /*include_download_urls*/ true, ) .await?; Ok::<_, RemotePluginCatalogError>((scope, installed_plugins)) @@ -139,7 +145,11 @@ pub async fn sync_remote_installed_plugin_bundles_once( let workspace = async { let scope = RemotePluginScope::Workspace; let installed_plugins = fetch_installed_plugins_for_scope_with_download_url( - config, auth, scope, /*include_download_urls*/ true, + config, + auth, + &originator, + scope, + /*include_download_urls*/ true, ) .await?; Ok::<_, RemotePluginCatalogError>((scope, installed_plugins)) diff --git a/codex-rs/core-plugins/src/remote/share.rs b/codex-rs/core-plugins/src/remote/share.rs index 0b454ed68b..5e5bf2be22 100644 --- a/codex-rs/core-plugins/src/remote/share.rs +++ b/codex-rs/core-plugins/src/remote/share.rs @@ -1,5 +1,7 @@ use super::*; use codex_login::CodexAuth; +use codex_login::default_client::Originator; +use codex_login::default_client::build_reqwest_client; use codex_utils_absolute_path::AbsolutePathBuf; use flate2::Compression; use flate2::write::GzEncoder; @@ -140,6 +142,7 @@ struct RemotePluginShareUpdateTargetsResponse { pub async fn save_remote_plugin_share( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, codex_home: &Path, plugin_path: &AbsolutePathBuf, remote_plugin_id: Option<&str>, @@ -157,6 +160,7 @@ pub async fn save_remote_plugin_share( let upload = create_workspace_plugin_upload( config, auth, + originator, &filename, archive_bytes.len(), remote_plugin_id, @@ -165,13 +169,14 @@ pub async fn save_remote_plugin_share( let etag = upload .etag .ok_or(RemotePluginCatalogError::MissingUploadEtag)?; - put_workspace_plugin_upload(&upload.upload_url, archive_bytes).await?; + put_workspace_plugin_upload(originator, &upload.upload_url, archive_bytes).await?; let share_targets = access_policy.share_targets; let share_targets = ensure_unlisted_workspace_target(auth, access_policy.discoverability, share_targets)?; let response = finalize_workspace_plugin_upload( config, auth, + originator, remote_plugin_id, RemoteWorkspacePluginCreateRequest { file_id: upload.file_id, @@ -207,16 +212,17 @@ pub async fn save_remote_plugin_share( pub async fn list_remote_plugin_shares( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, codex_home: &Path, ) -> Result, RemotePluginCatalogError> { let auth = ensure_chatgpt_auth(auth)?; - let created_plugins = fetch_created_workspace_plugins(config, auth).await?; + let created_plugins = fetch_created_workspace_plugins(config, auth, originator).await?; if created_plugins.is_empty() { return Ok(Vec::new()); } let installed_by_id = - fetch_installed_plugins_for_scope(config, auth, RemotePluginScope::Workspace) + fetch_installed_plugins_for_scope(config, auth, originator, RemotePluginScope::Workspace) .await? .into_iter() .map(|plugin| (plugin.plugin.id.clone(), plugin)) @@ -275,13 +281,14 @@ pub fn load_plugin_share_remote_ids_by_local_path( pub async fn delete_remote_plugin_share( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, codex_home: &Path, remote_plugin_id: &str, ) -> Result<(), RemotePluginCatalogError> { 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_process_reqwest_client(); + let client = build_reqwest_client(originator); 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) { @@ -296,6 +303,7 @@ pub async fn delete_remote_plugin_share( pub async fn update_remote_plugin_share_targets( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, remote_plugin_id: &str, targets: Vec, discoverability: RemotePluginShareUpdateDiscoverability, @@ -314,7 +322,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_process_reqwest_client(); + let client = build_reqwest_client(originator); let request = authenticated_request(client.put(&url), auth)?.json( &RemotePluginShareUpdateTargetsRequest { discoverability, @@ -358,12 +366,14 @@ fn ensure_unlisted_workspace_target( async fn fetch_created_workspace_plugins( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, ) -> Result, RemotePluginCatalogError> { let mut plugins = Vec::new(); let mut page_token = None; loop { let response = - get_created_workspace_plugins_page(config, auth, page_token.as_deref()).await?; + get_created_workspace_plugins_page(config, auth, originator, page_token.as_deref()) + .await?; plugins.extend(response.plugins); let Some(next_page_token) = response.pagination.next_page_token else { break; @@ -376,11 +386,12 @@ async fn fetch_created_workspace_plugins( async fn get_created_workspace_plugins_page( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, page_token: Option<&str>, ) -> Result { let base_url = config.chatgpt_base_url.trim_end_matches('/'); let url = format!("{base_url}/ps/plugins/workspace/created"); - let client = build_process_reqwest_client(); + let client = build_reqwest_client(originator); 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 { @@ -392,13 +403,14 @@ async fn get_created_workspace_plugins_page( async fn create_workspace_plugin_upload( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, filename: &str, size_bytes: usize, remote_plugin_id: Option<&str>, ) -> Result { let base_url = config.chatgpt_base_url.trim_end_matches('/'); let url = format!("{base_url}/public/plugins/workspace/upload-url"); - let client = build_process_reqwest_client(); + let client = build_reqwest_client(originator); let request = authenticated_request(client.post(&url), auth)?.json( &RemoteWorkspacePluginUploadUrlRequest { filename, @@ -411,10 +423,11 @@ async fn create_workspace_plugin_upload( } async fn put_workspace_plugin_upload( + originator: &Originator, upload_url: &str, archive_bytes: Vec, ) -> Result<(), RemotePluginCatalogError> { - let client = build_process_reqwest_client(); + let client = build_reqwest_client(originator); let request = client .put(upload_url) .timeout(REMOTE_PLUGIN_CATALOG_TIMEOUT) @@ -443,6 +456,7 @@ async fn put_workspace_plugin_upload( async fn finalize_workspace_plugin_upload( config: &RemotePluginServiceConfig, auth: &CodexAuth, + originator: &Originator, remote_plugin_id: Option<&str>, body: RemoteWorkspacePluginCreateRequest, ) -> Result { @@ -452,7 +466,7 @@ async fn finalize_workspace_plugin_upload( } else { format!("{base_url}/public/plugins/workspace") }; - let client = build_process_reqwest_client(); + let client = build_reqwest_client(originator); let request = authenticated_request(client.post(&url), auth)?.json(&body); send_and_decode(request, &url).await } diff --git a/codex-rs/core-plugins/src/remote/share/checkout.rs b/codex-rs/core-plugins/src/remote/share/checkout.rs index 8aabccff8b..b23a03c150 100644 --- a/codex-rs/core-plugins/src/remote/share/checkout.rs +++ b/codex-rs/core-plugins/src/remote/share/checkout.rs @@ -7,6 +7,7 @@ use super::local_paths; use codex_app_server_protocol::PluginAuthPolicy; use codex_app_server_protocol::PluginInstallPolicy; use codex_login::CodexAuth; +use codex_login::default_client::Originator; use codex_plugin::PluginId; use codex_plugin::validate_plugin_segment; use codex_utils_absolute_path::AbsolutePathBuf; @@ -37,12 +38,14 @@ pub struct RemotePluginShareCheckoutResult { pub async fn checkout_remote_plugin_share( config: &RemotePluginServiceConfig, auth: Option<&CodexAuth>, + originator: &Originator, codex_home: &Path, remote_plugin_id: &str, ) -> Result { let detail = super::super::fetch_remote_plugin_detail_with_download_urls( config, auth, + originator, REMOTE_WORKSPACE_SHARED_WITH_ME_PRIVATE_MARKETPLACE_NAME, remote_plugin_id, ) diff --git a/codex-rs/core-plugins/src/remote/share/tests.rs b/codex-rs/core-plugins/src/remote/share/tests.rs index f62051374e..7e39a58a88 100644 --- a/codex-rs/core-plugins/src/remote/share/tests.rs +++ b/codex-rs/core-plugins/src/remote/share/tests.rs @@ -229,9 +229,11 @@ async fn save_remote_plugin_share_creates_workspace_plugin() { .mount(&server) .await; + let originator = Originator::process_default(); let result = save_remote_plugin_share( &config, Some(&auth), + &originator, codex_home.path(), &plugin_path, /*remote_plugin_id*/ None, @@ -374,9 +376,11 @@ async fn save_remote_plugin_share_updates_existing_workspace_plugin() { .mount(&server) .await; + let originator = Originator::process_default(); let result = save_remote_plugin_share( &config, Some(&auth), + &originator, codex_home.path(), &plugin_path, Some("plugins_123"), @@ -445,9 +449,11 @@ async fn update_remote_plugin_share_targets_updates_targets() { .mount(&server) .await; + let originator = Originator::process_default(); let result = update_remote_plugin_share_targets( &config, Some(&auth), + &originator, "plugins_123", vec![ RemotePluginShareTarget { @@ -577,7 +583,8 @@ async fn list_remote_plugin_shares_fetches_created_workspace_plugins() { .mount(&server) .await; - let result = list_remote_plugin_shares(&config, Some(&auth), codex_home.path()) + let originator = Originator::process_default(); + let result = list_remote_plugin_shares(&config, Some(&auth), &originator, codex_home.path()) .await .unwrap(); @@ -683,9 +690,16 @@ async fn delete_remote_plugin_share_deletes_workspace_plugin() { .mount(&server) .await; - delete_remote_plugin_share(&config, Some(&auth), codex_home.path(), "plugins_123") - .await - .unwrap(); + let originator = Originator::process_default(); + delete_remote_plugin_share( + &config, + Some(&auth), + &originator, + codex_home.path(), + "plugins_123", + ) + .await + .unwrap(); assert_eq!( local_paths::load_plugin_share_local_paths(codex_home.path()).unwrap(), BTreeMap::new()