mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] Gate remote plugin catalog by auth (#28625)
## Summary - Treat the remote global plugin catalog as active only when `remote_plugin` is enabled and the current auth uses the Codex backend. - Skip the local OpenAI curated marketplace for remote-enabled ChatGPT users while preserving configured marketplaces. - Keep the local curated marketplace for API-key users, unauthenticated fallback, and ChatGPT users with `remote_plugin` disabled. - Apply the same effective-remote gate to the remote installed-marketplace cache. ## Root cause The tool-suggestion discovery path unconditionally included the local OpenAI curated marketplace. For remote-enabled ChatGPT users, that made remote discovery additive: Codex parsed every local curated `plugin.json` before also loading the remote catalog. ## Validation - `just fmt` - `cargo build -p codex-cli --bin codex` - Targeted auth/feature matrix tests pass, including API-key auth with `remote_plugin` enabled. - Manual CLI validation confirmed: - ChatGPT + remote off includes local curated. - ChatGPT + remote on excludes local curated. - API-key auth keeps local curated when remote is enabled. - `just test -p codex-core-plugins`: 235 passed; one unrelated existing marketplace test failed because it loaded the developer's home marketplace configuration.
This commit is contained in:
committed by
GitHub
Unverified
parent
bfe90188ad
commit
69bc0645ac
@@ -76,11 +76,17 @@ impl PluginsManager {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let use_remote_global_catalog =
|
||||
input.plugins.remote_plugin_enabled && auth.is_some_and(CodexAuth::uses_codex_backend);
|
||||
let marketplaces = self
|
||||
.list_marketplaces_for_config(&input.plugins, &[], /*include_openai_curated*/ true)
|
||||
.list_marketplaces_for_config(
|
||||
&input.plugins,
|
||||
&[],
|
||||
/*include_openai_curated*/ !use_remote_global_catalog,
|
||||
)
|
||||
.context("failed to list plugin marketplaces for tool suggestions")?
|
||||
.marketplaces;
|
||||
let remote_installed_marketplaces = if input.plugins.remote_plugin_enabled {
|
||||
let remote_installed_marketplaces = if use_remote_global_catalog {
|
||||
self.build_remote_installed_plugin_marketplaces_from_cache(&[
|
||||
REMOTE_GLOBAL_MARKETPLACE_NAME,
|
||||
])
|
||||
|
||||
@@ -15,6 +15,7 @@ use crate::test_support::write_curated_plugin_sha_with;
|
||||
use crate::test_support::write_file;
|
||||
use crate::test_support::write_openai_api_curated_marketplace;
|
||||
use crate::test_support::write_openai_curated_marketplace;
|
||||
use codex_app_server_protocol::AuthMode;
|
||||
use codex_config::CONFIG_TOML_FILE;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
@@ -34,17 +35,19 @@ use wiremock::matchers::path;
|
||||
use wiremock::matchers::query_param;
|
||||
|
||||
#[tokio::test]
|
||||
async fn returns_fallback_plugins_without_installed_apps() {
|
||||
async fn returns_fallback_plugins_when_remote_disabled_for_codex_auth() {
|
||||
let codex_home = tempdir().expect("tempdir should succeed");
|
||||
let curated_root = curated_plugins_repo_path(codex_home.path());
|
||||
write_openai_curated_marketplace(&curated_root, &["sample", "slack", "openai-developers"]);
|
||||
|
||||
let plugins = load_plugins_config(codex_home.path(), codex_home.path()).await;
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
plugins_manager.set_auth_mode(Some(AuthMode::Chatgpt));
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let discoverable_plugins = list_discoverable_plugins(
|
||||
&plugins_manager,
|
||||
discovery_input(plugins, &[], &[], &[]),
|
||||
/*auth*/ None,
|
||||
Some(&auth),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -66,8 +69,10 @@ async fn returns_api_curated_fallback_plugins_for_direct_provider_auth() {
|
||||
let curated_root = curated_plugins_repo_path(codex_home.path());
|
||||
write_openai_api_curated_marketplace(&curated_root, &["sample", "slack", "openai-developers"]);
|
||||
|
||||
let plugins = load_plugins_config(codex_home.path(), codex_home.path()).await;
|
||||
let mut plugins = load_plugins_config(codex_home.path(), codex_home.path()).await;
|
||||
plugins.remote_plugin_enabled = true;
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
plugins_manager.set_auth_mode(Some(AuthMode::ApiKey));
|
||||
let auth = CodexAuth::from_api_key("test-api-key");
|
||||
let discoverable_plugins = list_discoverable_plugins(
|
||||
&plugins_manager,
|
||||
@@ -121,7 +126,7 @@ async fn returns_microsoft_fallback_plugins() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn includes_openai_curated_when_remote_enabled() {
|
||||
async fn omits_openai_curated_but_keeps_configured_marketplaces_for_remote_codex_auth() {
|
||||
let codex_home = tempdir().expect("tempdir should succeed");
|
||||
let curated_root = curated_plugins_repo_path(codex_home.path());
|
||||
write_openai_curated_marketplace(&curated_root, &["slack"]);
|
||||
@@ -159,6 +164,33 @@ source = "/tmp/{bundled_marketplace_name}"
|
||||
|
||||
let plugins = load_plugins_config(codex_home.path(), codex_home.path()).await;
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
plugins_manager.set_auth_mode(Some(AuthMode::Chatgpt));
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let discoverable_plugins = list_discoverable_plugins(
|
||||
&plugins_manager,
|
||||
discovery_input(plugins, &[], &[], &[]),
|
||||
Some(&auth),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
discoverable_plugins
|
||||
.into_iter()
|
||||
.map(|plugin| plugin.id)
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["chrome@openai-bundled".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn includes_openai_curated_when_remote_enabled_without_auth() {
|
||||
let codex_home = tempdir().expect("tempdir should succeed");
|
||||
let curated_root = curated_plugins_repo_path(codex_home.path());
|
||||
write_openai_curated_marketplace(&curated_root, &["slack"]);
|
||||
|
||||
let mut plugins = load_plugins_config(codex_home.path(), codex_home.path()).await;
|
||||
plugins.remote_plugin_enabled = true;
|
||||
let plugins_manager = PluginsManager::new(codex_home.path().to_path_buf());
|
||||
let discoverable_plugins = list_discoverable_plugins(
|
||||
&plugins_manager,
|
||||
discovery_input(plugins, &[], &[], &[]),
|
||||
@@ -171,10 +203,7 @@ source = "/tmp/{bundled_marketplace_name}"
|
||||
.into_iter()
|
||||
.map(|plugin| plugin.id)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
"chrome@openai-bundled".to_string(),
|
||||
"slack@openai-curated".to_string(),
|
||||
]
|
||||
vec!["slack@openai-curated".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user