diff --git a/codex-rs/app-server/src/config/external_agent_config.rs b/codex-rs/app-server/src/config/external_agent_config.rs index eca927fc2..665588213 100644 --- a/codex-rs/app-server/src/config/external_agent_config.rs +++ b/codex-rs/app-server/src/config/external_agent_config.rs @@ -1147,7 +1147,7 @@ fn configured_marketplace_plugins( ) -> io::Result>> { let plugins_input = config.plugins_config_input(); let marketplaces = plugins_manager - .list_marketplaces_for_config(&plugins_input, &[]) + .list_marketplaces_for_config(&plugins_input, &[], /*include_openai_curated*/ true) .map_err(|err| { invalid_data_error(format!("failed to list configured marketplaces: {err}")) })?; diff --git a/codex-rs/app-server/src/request_processors/plugins.rs b/codex-rs/app-server/src/request_processors/plugins.rs index 54a5efbe7..246ca92be 100644 --- a/codex-rs/app-server/src/request_processors/plugins.rs +++ b/codex-rs/app-server/src/request_processors/plugins.rs @@ -573,6 +573,7 @@ impl PluginRequestProcessor { .list_marketplaces_for_config( &config_for_marketplace_listing, &roots_for_marketplace_listing, + /*include_openai_curated*/ true, )?; Ok::< ( @@ -837,8 +838,11 @@ impl PluginRequestProcessor { let config_for_marketplace_listing = plugins_input.clone(); let shared_plugin_ids_by_local_path = load_shared_plugin_ids_by_local_path(config)?; match tokio::task::spawn_blocking(move || { - let outcome = plugins_manager - .list_marketplaces_for_config(&config_for_marketplace_listing, &roots)?; + let outcome = plugins_manager.list_marketplaces_for_config( + &config_for_marketplace_listing, + &roots, + /*include_openai_curated*/ true, + )?; Ok::< ( Vec, diff --git a/codex-rs/cli/src/plugin_cmd.rs b/codex-rs/cli/src/plugin_cmd.rs index 94c388066..d6e28b5da 100644 --- a/codex-rs/cli/src/plugin_cmd.rs +++ b/codex-rs/cli/src/plugin_cmd.rs @@ -204,7 +204,7 @@ pub async fn run_plugin_list( .. } = load_plugin_command_context(overrides).await?; let outcome = manager - .list_marketplaces_for_config(&plugins_input, &[]) + .list_marketplaces_for_config(&plugins_input, &[], /*include_openai_curated*/ true) .context("failed to list marketplace plugins")?; ensure_configured_marketplace_snapshots_loaded( codex_home.as_path(), @@ -609,7 +609,7 @@ fn find_marketplace_for_plugin( plugin_name: &str, ) -> Result { let outcome = manager - .list_marketplaces_for_config(plugins_input, &[]) + .list_marketplaces_for_config(plugins_input, &[], /*include_openai_curated*/ true) .context("failed to list marketplace plugins")?; ensure_configured_marketplace_snapshots_loaded( codex_home, diff --git a/codex-rs/core-plugins/src/discoverable.rs b/codex-rs/core-plugins/src/discoverable.rs index d0742e980..a630877f5 100644 --- a/codex-rs/core-plugins/src/discoverable.rs +++ b/codex-rs/core-plugins/src/discoverable.rs @@ -87,7 +87,11 @@ impl PluginsManager { } let marketplaces = self - .list_marketplaces_for_config(&input.plugins, &[]) + .list_marketplaces_for_config( + &input.plugins, + &[], + /*include_openai_curated*/ !input.plugins.remote_plugin_enabled, + ) .context("failed to list plugin marketplaces for tool suggestions")? .marketplaces; let mut installed_app_connector_ids = self @@ -110,11 +114,6 @@ impl PluginsManager { let mut discoverable_plugins = Vec::::new(); for marketplace in marketplaces { let marketplace_name = marketplace.name; - if input.plugins.remote_plugin_enabled - && marketplace_name == OPENAI_CURATED_MARKETPLACE_NAME - { - continue; - } let use_legacy_local_curated_filter = should_use_legacy_local_curated_discovery_filter( &marketplace_name, marketplace.path.as_path(), diff --git a/codex-rs/core-plugins/src/manager.rs b/codex-rs/core-plugins/src/manager.rs index 095c5fd69..e08757f59 100644 --- a/codex-rs/core-plugins/src/manager.rs +++ b/codex-rs/core-plugins/src/manager.rs @@ -992,14 +992,19 @@ impl PluginsManager { &self, config: &PluginsConfigInput, additional_roots: &[AbsolutePathBuf], + include_openai_curated: bool, ) -> Result { if !config.plugins_enabled { return Ok(ConfiguredMarketplaceListOutcome::default()); } let (installed_plugins, enabled_plugins) = self.configured_plugin_states(config); - let marketplace_outcome = - self.discover_marketplaces_for_config(config, additional_roots)?; + let mut marketplace_roots = self.marketplace_roots(config, additional_roots); + if !include_openai_curated { + let curated_repo_root = curated_plugins_repo_path(self.codex_home.as_path()); + marketplace_roots.retain(|root| root.as_path() != curated_repo_root.as_path()); + } + let marketplace_outcome = list_marketplaces(&marketplace_roots)?; let mut seen_plugin_keys = HashSet::new(); let marketplaces = marketplace_outcome .marketplaces diff --git a/codex-rs/core-plugins/src/manager_tests.rs b/codex-rs/core-plugins/src/manager_tests.rs index c3961796c..a0177ebf1 100644 --- a/codex-rs/core-plugins/src/manager_tests.rs +++ b/codex-rs/core-plugins/src/manager_tests.rs @@ -1815,7 +1815,11 @@ enabled = false let config = load_config(tmp.path(), &repo_root).await; let marketplaces = PluginsManager::new(tmp.path().to_path_buf()) - .list_marketplaces_for_config(&config, &[AbsolutePathBuf::try_from(repo_root).unwrap()]) + .list_marketplaces_for_config( + &config, + &[AbsolutePathBuf::try_from(repo_root).unwrap()], + /*include_openai_curated*/ true, + ) .unwrap() .marketplaces; @@ -1917,7 +1921,11 @@ enabled = true let config = load_config(tmp.path(), &repo_root).await; let marketplaces = PluginsManager::new(tmp.path().to_path_buf()) - .list_marketplaces_for_config(&config, &[AbsolutePathBuf::try_from(repo_root).unwrap()]) + .list_marketplaces_for_config( + &config, + &[AbsolutePathBuf::try_from(repo_root).unwrap()], + /*include_openai_curated*/ true, + ) .unwrap() .marketplaces; @@ -1965,7 +1973,11 @@ plugins = true let config = load_config(tmp.path(), &repo_root).await; let marketplaces = PluginsManager::new(tmp.path().to_path_buf()) - .list_marketplaces_for_config(&config, &[AbsolutePathBuf::try_from(repo_root).unwrap()]) + .list_marketplaces_for_config( + &config, + &[AbsolutePathBuf::try_from(repo_root).unwrap()], + /*include_openai_curated*/ true, + ) .unwrap() .marketplaces; @@ -2402,7 +2414,11 @@ enabled = true let config = load_config(tmp.path(), &repo_root).await; let marketplaces = PluginsManager::new(tmp.path().to_path_buf()) - .list_marketplaces_for_config(&config, &[AbsolutePathBuf::try_from(repo_root).unwrap()]) + .list_marketplaces_for_config( + &config, + &[AbsolutePathBuf::try_from(repo_root).unwrap()], + /*include_openai_curated*/ true, + ) .unwrap() .marketplaces; @@ -2496,7 +2512,7 @@ plugins = true let config = load_config(tmp.path(), tmp.path()).await; let marketplaces = PluginsManager::new(tmp.path().to_path_buf()) - .list_marketplaces_for_config(&config, &[]) + .list_marketplaces_for_config(&config, &[], /*include_openai_curated*/ true) .unwrap() .marketplaces; @@ -2534,6 +2550,37 @@ plugins = true ); } +#[tokio::test] +async fn list_marketplaces_can_skip_openai_curated_before_loading() { + let tmp = tempfile::tempdir().unwrap(); + let curated_root = curated_plugins_repo_path(tmp.path()); + + write_file( + &tmp.path().join(CONFIG_TOML_FILE), + r#"[features] +plugins = true +"#, + ); + write_file( + &curated_root.join(".agents/plugins/marketplace.json"), + "{not valid json", + ); + + let config = load_config(tmp.path(), tmp.path()).await; + let outcome = PluginsManager::new(tmp.path().to_path_buf()) + .list_marketplaces_for_config(&config, &[], /*include_openai_curated*/ false) + .unwrap(); + + assert_eq!(outcome.errors, Vec::new()); + assert_eq!( + outcome + .marketplaces + .iter() + .any(|marketplace| marketplace.name == OPENAI_CURATED_MARKETPLACE_NAME), + false + ); +} + #[tokio::test] async fn list_marketplaces_includes_installed_marketplace_roots() { let tmp = tempfile::tempdir().unwrap(); @@ -2576,7 +2623,7 @@ source = "/tmp/debug" .unwrap(); let config = load_config(tmp.path(), tmp.path()).await; let marketplaces = PluginsManager::new(tmp.path().to_path_buf()) - .list_marketplaces_for_config(&config, &[]) + .list_marketplaces_for_config(&config, &[], /*include_openai_curated*/ true) .unwrap() .marketplaces; @@ -2652,7 +2699,7 @@ source = "/tmp/debug" let config = load_config(tmp.path(), tmp.path()).await; let marketplaces = PluginsManager::new(tmp.path().to_path_buf()) - .list_marketplaces_for_config(&config, &[]) + .list_marketplaces_for_config(&config, &[], /*include_openai_curated*/ true) .unwrap() .marketplaces; @@ -2707,7 +2754,7 @@ plugins = true .unwrap(); let config = load_config(tmp.path(), tmp.path()).await; let marketplaces = PluginsManager::new(tmp.path().to_path_buf()) - .list_marketplaces_for_config(&config, &[]) + .list_marketplaces_for_config(&config, &[], /*include_openai_curated*/ true) .unwrap() .marketplaces; @@ -2792,6 +2839,7 @@ enabled = false AbsolutePathBuf::try_from(repo_a_root).unwrap(), AbsolutePathBuf::try_from(repo_b_root).unwrap(), ], + /*include_openai_curated*/ true, ) .unwrap() .marketplaces; @@ -2902,7 +2950,11 @@ enabled = true let config = load_config(tmp.path(), &repo_root).await; let marketplaces = PluginsManager::new(tmp.path().to_path_buf()) - .list_marketplaces_for_config(&config, &[AbsolutePathBuf::try_from(repo_root).unwrap()]) + .list_marketplaces_for_config( + &config, + &[AbsolutePathBuf::try_from(repo_root).unwrap()], + /*include_openai_curated*/ true, + ) .unwrap() .marketplaces; diff --git a/codex-rs/core/src/tools/handlers/request_plugin_install.rs b/codex-rs/core/src/tools/handlers/request_plugin_install.rs index 801031348..55906185a 100644 --- a/codex-rs/core/src/tools/handlers/request_plugin_install.rs +++ b/codex-rs/core/src/tools/handlers/request_plugin_install.rs @@ -359,7 +359,7 @@ fn verified_plugin_install_completed( ) -> bool { let plugins_input = config.plugins_config_input(); plugins_manager - .list_marketplaces_for_config(&plugins_input, &[]) + .list_marketplaces_for_config(&plugins_input, &[], /*include_openai_curated*/ true) .ok() .into_iter() .flat_map(|outcome| outcome.marketplaces)