[codex] Add created-by-me remote plugin marketplace (#28203)

## Summary
- add the `created-by-me-remote` marketplace backed by paginated
`scope=USER` plugin directory and installed-plugin requests
- include USER plugins in installed-plugin caching, bundle sync, and
stale-cache cleanup without client-side discoverability filtering
- expose the marketplace through app-server v2 and regenerate the
protocol schemas

## Testing
- `cargo build -p codex-app-server --bin codex-app-server`
- production-auth `plugin/list` smoke test for `created-by-me-remote`
(returned the expected USER plugin as installed and enabled)
- `just test -p codex-core-plugins` (221 passed)
- `just test -p codex-app-server-protocol` (231 passed)
- `just test -p codex-app-server suite::v2::plugin_list::` (37 passed)
- `just fix -p codex-core-plugins -p codex-app-server-protocol -p
codex-app-server`
- `just fmt`
This commit is contained in:
Eric Ning
2026-06-15 15:07:07 -07:00
committed by GitHub
Unverified
parent 040dafa32d
commit 709f19e111
15 changed files with 431 additions and 17 deletions
+39 -4
View File
@@ -51,6 +51,7 @@ pub use share::save_remote_plugin_share;
pub use share::update_remote_plugin_share_targets;
pub const REMOTE_GLOBAL_MARKETPLACE_NAME: &str = "openai-curated-remote";
pub const REMOTE_CREATED_BY_ME_MARKETPLACE_NAME: &str = "created-by-me-remote";
pub const REMOTE_WORKSPACE_MARKETPLACE_NAME: &str = "workspace-directory";
pub const REMOTE_WORKSPACE_SHARED_WITH_ME_MARKETPLACE_NAME: &str = "workspace-shared-with-me";
pub const REMOTE_WORKSPACE_SHARED_WITH_ME_PRIVATE_MARKETPLACE_NAME: &str =
@@ -58,6 +59,7 @@ pub const REMOTE_WORKSPACE_SHARED_WITH_ME_PRIVATE_MARKETPLACE_NAME: &str =
pub const REMOTE_WORKSPACE_SHARED_WITH_ME_UNLISTED_MARKETPLACE_NAME: &str =
"workspace-shared-with-me-unlisted";
pub const REMOTE_GLOBAL_MARKETPLACE_DISPLAY_NAME: &str = "OpenAI Curated Remote";
pub const REMOTE_CREATED_BY_ME_MARKETPLACE_DISPLAY_NAME: &str = "Created by me";
pub const REMOTE_WORKSPACE_MARKETPLACE_DISPLAY_NAME: &str = "Workspace Directory";
pub const REMOTE_WORKSPACE_SHARED_WITH_ME_PRIVATE_MARKETPLACE_DISPLAY_NAME: &str = "Shared with me";
pub const REMOTE_WORKSPACE_SHARED_WITH_ME_UNLISTED_MARKETPLACE_DISPLAY_NAME: &str =
@@ -71,11 +73,15 @@ const REMOTE_PLUGIN_LIST_PAGE_LIMIT: u32 = 200;
const MAX_REMOTE_DEFAULT_PROMPT_COUNT: usize = 3;
const MAX_REMOTE_DEFAULT_PROMPT_LEN: usize = 128;
const INVALID_REQUEST_ERROR_CODE: i64 = -32600;
const REMOTE_INSTALLED_MARKETPLACE_DISPLAY_ORDER: [(&str, &str); 5] = [
const REMOTE_INSTALLED_MARKETPLACE_DISPLAY_ORDER: [(&str, &str); 6] = [
(
REMOTE_GLOBAL_MARKETPLACE_NAME,
REMOTE_GLOBAL_MARKETPLACE_DISPLAY_NAME,
),
(
REMOTE_CREATED_BY_ME_MARKETPLACE_NAME,
REMOTE_CREATED_BY_ME_MARKETPLACE_DISPLAY_NAME,
),
(
REMOTE_WORKSPACE_MARKETPLACE_NAME,
REMOTE_WORKSPACE_MARKETPLACE_DISPLAY_NAME,
@@ -109,6 +115,7 @@ pub struct RemoteMarketplace {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RemoteMarketplaceSource {
Global,
CreatedByMeRemote,
WorkspaceDirectory,
SharedWithMe,
}
@@ -338,6 +345,8 @@ pub enum RemotePluginCatalogError {
pub enum RemotePluginScope {
#[serde(rename = "GLOBAL")]
Global,
#[serde(rename = "USER")]
User,
#[serde(rename = "WORKSPACE")]
Workspace,
}
@@ -346,6 +355,7 @@ impl RemotePluginScope {
fn api_value(self) -> &'static str {
match self {
Self::Global => "GLOBAL",
Self::User => "USER",
Self::Workspace => "WORKSPACE",
}
}
@@ -353,6 +363,7 @@ impl RemotePluginScope {
fn marketplace_name(self) -> &'static str {
match self {
Self::Global => REMOTE_GLOBAL_MARKETPLACE_NAME,
Self::User => REMOTE_CREATED_BY_ME_MARKETPLACE_NAME,
Self::Workspace => REMOTE_WORKSPACE_MARKETPLACE_NAME,
}
}
@@ -360,6 +371,7 @@ impl RemotePluginScope {
fn marketplace_display_name(self) -> &'static str {
match self {
Self::Global => REMOTE_GLOBAL_MARKETPLACE_DISPLAY_NAME,
Self::User => REMOTE_CREATED_BY_ME_MARKETPLACE_DISPLAY_NAME,
Self::Workspace => REMOTE_WORKSPACE_MARKETPLACE_DISPLAY_NAME,
}
}
@@ -367,6 +379,7 @@ impl RemotePluginScope {
fn from_marketplace_name(name: &str) -> Option<Self> {
match name {
REMOTE_GLOBAL_MARKETPLACE_NAME => Some(Self::Global),
REMOTE_CREATED_BY_ME_MARKETPLACE_NAME => Some(Self::User),
REMOTE_WORKSPACE_MARKETPLACE_NAME
| REMOTE_WORKSPACE_SHARED_WITH_ME_MARKETPLACE_NAME
| REMOTE_WORKSPACE_SHARED_WITH_ME_PRIVATE_MARKETPLACE_NAME
@@ -500,6 +513,7 @@ fn remote_plugin_canonical_marketplace_name(
) -> Result<&'static str, RemotePluginCatalogError> {
match plugin.scope {
RemotePluginScope::Global => Ok(REMOTE_GLOBAL_MARKETPLACE_NAME),
RemotePluginScope::User => Ok(REMOTE_CREATED_BY_ME_MARKETPLACE_NAME),
RemotePluginScope::Workspace => match workspace_plugin_discoverability(plugin)? {
RemotePluginShareDiscoverability::Listed => Ok(REMOTE_WORKSPACE_MARKETPLACE_NAME),
RemotePluginShareDiscoverability::Private
@@ -631,6 +645,22 @@ pub async fn fetch_remote_marketplaces(
);
}
}
RemoteMarketplaceSource::CreatedByMeRemote => {
let scope = RemotePluginScope::User;
let (directory_plugins, installed_plugins) = tokio::try_join!(
fetch_directory_plugins_for_scope(config, auth, scope),
fetch_installed_plugins_for_scope(config, auth, scope),
)?;
if let Some(marketplace) = build_remote_marketplace(
scope.marketplace_name(),
scope.marketplace_display_name(),
directory_plugins,
installed_plugins,
/*include_installed_only*/ false,
)? {
marketplaces.push(marketplace);
}
}
RemoteMarketplaceSource::WorkspaceDirectory => {
let scope = RemotePluginScope::Workspace;
let directory_plugins =
@@ -838,9 +868,14 @@ pub(crate) async fn fetch_remote_installed_plugins(
let installed_plugins = fetch_installed_plugins_for_scope(config, auth, scope).await?;
Ok::<_, RemotePluginCatalogError>((scope, installed_plugins))
};
let user = async {
let scope = RemotePluginScope::User;
let installed_plugins = fetch_installed_plugins_for_scope(config, auth, scope).await?;
Ok::<_, RemotePluginCatalogError>((scope, installed_plugins))
};
let (global, workspace) = tokio::try_join!(global, workspace)?;
let mut installed_plugins = [global, workspace]
let (global, workspace, user) = tokio::try_join!(global, workspace, user)?;
let mut installed_plugins = [global, workspace, user]
.into_iter()
.flat_map(|(_scope, plugins)| plugins)
.map(|plugin| remote_installed_plugin_to_cache_entry(&plugin))
@@ -1266,7 +1301,7 @@ fn remote_plugin_share_context(
plugin: &RemotePluginDirectoryItem,
) -> Result<Option<RemotePluginShareContext>, RemotePluginCatalogError> {
match plugin.scope {
RemotePluginScope::Global => Ok(None),
RemotePluginScope::Global | RemotePluginScope::User => Ok(None),
RemotePluginScope::Workspace => {
let discoverability = workspace_plugin_discoverability(plugin)?;
Ok(Some(RemotePluginShareContext {