mirror of
https://github.com/openai/codex.git
synced 2026-05-28 23:10:20 +00:00
Expose plugin manifest keywords in app server (#21271)
## Summary - Add plugin manifest keywords to core plugin marketplace/detail models - Expose keywords on app-server v2 PluginSummary and generated schema/types - Populate keywords in plugin/list and plugin/read responses for local plugins Depends on https://github.com/openai/openai/pull/891087 ## Validation - just fmt - just write-app-server-schema - cargo test -p codex-app-server-protocol - cargo test -p codex-core-plugins - cargo test -p codex-app-server plugin_list_keeps_valid_marketplaces_when_another_marketplace_fails_to_load - cargo test -p codex-app-server plugin_read_returns_plugin_details_with_bundle_contents
This commit is contained in:
committed by
GitHub
parent
136e442e95
commit
94db03d5af
@@ -223,6 +223,7 @@ pub struct PluginDetail {
|
||||
pub source: MarketplacePluginSource,
|
||||
pub policy: MarketplacePluginPolicy,
|
||||
pub interface: Option<PluginManifestInterface>,
|
||||
pub keywords: Vec<String>,
|
||||
pub installed: bool,
|
||||
pub enabled: bool,
|
||||
pub skills: Vec<SkillMetadata>,
|
||||
@@ -252,6 +253,7 @@ pub struct ConfiguredMarketplacePlugin {
|
||||
pub source: MarketplacePluginSource,
|
||||
pub policy: MarketplacePluginPolicy,
|
||||
pub interface: Option<PluginManifestInterface>,
|
||||
pub keywords: Vec<String>,
|
||||
pub installed: bool,
|
||||
pub enabled: bool,
|
||||
}
|
||||
@@ -1196,6 +1198,7 @@ impl PluginsManager {
|
||||
source: plugin.source,
|
||||
policy: plugin.policy,
|
||||
interface: plugin.interface,
|
||||
keywords: plugin.keywords,
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@@ -1245,6 +1248,11 @@ impl PluginsManager {
|
||||
source: plugin.source,
|
||||
policy: plugin.policy,
|
||||
interface: plugin.interface,
|
||||
keywords: plugin
|
||||
.manifest
|
||||
.as_ref()
|
||||
.map(|manifest| manifest.keywords.clone())
|
||||
.unwrap_or_default(),
|
||||
installed: installed_plugins.contains(&plugin_key),
|
||||
enabled: enabled_plugins.contains(&plugin_key),
|
||||
},
|
||||
@@ -1287,6 +1295,7 @@ impl PluginsManager {
|
||||
source: plugin.source,
|
||||
policy: plugin.policy,
|
||||
interface: plugin.interface,
|
||||
keywords: plugin.keywords,
|
||||
installed: plugin.installed,
|
||||
enabled: plugin.enabled,
|
||||
skills: Vec::new(),
|
||||
@@ -1363,6 +1372,7 @@ impl PluginsManager {
|
||||
source: plugin.source,
|
||||
policy: plugin.policy,
|
||||
interface,
|
||||
keywords: manifest.keywords,
|
||||
installed: plugin.installed,
|
||||
enabled: plugin.enabled,
|
||||
skills: resolved_skills.skills,
|
||||
|
||||
@@ -1550,6 +1550,7 @@ enabled = false
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
installed: true,
|
||||
enabled: true,
|
||||
},
|
||||
@@ -1566,6 +1567,7 @@ enabled = false
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
installed: true,
|
||||
enabled: false,
|
||||
},
|
||||
@@ -1684,6 +1686,7 @@ plugins = true
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
installed: false,
|
||||
enabled: false,
|
||||
}]
|
||||
@@ -2068,6 +2071,7 @@ plugins = true
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
installed: false,
|
||||
enabled: false,
|
||||
}],
|
||||
@@ -2361,6 +2365,7 @@ enabled = false
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
installed: false,
|
||||
enabled: true,
|
||||
}]
|
||||
@@ -2390,6 +2395,7 @@ enabled = false
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
installed: false,
|
||||
enabled: false,
|
||||
}]
|
||||
@@ -2473,6 +2479,7 @@ enabled = true
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
installed: false,
|
||||
enabled: true,
|
||||
}],
|
||||
|
||||
@@ -18,6 +18,8 @@ struct RawPluginManifest {
|
||||
version: Option<String>,
|
||||
#[serde(default)]
|
||||
description: Option<String>,
|
||||
#[serde(default)]
|
||||
keywords: Vec<String>,
|
||||
// Keep manifest paths as raw strings so we can validate the required `./...` syntax before
|
||||
// resolving them under the plugin root.
|
||||
#[serde(default)]
|
||||
@@ -37,6 +39,7 @@ pub struct PluginManifest {
|
||||
pub name: String,
|
||||
pub version: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub keywords: Vec<String>,
|
||||
pub paths: PluginManifestPaths,
|
||||
pub interface: Option<PluginManifestInterface>,
|
||||
}
|
||||
@@ -143,6 +146,7 @@ pub fn load_plugin_manifest(plugin_root: &Path) -> Option<PluginManifest> {
|
||||
name: raw_name,
|
||||
version,
|
||||
description,
|
||||
keywords,
|
||||
skills,
|
||||
mcp_servers,
|
||||
apps,
|
||||
@@ -232,6 +236,7 @@ pub fn load_plugin_manifest(plugin_root: &Path) -> Option<PluginManifest> {
|
||||
name,
|
||||
version,
|
||||
description,
|
||||
keywords,
|
||||
paths: PluginManifestPaths {
|
||||
skills: resolve_manifest_path(plugin_root, "skills", skills.as_deref()),
|
||||
mcp_servers: resolve_manifest_path(
|
||||
@@ -568,6 +573,28 @@ mod tests {
|
||||
assert_eq!(manifest.version, Some("1.2.3-beta+7".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_manifest_reads_keywords() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let plugin_root = tmp.path().join("demo-plugin");
|
||||
fs::create_dir_all(plugin_root.join(".codex-plugin")).expect("create manifest dir");
|
||||
fs::write(
|
||||
plugin_root.join(".codex-plugin/plugin.json"),
|
||||
r#"{
|
||||
"name": "demo-plugin",
|
||||
"keywords": ["api-key", "developer tools"]
|
||||
}"#,
|
||||
)
|
||||
.expect("write manifest");
|
||||
|
||||
let manifest = load_manifest(&plugin_root);
|
||||
|
||||
assert_eq!(
|
||||
manifest.keywords,
|
||||
vec!["api-key".to_string(), "developer tools".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_manifest_uses_alternate_discoverable_path() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
|
||||
@@ -62,6 +62,7 @@ pub struct MarketplacePlugin {
|
||||
pub source: MarketplacePluginSource,
|
||||
pub policy: MarketplacePluginPolicy,
|
||||
pub interface: Option<PluginManifestInterface>,
|
||||
pub keywords: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -293,6 +294,10 @@ pub fn load_marketplace(path: &AbsolutePathBuf) -> Result<Marketplace, Marketpla
|
||||
source: plugin.source,
|
||||
policy: plugin.policy,
|
||||
interface: plugin.interface,
|
||||
keywords: plugin
|
||||
.manifest
|
||||
.map(|manifest| manifest.keywords)
|
||||
.unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -413,6 +413,7 @@ fn list_marketplaces_supports_alternate_manifest_layout() {
|
||||
logo: None,
|
||||
screenshots: Vec::new(),
|
||||
}),
|
||||
keywords: Vec::new(),
|
||||
}],
|
||||
}]
|
||||
);
|
||||
@@ -462,6 +463,7 @@ fn list_marketplaces_includes_plugins_without_discoverable_manifest() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
}],
|
||||
}]
|
||||
);
|
||||
@@ -602,6 +604,7 @@ fn list_marketplaces_returns_home_and_repo_marketplaces() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
},
|
||||
MarketplacePlugin {
|
||||
name: "home-only".to_string(),
|
||||
@@ -614,6 +617,7 @@ fn list_marketplaces_returns_home_and_repo_marketplaces() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -635,6 +639,7 @@ fn list_marketplaces_returns_home_and_repo_marketplaces() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
},
|
||||
MarketplacePlugin {
|
||||
name: "repo-only".to_string(),
|
||||
@@ -647,6 +652,7 @@ fn list_marketplaces_returns_home_and_repo_marketplaces() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -724,6 +730,7 @@ fn list_marketplaces_keeps_distinct_entries_for_same_name() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
}],
|
||||
},
|
||||
Marketplace {
|
||||
@@ -741,6 +748,7 @@ fn list_marketplaces_keeps_distinct_entries_for_same_name() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
}],
|
||||
},
|
||||
]
|
||||
@@ -814,6 +822,7 @@ fn list_marketplaces_dedupes_multiple_roots_in_same_repo() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
}],
|
||||
}]
|
||||
);
|
||||
@@ -976,6 +985,7 @@ fn list_marketplaces_skips_plugins_with_invalid_names_but_keeps_marketplace() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
}],
|
||||
}]
|
||||
);
|
||||
@@ -1093,6 +1103,7 @@ fn list_marketplaces_keeps_remote_and_local_plugin_sources() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
},
|
||||
MarketplacePlugin {
|
||||
name: "url-plugin".to_string(),
|
||||
@@ -1108,6 +1119,7 @@ fn list_marketplaces_keeps_remote_and_local_plugin_sources() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
},
|
||||
MarketplacePlugin {
|
||||
name: "git-subdir-plugin".to_string(),
|
||||
@@ -1123,6 +1135,7 @@ fn list_marketplaces_keeps_remote_and_local_plugin_sources() {
|
||||
products: None,
|
||||
},
|
||||
interface: None,
|
||||
keywords: Vec::new(),
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
@@ -74,6 +74,7 @@ pub struct RemotePluginSummary {
|
||||
pub auth_policy: PluginAuthPolicy,
|
||||
pub availability: PluginAvailability,
|
||||
pub interface: Option<PluginInterface>,
|
||||
pub keywords: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -321,6 +322,8 @@ struct RemotePluginReleaseResponse {
|
||||
bundle_download_url: Option<String>,
|
||||
#[serde(default)]
|
||||
app_ids: Vec<String>,
|
||||
#[serde(default)]
|
||||
keywords: Vec<String>,
|
||||
interface: RemotePluginReleaseInterfaceResponse,
|
||||
#[serde(default)]
|
||||
skills: Vec<RemotePluginSkillResponse>,
|
||||
@@ -771,6 +774,7 @@ fn build_remote_plugin_summary(
|
||||
auth_policy: plugin.authentication_policy,
|
||||
availability: plugin.availability,
|
||||
interface: remote_plugin_interface_to_info(plugin),
|
||||
keywords: plugin.release.keywords.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -442,6 +442,7 @@ async fn list_remote_plugin_shares_fetches_created_workspace_plugins() {
|
||||
auth_policy: PluginAuthPolicy::OnUse,
|
||||
availability: PluginAvailability::Available,
|
||||
interface: Some(expected_plugin_interface()),
|
||||
keywords: Vec::new(),
|
||||
},
|
||||
share_url: Some("https://chatgpt.example/plugins/share/share-key-1".to_string()),
|
||||
local_plugin_path: Some(local_plugin_path),
|
||||
@@ -456,6 +457,7 @@ async fn list_remote_plugin_shares_fetches_created_workspace_plugins() {
|
||||
auth_policy: PluginAuthPolicy::OnUse,
|
||||
availability: PluginAvailability::Available,
|
||||
interface: Some(expected_plugin_interface()),
|
||||
keywords: Vec::new(),
|
||||
},
|
||||
share_url: None,
|
||||
local_plugin_path: None,
|
||||
|
||||
Reference in New Issue
Block a user