feat: Handle alternate plugin manifest paths (#18182)

Load plugin manifests through a shared discoverable-path helper so
manifest reads, installs, and skill names all see the same alternate
manifest location.
This commit is contained in:
xl-openai
2026-04-16 19:43:19 -07:00
committed by GitHub
Unverified
parent a803790a10
commit 37161bc76e
13 changed files with 581 additions and 222 deletions
+27 -44
View File
@@ -35,9 +35,10 @@ use codex_core_plugins::marketplace::MarketplacePluginAuthPolicy;
use codex_core_plugins::marketplace::MarketplacePluginPolicy;
use codex_core_plugins::marketplace::MarketplacePluginSource;
use codex_core_plugins::marketplace::ResolvedMarketplacePlugin;
use codex_core_plugins::marketplace::find_installable_marketplace_plugin;
use codex_core_plugins::marketplace::find_marketplace_plugin;
use codex_core_plugins::marketplace::list_marketplaces;
use codex_core_plugins::marketplace::load_marketplace;
use codex_core_plugins::marketplace::resolve_marketplace_plugin;
use codex_core_plugins::marketplace_upgrade::ConfiguredMarketplaceUpgradeError;
use codex_core_plugins::marketplace_upgrade::ConfiguredMarketplaceUpgradeOutcome;
use codex_core_plugins::marketplace_upgrade::configured_git_marketplace_names;
@@ -535,7 +536,7 @@ impl PluginsManager {
&self,
request: PluginInstallRequest,
) -> Result<PluginInstallOutcome, PluginInstallError> {
let resolved = resolve_marketplace_plugin(
let resolved = find_installable_marketplace_plugin(
&request.marketplace_path,
&request.plugin_name,
self.restriction_product,
@@ -549,7 +550,7 @@ impl PluginsManager {
auth: Option<&CodexAuth>,
request: PluginInstallRequest,
) -> Result<PluginInstallOutcome, PluginInstallError> {
let resolved = resolve_marketplace_plugin(
let resolved = find_installable_marketplace_plugin(
&request.marketplace_path,
&request.plugin_name,
self.restriction_product,
@@ -572,7 +573,7 @@ impl PluginsManager {
&self,
resolved: ResolvedMarketplacePlugin,
) -> Result<PluginInstallOutcome, PluginInstallError> {
let auth_policy = resolved.auth_policy;
let auth_policy = resolved.policy.authentication;
let plugin_version =
if resolved.plugin_id.marketplace_name == OPENAI_CURATED_MARKETPLACE_NAME {
Some(
@@ -587,10 +588,11 @@ impl PluginsManager {
};
let store = self.store.clone();
let result: StorePluginInstallResult = tokio::task::spawn_blocking(move || {
let MarketplacePluginSource::Local { path: source_path } = resolved.source;
if let Some(plugin_version) = plugin_version {
store.install_with_version(resolved.source_path, resolved.plugin_id, plugin_version)
store.install_with_version(source_path, resolved.plugin_id, plugin_version)
} else {
store.install(resolved.source_path, resolved.plugin_id)
store.install(source_path, resolved.plugin_id)
}
})
.await
@@ -978,32 +980,24 @@ impl PluginsManager {
return Err(MarketplaceError::PluginsDisabled);
}
let marketplace = load_marketplace(&request.marketplace_path)?;
let marketplace_name = marketplace.name.clone();
let plugin = marketplace
.plugins
.into_iter()
.find(|plugin| plugin.name == request.plugin_name);
let Some(plugin) = plugin else {
let plugin = find_marketplace_plugin(&request.marketplace_path, &request.plugin_name)?;
if !self.restriction_product_matches(plugin.policy.products.as_deref()) {
return Err(MarketplaceError::PluginNotFound {
plugin_name: request.plugin_name.clone(),
marketplace_name,
plugin_name: plugin.plugin_id.plugin_name,
marketplace_name: plugin.plugin_id.marketplace_name,
});
};
let plugin_id = PluginId::new(plugin.name.clone(), marketplace.name.clone()).map_err(
|err| match err {
PluginIdError::Invalid(message) => MarketplaceError::InvalidPlugin(message),
},
)?;
let plugin_key = plugin_id.as_key();
}
let marketplace_name = plugin.plugin_id.marketplace_name.clone();
let plugin_key = plugin.plugin_id.as_key();
let (installed_plugins, enabled_plugins) = self.configured_plugin_states(config);
let plugin = self
.read_plugin_detail_for_marketplace_plugin(
config,
&marketplace.name,
&marketplace_name,
ConfiguredMarketplacePlugin {
id: plugin_key.clone(),
name: plugin.name,
name: plugin.plugin_id.plugin_name,
source: plugin.source,
policy: plugin.policy,
interface: plugin.interface,
@@ -1014,12 +1008,12 @@ impl PluginsManager {
.await?;
Ok(PluginReadOutcome {
marketplace_name: if marketplace.name == OPENAI_CURATED_MARKETPLACE_NAME {
marketplace_name: if marketplace_name == OPENAI_CURATED_MARKETPLACE_NAME {
OPENAI_CURATED_MARKETPLACE_DISPLAY_NAME.to_string()
} else {
marketplace.name
marketplace_name
},
marketplace_path: marketplace.path,
marketplace_path: request.marketplace_path.clone(),
plugin,
})
}
@@ -1037,13 +1031,6 @@ impl PluginsManager {
});
}
let plugin_id =
PluginId::new(plugin.name.clone(), marketplace_name.to_string()).map_err(|err| {
match err {
PluginIdError::Invalid(message) => MarketplaceError::InvalidPlugin(message),
}
})?;
let plugin_key = plugin_id.as_key();
let source_path = match &plugin.source {
MarketplacePluginSource::Local { path } => path.clone(),
};
@@ -1053,20 +1040,16 @@ impl PluginsManager {
));
}
let manifest = load_plugin_manifest(source_path.as_path()).ok_or_else(|| {
MarketplaceError::InvalidPlugin(
"missing or invalid .codex-plugin/plugin.json".to_string(),
)
MarketplaceError::InvalidPlugin("missing or invalid plugin.json".to_string())
})?;
let description = manifest.description.clone();
let manifest_paths = &manifest.paths;
let skill_config_rules = codex_core_skills::config_rules::skill_config_rules_from_stack(
&config.config_layer_stack,
);
let resolved_skills = load_plugin_skills(
&source_path,
manifest_paths,
&manifest.paths,
self.restriction_product,
&skill_config_rules,
&codex_core_skills::config_rules::skill_config_rules_from_stack(
&config.config_layer_stack,
),
)
.await;
let apps = load_plugin_apps(source_path.as_path()).await;
@@ -1078,7 +1061,7 @@ impl PluginsManager {
mcp_server_names.dedup();
Ok(PluginDetail {
id: plugin_key,
id: plugin.id,
name: plugin.name,
description,
source: plugin.source,
+24 -3
View File
@@ -1578,7 +1578,13 @@ source = "/tmp/debug"
let marketplace = marketplaces
.into_iter()
.find(|marketplace| marketplace.name == "debug")
.find(|marketplace| {
marketplace.path
== AbsolutePathBuf::try_from(
marketplace_root.join(".agents/plugins/marketplace.json"),
)
.unwrap()
})
.expect("installed marketplace should be listed");
assert_eq!(
@@ -1648,7 +1654,13 @@ source = "/tmp/debug"
let marketplace = marketplaces
.into_iter()
.find(|marketplace| marketplace.name == "debug")
.find(|marketplace| {
marketplace.path
== AbsolutePathBuf::try_from(
marketplace_root.join(".agents/plugins/marketplace.json"),
)
.unwrap()
})
.expect("configured marketplace should be discovered");
assert_eq!(marketplace.plugins[0].id, "sample@debug");
@@ -1695,7 +1707,16 @@ plugins = true
.unwrap()
.marketplaces;
assert!(marketplaces.is_empty());
assert!(
marketplaces.iter().all(|marketplace| {
marketplace.path
!= AbsolutePathBuf::try_from(
marketplace_root.join(".agents/plugins/marketplace.json"),
)
.unwrap()
}),
"installed marketplace root missing from config should not be listed"
);
}
#[tokio::test]