[codex] Support marketplace plugin manifest fallback (#28789)

## Summary

Support marketplace plugins whose source directory does not include a
discoverable plugin manifest. Metadata-rich `marketplace.json` entries
now act as fallback plugin manifests for listing, local detail reads,
install, and non-curated cache refresh.

The fallback preserves marketplace-entry plugin fields wholesale, then
adds the small Codex-facing compatibility bridge for presentation
metadata. A real source `plugin.json` always wins when present.

## Details

- Capture flattened marketplace-entry fields into
`MarketplacePluginManifestFallback`, preserving fields such as
`version`, `description`, `skills`, `mcpServers`, `apps`, `hooks`,
`agents`, `commands`, `strict`, `author`, and future manifest fields
without a per-field translation list.
- Bridge Claude-style top-level `displayName`, `author.name`,
`homepage`, and marketplace `category` into Codex's nested `interface`
fields only when the nested values are absent.
- Treat fallback metadata as installable only when the marketplace entry
contributes metadata beyond bare `name` and `source`; existing
missing-manifest behavior remains for metadata-free entries.
- Read local plugin details from the already parsed fallback manifest,
including fallback-declared app and MCP paths, instead of rereading only
an on-disk manifest.
- Pass fallback contents into `PluginStore`, which validates them and
injects `.codex-plugin/plugin.json` into Store's existing atomic copy.
Local marketplace source directories are never mutated, and the fallback
path no longer needs an additional staging directory.
- Keep Git source materialization unchanged; Git clones still use the
existing marketplace source staging area before Store installation.
This commit is contained in:
charlesgong-openai
2026-06-18 15:49:27 -07:00
committed by GitHub
parent dce673905a
commit 772c5c5195
7 changed files with 897 additions and 47 deletions
+3 -2
View File
@@ -192,7 +192,7 @@ fn install_with_version_uses_requested_cache_version() {
}
#[test]
fn install_uses_manifest_version_when_present() {
fn install_prefers_on_disk_manifest_version_over_fallback() {
let tmp = tempdir().unwrap();
write_plugin_with_version(
tmp.path(),
@@ -203,9 +203,10 @@ fn install_uses_manifest_version_when_present() {
let plugin_id = PluginId::new("sample-plugin".to_string(), "debug".to_string()).unwrap();
let result = PluginStore::new(tmp.path().to_path_buf())
.install(
.install_with_fallback_manifest(
AbsolutePathBuf::try_from(tmp.path().join("sample-plugin")).unwrap(),
plugin_id.clone(),
r#"{"name":"sample-plugin","version":"9.9.9"}"#,
)
.unwrap();