Read cached metadata for installed Git plugins (#20825)

## Summary
- Populate `plugin/list` interface metadata for installed Git-sourced
marketplace plugins from the active cached plugin bundle.
- Preserve marketplace category precedence so list behavior matches
`plugin/read`.
- Keep existing fallback behavior when the cache or manifest is missing
or invalid.

## Test Plan
- `cd codex-rs && just fmt`
- `cd codex-rs && cargo test -p codex-core-plugins
list_marketplaces_installed_git_source_reads_metadata_from_cache_without_cloning`
- `cd codex-rs && cargo test -p codex-app-server
plugin_list_returns_installed_git_source_interface_from_cache`
- `cd codex-rs && just fix -p codex-core-plugins`
- `cd codex-rs && just fix -p codex-app-server`
- `git diff --check`

Server-truth check: OpenAI monorepo app-server generated types already
expose `PluginSummary.interface`, and the webview consumes it for plugin
cards. This PR keeps the protocol/schema unchanged and fills the
existing field from the cached installed bundle for Git-backed
cross-repo plugins.
This commit is contained in:
xli-oai
2026-05-10 16:59:57 -07:00
committed by GitHub
Unverified
parent 5248e3da2b
commit 2abdeb34d5
3 changed files with 250 additions and 4 deletions
+22 -4
View File
@@ -1196,19 +1196,37 @@ impl PluginsManager {
if !self.restriction_product_matches(plugin.policy.products.as_deref()) {
return None;
}
let installed = installed_plugins.contains(&plugin_key);
let enabled = enabled_plugins.contains(&plugin_key);
let mut interface = plugin.interface;
if installed
&& matches!(&plugin.source, MarketplacePluginSource::Git { .. })
&& let Ok(plugin_id) =
PluginId::new(plugin.name.clone(), marketplace_name.clone())
&& let Some(plugin_root) = self.store.active_plugin_root(&plugin_id)
&& let Some(manifest) = load_plugin_manifest(plugin_root.as_path())
{
let marketplace_category = interface
.as_ref()
.and_then(|interface| interface.category.clone());
interface = plugin_interface_with_marketplace_category(
manifest.interface,
marketplace_category,
);
}
Some(ConfiguredMarketplacePlugin {
// Enabled state is keyed by `<plugin>@<marketplace>`, so duplicate
// plugin entries from duplicate marketplace files intentionally
// resolve to the first discovered source.
id: plugin_key.clone(),
installed: installed_plugins.contains(&plugin_key),
enabled: enabled_plugins.contains(&plugin_key),
id: plugin_key,
installed,
enabled,
name: plugin.name,
source: plugin.source,
policy: plugin.policy,
interface: plugin.interface,
keywords: plugin.keywords,
interface,
})
})
.collect::<Vec<_>>();