feat: Add marketplace source filtering and plugin share context (#21419)

Adds marketplaceKinds to plugin/list for local, workspace-directory, and
shared-with-me; omitted params keep default local plus gated global
behavior, while explicit kinds are exact.

Exposes shareContext on plugin summaries from local share mappings and
remote workspace/shared responses, including remotePluginId and nullable
creator metadata.

Adds shared-with-me listing through /ps/plugins/workspace/shared,
renames the workspace remote namespace to workspace-directory, and keeps
direct remote read/share/install/update/delete paths gated by plugins
rather than remote_plugin.
This commit is contained in:
xl-openai
2026-05-06 16:12:23 -07:00
committed by GitHub
parent 9417cf9696
commit 11106016ff
28 changed files with 1357 additions and 201 deletions

View File

@@ -2144,6 +2144,14 @@
],
"type": "object"
},
"PluginListMarketplaceKind": {
"enum": [
"local",
"workspace-directory",
"shared-with-me"
],
"type": "string"
},
"PluginListParams": {
"properties": {
"cwds": {
@@ -2155,6 +2163,16 @@
"array",
"null"
]
},
"marketplaceKinds": {
"description": "Optional marketplace kind filter. When omitted, only local marketplaces are queried, plus the default remote catalog when enabled by feature flag.",
"items": {
"$ref": "#/definitions/PluginListMarketplaceKind"
},
"type": [
"array",
"null"
]
}
},
"type": "object"

View File

@@ -12353,6 +12353,14 @@
],
"type": "object"
},
"PluginListMarketplaceKind": {
"enum": [
"local",
"workspace-directory",
"shared-with-me"
],
"type": "string"
},
"PluginListParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -12365,6 +12373,16 @@
"array",
"null"
]
},
"marketplaceKinds": {
"description": "Optional marketplace kind filter. When omitted, only local marketplaces are queried, plus the default remote catalog when enabled by feature flag.",
"items": {
"$ref": "#/definitions/v2/PluginListMarketplaceKind"
},
"type": [
"array",
"null"
]
}
},
"title": "PluginListParams",
@@ -12481,6 +12499,29 @@
"title": "PluginReadResponse",
"type": "object"
},
"PluginShareContext": {
"properties": {
"creatorAccountUserId": {
"type": [
"string",
"null"
]
},
"creatorName": {
"type": [
"string",
"null"
]
},
"remotePluginId": {
"type": "string"
}
},
"required": [
"remotePluginId"
],
"type": "object"
},
"PluginShareDeleteParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -12845,6 +12886,17 @@
"name": {
"type": "string"
},
"shareContext": {
"anyOf": [
{
"$ref": "#/definitions/v2/PluginShareContext"
},
{
"type": "null"
}
],
"description": "Remote sharing context associated with this plugin when available."
},
"source": {
"$ref": "#/definitions/v2/PluginSource"
}

View File

@@ -8964,6 +8964,14 @@
],
"type": "object"
},
"PluginListMarketplaceKind": {
"enum": [
"local",
"workspace-directory",
"shared-with-me"
],
"type": "string"
},
"PluginListParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -8976,6 +8984,16 @@
"array",
"null"
]
},
"marketplaceKinds": {
"description": "Optional marketplace kind filter. When omitted, only local marketplaces are queried, plus the default remote catalog when enabled by feature flag.",
"items": {
"$ref": "#/definitions/PluginListMarketplaceKind"
},
"type": [
"array",
"null"
]
}
},
"title": "PluginListParams",
@@ -9092,6 +9110,29 @@
"title": "PluginReadResponse",
"type": "object"
},
"PluginShareContext": {
"properties": {
"creatorAccountUserId": {
"type": [
"string",
"null"
]
},
"creatorName": {
"type": [
"string",
"null"
]
},
"remotePluginId": {
"type": "string"
}
},
"required": [
"remotePluginId"
],
"type": "object"
},
"PluginShareDeleteParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -9456,6 +9497,17 @@
"name": {
"type": "string"
},
"shareContext": {
"anyOf": [
{
"$ref": "#/definitions/PluginShareContext"
},
{
"type": "null"
}
],
"description": "Remote sharing context associated with this plugin when available."
},
"source": {
"$ref": "#/definitions/PluginSource"
}

View File

