mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat: Expose plugin versions and gate plugin sharing (#22397)
- Adds localVersion to plugin summaries and remoteVersion to share
context, including generated API schemas.
- Hydrates local and remote plugin versions from manifests and remote
release metadata.
- Adds default-on plugin_sharing gate for shared-with-me listing and
plugin/share/save, with disabled-path errors
and focused coverage.
This commit is contained in:
@@ -222,6 +222,7 @@ pub struct PluginReadOutcome {
|
||||
pub struct PluginDetail {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub local_version: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub source: MarketplacePluginSource,
|
||||
pub policy: MarketplacePluginPolicy,
|
||||
@@ -260,6 +261,7 @@ pub struct ConfiguredMarketplace {
|
||||
pub struct ConfiguredMarketplacePlugin {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub local_version: Option<String>,
|
||||
pub source: MarketplacePluginSource,
|
||||
pub policy: MarketplacePluginPolicy,
|
||||
pub interface: Option<PluginManifestInterface>,
|
||||
@@ -1199,6 +1201,7 @@ impl PluginsManager {
|
||||
let installed = installed_plugins.contains(&plugin_key);
|
||||
let enabled = enabled_plugins.contains(&plugin_key);
|
||||
let mut interface = plugin.interface;
|
||||
let mut local_version = plugin.local_version;
|
||||
if installed
|
||||
&& matches!(&plugin.source, MarketplacePluginSource::Git { .. })
|
||||
&& let Ok(plugin_id) =
|
||||
@@ -1206,6 +1209,7 @@ impl PluginsManager {
|
||||
&& let Some(plugin_root) = self.store.active_plugin_root(&plugin_id)
|
||||
&& let Some(manifest) = load_plugin_manifest(plugin_root.as_path())
|
||||
{
|
||||
local_version = manifest.version.clone();
|
||||
let marketplace_category = interface
|
||||
.as_ref()
|
||||
.and_then(|interface| interface.category.clone());
|
||||
@@ -1223,6 +1227,7 @@ impl PluginsManager {
|
||||
installed,
|
||||
enabled,
|
||||
name: plugin.name,
|
||||
local_version,
|
||||
source: plugin.source,
|
||||
policy: plugin.policy,
|
||||
keywords: plugin.keywords,
|
||||
@@ -1273,6 +1278,10 @@ impl PluginsManager {
|
||||
ConfiguredMarketplacePlugin {
|
||||
id: plugin_key.clone(),
|
||||
name: plugin.plugin_id.plugin_name,
|
||||
local_version: plugin
|
||||
.manifest
|
||||
.as_ref()
|
||||
.and_then(|manifest| manifest.version.clone()),
|
||||
source: plugin.source,
|
||||
policy: plugin.policy,
|
||||
interface: plugin.interface,
|
||||
@@ -1319,6 +1328,7 @@ impl PluginsManager {
|
||||
return Ok(PluginDetail {
|
||||
id: plugin_key,
|
||||
name: plugin.name,
|
||||
local_version: None,
|
||||
description: Some(description),
|
||||
source: plugin.source,
|
||||
policy: plugin.policy,
|
||||
@@ -1411,6 +1421,7 @@ impl PluginsManager {
|
||||
Ok(PluginDetail {
|
||||
id: plugin.id,
|
||||
name: plugin.name,
|
||||
local_version: manifest.version.clone(),
|
||||
description,
|
||||
source: plugin.source,
|
||||
policy: plugin.policy,
|
||||
|
||||
@@ -1541,6 +1541,7 @@ enabled = false
|
||||
ConfiguredMarketplacePlugin {
|
||||
id: "enabled-plugin@debug".to_string(),
|
||||
name: "enabled-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(tmp.path().join("repo/enabled-plugin"))
|
||||
.unwrap(),
|
||||
@@ -1558,6 +1559,7 @@ enabled = false
|
||||
ConfiguredMarketplacePlugin {
|
||||
id: "disabled-plugin@debug".to_string(),
|
||||
name: "disabled-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(tmp.path().join("repo/disabled-plugin"),)
|
||||
.unwrap(),
|
||||
@@ -1678,6 +1680,7 @@ plugins = true
|
||||
vec![ConfiguredMarketplacePlugin {
|
||||
id: "default-plugin@debug".to_string(),
|
||||
name: "default-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(tmp.path().join("repo/default-plugin")).unwrap(),
|
||||
},
|
||||
@@ -2109,6 +2112,7 @@ enabled = true
|
||||
vec![ConfiguredMarketplacePlugin {
|
||||
id: "toolkit@debug".to_string(),
|
||||
name: "toolkit".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Git {
|
||||
url: missing_remote_repo_url,
|
||||
path: Some("plugins/toolkit".to_string()),
|
||||
@@ -2225,6 +2229,7 @@ plugins = true
|
||||
plugins: vec![ConfiguredMarketplacePlugin {
|
||||
id: "linear@openai-curated".to_string(),
|
||||
name: "linear".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(curated_root.join("plugins/linear")).unwrap(),
|
||||
},
|
||||
@@ -2519,6 +2524,7 @@ enabled = false
|
||||
vec![ConfiguredMarketplacePlugin {
|
||||
id: "dup-plugin@debug".to_string(),
|
||||
name: "dup-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(tmp.path().join("repo-a/from-a")).unwrap(),
|
||||
},
|
||||
@@ -2549,6 +2555,7 @@ enabled = false
|
||||
vec![ConfiguredMarketplacePlugin {
|
||||
id: "b-only-plugin@debug".to_string(),
|
||||
name: "b-only-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(tmp.path().join("repo-b/from-b-only")).unwrap(),
|
||||
},
|
||||
@@ -2633,6 +2640,7 @@ enabled = true
|
||||
plugins: vec![ConfiguredMarketplacePlugin {
|
||||
id: "sample-plugin@debug".to_string(),
|
||||
name: "sample-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(tmp.path().join("repo/sample-plugin")).unwrap(),
|
||||
},
|
||||
|
||||
@@ -59,6 +59,7 @@ pub struct MarketplaceInterface {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MarketplacePlugin {
|
||||
pub name: String,
|
||||
pub local_version: Option<String>,
|
||||
pub source: MarketplacePluginSource,
|
||||
pub policy: MarketplacePluginPolicy,
|
||||
pub interface: Option<PluginManifestInterface>,
|
||||
@@ -289,15 +290,22 @@ pub fn load_marketplace(path: &AbsolutePathBuf) -> Result<Marketplace, Marketpla
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
let local_version = plugin
|
||||
.manifest
|
||||
.as_ref()
|
||||
.and_then(|manifest| manifest.version.clone());
|
||||
let keywords = plugin
|
||||
.manifest
|
||||
.map(|manifest| manifest.keywords)
|
||||
.unwrap_or_default();
|
||||
|
||||
plugins.push(MarketplacePlugin {
|
||||
name: plugin.plugin_id.plugin_name,
|
||||
local_version,
|
||||
source: plugin.source,
|
||||
policy: plugin.policy,
|
||||
interface: plugin.interface,
|
||||
keywords: plugin
|
||||
.manifest
|
||||
.map(|manifest| manifest.keywords)
|
||||
.unwrap_or_default(),
|
||||
keywords,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -388,6 +388,7 @@ fn list_marketplaces_supports_alternate_manifest_layout() {
|
||||
interface: None,
|
||||
plugins: vec![MarketplacePlugin {
|
||||
name: "string-source-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(repo_root.join("plugins/string-source-plugin"))
|
||||
.unwrap(),
|
||||
@@ -453,6 +454,7 @@ fn list_marketplaces_includes_plugins_without_discoverable_manifest() {
|
||||
interface: None,
|
||||
plugins: vec![MarketplacePlugin {
|
||||
name: "missing-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(repo_root.join("plugins/missing-plugin"),)
|
||||
.unwrap(),
|
||||
@@ -595,6 +597,7 @@ fn list_marketplaces_returns_home_and_repo_marketplaces() {
|
||||
plugins: vec![
|
||||
MarketplacePlugin {
|
||||
name: "shared-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(home_root.join("home-shared")).unwrap(),
|
||||
},
|
||||
@@ -608,6 +611,7 @@ fn list_marketplaces_returns_home_and_repo_marketplaces() {
|
||||
},
|
||||
MarketplacePlugin {
|
||||
name: "home-only".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(home_root.join("home-only")).unwrap(),
|
||||
},
|
||||
@@ -630,6 +634,7 @@ fn list_marketplaces_returns_home_and_repo_marketplaces() {
|
||||
plugins: vec![
|
||||
MarketplacePlugin {
|
||||
name: "shared-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(repo_root.join("repo-shared")).unwrap(),
|
||||
},
|
||||
@@ -643,6 +648,7 @@ fn list_marketplaces_returns_home_and_repo_marketplaces() {
|
||||
},
|
||||
MarketplacePlugin {
|
||||
name: "repo-only".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(repo_root.join("repo-only")).unwrap(),
|
||||
},
|
||||
@@ -721,6 +727,7 @@ fn list_marketplaces_keeps_distinct_entries_for_same_name() {
|
||||
interface: None,
|
||||
plugins: vec![MarketplacePlugin {
|
||||
name: "local-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(home_root.join("home-plugin")).unwrap(),
|
||||
},
|
||||
@@ -739,6 +746,7 @@ fn list_marketplaces_keeps_distinct_entries_for_same_name() {
|
||||
interface: None,
|
||||
plugins: vec![MarketplacePlugin {
|
||||
name: "local-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(repo_root.join("repo-plugin")).unwrap(),
|
||||
},
|
||||
@@ -813,6 +821,7 @@ fn list_marketplaces_dedupes_multiple_roots_in_same_repo() {
|
||||
interface: None,
|
||||
plugins: vec![MarketplacePlugin {
|
||||
name: "local-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(repo_root.join("plugin")).unwrap(),
|
||||
},
|
||||
@@ -976,6 +985,7 @@ fn list_marketplaces_skips_plugins_with_invalid_names_but_keeps_marketplace() {
|
||||
interface: None,
|
||||
plugins: vec![MarketplacePlugin {
|
||||
name: "valid-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(repo_root.join("valid-plugin")).unwrap(),
|
||||
},
|
||||
@@ -1093,6 +1103,7 @@ fn list_marketplaces_keeps_remote_and_local_plugin_sources() {
|
||||
vec![
|
||||
MarketplacePlugin {
|
||||
name: "local-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Local {
|
||||
path: AbsolutePathBuf::try_from(repo_root.join("plugins/local-plugin"))
|
||||
.unwrap(),
|
||||
@@ -1107,6 +1118,7 @@ fn list_marketplaces_keeps_remote_and_local_plugin_sources() {
|
||||
},
|
||||
MarketplacePlugin {
|
||||
name: "url-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Git {
|
||||
url: "https://github.com/example/plugin.git".to_string(),
|
||||
path: None,
|
||||
@@ -1123,6 +1135,7 @@ fn list_marketplaces_keeps_remote_and_local_plugin_sources() {
|
||||
},
|
||||
MarketplacePlugin {
|
||||
name: "git-subdir-plugin".to_string(),
|
||||
local_version: None,
|
||||
source: MarketplacePluginSource::Git {
|
||||
url: "https://github.com/owner/repo.git".to_string(),
|
||||
path: Some("plugins/example".to_string()),
|
||||
|
||||
@@ -102,6 +102,7 @@ pub struct RemotePluginSummary {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RemotePluginShareContext {
|
||||
pub remote_plugin_id: String,
|
||||
pub remote_version: Option<String>,
|
||||
pub discoverability: RemotePluginShareDiscoverability,
|
||||
pub share_url: Option<String>,
|
||||
pub creator_account_user_id: Option<String>,
|
||||
@@ -897,6 +898,7 @@ fn remote_plugin_share_context(
|
||||
let discoverability = workspace_plugin_discoverability(plugin)?;
|
||||
Ok(Some(RemotePluginShareContext {
|
||||
remote_plugin_id: plugin.id.clone(),
|
||||
remote_version: plugin.release.version.clone(),
|
||||
discoverability,
|
||||
share_url: plugin.share_url.clone(),
|
||||
creator_account_user_id: plugin.creator_account_user_id.clone(),
|
||||
|
||||
@@ -97,6 +97,7 @@ fn remote_plugin_json(plugin_id: &str) -> serde_json::Value {
|
||||
"installation_policy": "AVAILABLE",
|
||||
"authentication_policy": "ON_USE",
|
||||
"release": {
|
||||
"version": "0.1.0",
|
||||
"display_name": "Demo Plugin",
|
||||
"description": "Demo plugin description",
|
||||
"interface": {
|
||||
@@ -590,6 +591,7 @@ async fn list_remote_plugin_shares_fetches_created_workspace_plugins() {
|
||||
name: "demo-plugin".to_string(),
|
||||
share_context: Some(RemotePluginShareContext {
|
||||
remote_plugin_id: "plugins_123".to_string(),
|
||||
remote_version: Some("0.1.0".to_string()),
|
||||
discoverability: RemotePluginShareDiscoverability::Private,
|
||||
share_url: Some(
|
||||
"https://chatgpt.example/plugins/share/share-key-1".to_string(),
|
||||
@@ -628,6 +630,7 @@ async fn list_remote_plugin_shares_fetches_created_workspace_plugins() {
|
||||
name: "demo-plugin".to_string(),
|
||||
share_context: Some(RemotePluginShareContext {
|
||||
remote_plugin_id: "plugins_456".to_string(),
|
||||
remote_version: Some("0.1.0".to_string()),
|
||||
discoverability: RemotePluginShareDiscoverability::Private,
|
||||
share_url: None,
|
||||
creator_account_user_id: None,
|
||||
|
||||
Reference in New Issue
Block a user