@@ -4,6 +4,14 @@
"AbsolutePathBuf": {
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type": "string"
},
"PluginListMarketplaceKind": {
"enum": [
"local",
"workspace-directory",
"shared-with-me"
],
"type": "string"
}
},
"properties": {
@@ -16,6 +24,16 @@
"array",
"null"
]
},
"marketplaceKinds": {
"description": "Optional marketplace kind filter. When omitted, only local marketplaces are queried, plus the default remote catalog when enabled by feature flag.",
"items": {
"$ref": "#/definitions/PluginListMarketplaceKind"
},
"type": [
"array",
"null"
]
}
},
"title": "PluginListParams",

View File

@@ -232,6 +232,29 @@
],
"type": "object"
},
"PluginShareContext": {
"properties": {
"creatorAccountUserId": {
"type": [
"string",
"null"
]
},
"creatorName": {
"type": [
"string",
"null"
]
},
"remotePluginId": {
"type": "string"
}
},
"required": [
"remotePluginId"
],
"type": "object"
},
"PluginSource": {
"oneOf": [
{
@@ -357,6 +380,17 @@
"name": {
"type": "string"
},
"shareContext": {
"anyOf": [
{
"$ref": "#/definitions/PluginShareContext"
},
{
"type": "null"
}
],
"description": "Remote sharing context associated with this plugin when available."
},
"source": {
"$ref": "#/definitions/PluginSource"
}

View File

@@ -251,6 +251,29 @@
],
"type": "object"
},
"PluginShareContext": {
"properties": {
"creatorAccountUserId": {
"type": [
"string",
"null"
]
},
"creatorName": {
"type": [
"string",
"null"
]
},
"remotePluginId": {
"type": "string"
}
},
"required": [
"remotePluginId"
],
"type": "object"
},
"PluginSource": {
"oneOf": [
{
@@ -376,6 +399,17 @@
"name": {
"type": "string"
},
"shareContext": {
"anyOf": [
{
"$ref": "#/definitions/PluginShareContext"
},
{
"type": "null"
}
],
"description": "Remote sharing context associated with this plugin when available."
},
"source": {
"$ref": "#/definitions/PluginSource"
}

View File

@@ -167,6 +167,29 @@
],
"type": "object"
},
"PluginShareContext": {
"properties": {
"creatorAccountUserId": {
"type": [
"string",
"null"
]
},
"creatorName": {
"type": [
"string",
"null"
]
},
"remotePluginId": {
"type": "string"
}
},
"required": [
"remotePluginId"
],
"type": "object"
},
"PluginShareListItem": {
"properties": {
"localPluginPath": {
@@ -317,6 +340,17 @@
"name": {
"type": "string"
},
"shareContext": {
"anyOf": [
{
"$ref": "#/definitions/PluginShareContext"
},
{
"type": "null"
}
],
"description": "Remote sharing context associated with this plugin when available."
},
"source": {
"$ref": "#/definitions/PluginSource"
}

View File

@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type PluginListMarketplaceKind = "local" | "workspace-directory" | "shared-with-me";

View File

@@ -2,10 +2,16 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { PluginListMarketplaceKind } from "./PluginListMarketplaceKind";
export type PluginListParams = {
/**
* Optional working directories used to discover repo marketplaces. When omitted,
* only home-scoped marketplaces and the official curated marketplace are considered.
*/
cwds?: Array<AbsolutePathBuf> | null, };
cwds?: Array<AbsolutePathBuf> | null,
/**
* Optional marketplace kind filter. When omitted, only local marketplaces are queried, plus
* the default remote catalog when enabled by feature flag.
*/
marketplaceKinds?: Array<PluginListMarketplaceKind> | null, };

View File

@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type PluginShareContext = { remotePluginId: string, creatorAccountUserId: string | null, creatorName: string | null, };

View File

@@ -5,9 +5,14 @@ import type { PluginAuthPolicy } from "./PluginAuthPolicy";
import type { PluginAvailability } from "./PluginAvailability";
import type { PluginInstallPolicy } from "./PluginInstallPolicy";
import type { PluginInterface } from "./PluginInterface";
import type { PluginShareContext } from "./PluginShareContext";
import type { PluginSource } from "./PluginSource";
export type PluginSummary = { id: string, name: string, source: PluginSource, installed: boolean, enabled: boolean, installPolicy: PluginInstallPolicy, authPolicy: PluginAuthPolicy,
export type PluginSummary = { id: string, name: string,
/**
* Remote sharing context associated with this plugin when available.
*/
shareContext: PluginShareContext | null, source: PluginSource, installed: boolean, enabled: boolean, installPolicy: PluginInstallPolicy, authPolicy: PluginAuthPolicy,
/**
* Availability state for installing and using the plugin.
*/

View File

@@ -278,11 +278,13 @@ export type { PluginInstallParams } from "./PluginInstallParams";
export type { PluginInstallPolicy } from "./PluginInstallPolicy";
export type { PluginInstallResponse } from "./PluginInstallResponse";
export type { PluginInterface } from "./PluginInterface";
export type { PluginListMarketplaceKind } from "./PluginListMarketplaceKind";
export type { PluginListParams } from "./PluginListParams";
export type { PluginListResponse } from "./PluginListResponse";
export type { PluginMarketplaceEntry } from "./PluginMarketplaceEntry";
export type { PluginReadParams } from "./PluginReadParams";
export type { PluginReadResponse } from "./PluginReadResponse";
export type { PluginShareContext } from "./PluginShareContext";
export type { PluginShareDeleteParams } from "./PluginShareDeleteParams";
export type { PluginShareDeleteResponse } from "./PluginShareDeleteResponse";
export type { PluginShareDiscoverability } from "./PluginShareDiscoverability";

View File

@@ -132,6 +132,24 @@ pub struct PluginListParams {
/// only home-scoped marketplaces and the official curated marketplace are considered.
#[ts(optional = nullable)]
pub cwds: Option<Vec<AbsolutePathBuf>>,
/// Optional marketplace kind filter. When omitted, only local marketplaces are queried, plus
/// the default remote catalog when enabled by feature flag.
#[ts(optional = nullable)]
pub marketplace_kinds: Option<Vec<PluginListMarketplaceKind>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[ts(export_to = "v2/")]
pub enum PluginListMarketplaceKind {
#[serde(rename = "local")]
#[ts(rename = "local")]
Local,
#[serde(rename = "workspace-directory")]
#[ts(rename = "workspace-directory")]
WorkspaceDirectory,
#[serde(rename = "shared-with-me")]
#[ts(rename = "shared-with-me")]
SharedWithMe,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
@@ -501,6 +519,8 @@ pub enum PluginAvailability {
pub struct PluginSummary {
pub id: String,
pub name: String,
/// Remote sharing context associated with this plugin when available.
pub share_context: Option<PluginShareContext>,
pub source: PluginSource,
pub installed: bool,
pub enabled: bool,
@@ -514,6 +534,15 @@ pub struct PluginSummary {
pub keywords: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct PluginShareContext {
pub remote_plugin_id: String,
pub creator_account_user_id: Option<String>,
pub creator_name: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]

View File

@@ -2843,7 +2843,33 @@ fn plugin_list_params_ignore_removed_force_remote_sync_field() {
"forceRemoteSync": true,
}))
.unwrap(),
PluginListParams { cwds: None },
PluginListParams {
cwds: None,
marketplace_kinds: None,
},
);
}
#[test]
fn plugin_list_params_serializes_marketplace_kind_filter() {
assert_eq!(
serde_json::to_value(PluginListParams {
cwds: None,
marketplace_kinds: Some(vec![
PluginListMarketplaceKind::Local,
PluginListMarketplaceKind::WorkspaceDirectory,
PluginListMarketplaceKind::SharedWithMe,
]),
})
.unwrap(),
json!({
"cwds": null,
"marketplaceKinds": [
"local",
"workspace-directory",
"shared-with-me",
],
}),
);
}
@@ -3099,6 +3125,7 @@ fn plugin_share_list_response_serializes_share_items() {
plugin: PluginSummary {
id: "plugins~Plugin_00000000000000000000000000000000".to_string(),
name: "gmail".to_string(),
share_context: None,
source: PluginSource::Remote,
installed: false,
enabled: false,
@@ -3118,6 +3145,7 @@ fn plugin_share_list_response_serializes_share_items() {
"plugin": {
"id": "plugins~Plugin_00000000000000000000000000000000",
"name": "gmail",
"shareContext": null,
"source": { "type": "remote" },
"installed": false,
"enabled": false,
@@ -3149,6 +3177,7 @@ fn plugin_summary_defaults_missing_availability_to_available() {
.unwrap();
assert_eq!(summary.availability, PluginAvailability::Available);
assert_eq!(summary.share_context, None);
}
#[test]