diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index 207c6c92f..4709bae11 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -8491,6 +8491,17 @@ "title": "LogoutAccountResponse", "type": "object" }, + "MarketplaceInterface": { + "properties": { + "displayName": { + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, "McpAuthStatus": { "enum": [ "unsupported", @@ -9376,10 +9387,14 @@ }, "PluginMarketplaceEntry": { "properties": { - "displayName": { - "type": [ - "string", - "null" + "interface": { + "anyOf": [ + { + "$ref": "#/definitions/v2/MarketplaceInterface" + }, + { + "type": "null" + } ] }, "name": { diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index 62acdafee..601306fe1 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -5279,6 +5279,17 @@ "title": "LogoutAccountResponse", "type": "object" }, + "MarketplaceInterface": { + "properties": { + "displayName": { + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, "McpAuthStatus": { "enum": [ "unsupported", @@ -6164,10 +6175,14 @@ }, "PluginMarketplaceEntry": { "properties": { - "displayName": { - "type": [ - "string", - "null" + "interface": { + "anyOf": [ + { + "$ref": "#/definitions/MarketplaceInterface" + }, + { + "type": "null" + } ] }, "name": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/PluginListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/PluginListResponse.json index 0c5eb0a1f..f889bf3e8 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/PluginListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/PluginListResponse.json @@ -5,6 +5,17 @@ "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" }, + "MarketplaceInterface": { + "properties": { + "displayName": { + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, "PluginAuthPolicy": { "enum": [ "ON_INSTALL", @@ -127,10 +138,14 @@ }, "PluginMarketplaceEntry": { "properties": { - "displayName": { - "type": [ - "string", - "null" + "interface": { + "anyOf": [ + { + "$ref": "#/definitions/MarketplaceInterface" + }, + { + "type": "null" + } ] }, "name": { diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/MarketplaceInterface.ts b/codex-rs/app-server-protocol/schema/typescript/v2/MarketplaceInterface.ts new file mode 100644 index 000000000..f82dc1794 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/MarketplaceInterface.ts @@ -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 MarketplaceInterface = { displayName: string | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/PluginMarketplaceEntry.ts b/codex-rs/app-server-protocol/schema/typescript/v2/PluginMarketplaceEntry.ts index 1a6149e22..c0ab75b8f 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/PluginMarketplaceEntry.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/PluginMarketplaceEntry.ts @@ -2,6 +2,7 @@ // 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 { MarketplaceInterface } from "./MarketplaceInterface"; import type { PluginSummary } from "./PluginSummary"; -export type PluginMarketplaceEntry = { name: string, path: AbsolutePathBuf, displayName: string | null, plugins: Array, }; +export type PluginMarketplaceEntry = { name: string, path: AbsolutePathBuf, interface: MarketplaceInterface | null, plugins: Array, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts index b8419a79d..1529bbea8 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts @@ -139,6 +139,7 @@ export type { ListMcpServerStatusResponse } from "./ListMcpServerStatusResponse" export type { LoginAccountParams } from "./LoginAccountParams"; export type { LoginAccountResponse } from "./LoginAccountResponse"; export type { LogoutAccountResponse } from "./LogoutAccountResponse"; +export type { MarketplaceInterface } from "./MarketplaceInterface"; export type { McpAuthStatus } from "./McpAuthStatus"; export type { McpElicitationArrayType } from "./McpElicitationArrayType"; export type { McpElicitationBooleanSchema } from "./McpElicitationBooleanSchema"; diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 3b481d563..e2316d8e7 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -3276,10 +3276,17 @@ pub struct SkillsListEntry { pub struct PluginMarketplaceEntry { pub name: String, pub path: AbsolutePathBuf, - pub display_name: Option, + pub interface: Option, pub plugins: Vec, } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct MarketplaceInterface { + pub display_name: Option, +} + #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[ts(export_to = "v2/")] pub enum PluginInstallPolicy { diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 6472b78bf..eb4e432c5 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -72,6 +72,7 @@ use codex_app_server_protocol::LoginAccountParams; use codex_app_server_protocol::LoginAccountResponse; use codex_app_server_protocol::LoginApiKeyParams; use codex_app_server_protocol::LogoutAccountResponse; +use codex_app_server_protocol::MarketplaceInterface; use codex_app_server_protocol::McpServerOauthLoginCompletedNotification; use codex_app_server_protocol::McpServerOauthLoginParams; use codex_app_server_protocol::McpServerOauthLoginResponse; @@ -5452,7 +5453,9 @@ impl CodexMessageProcessor { .map(|marketplace| PluginMarketplaceEntry { name: marketplace.name, path: marketplace.path, - display_name: marketplace.display_name, + interface: marketplace.interface.map(|interface| MarketplaceInterface { + display_name: interface.display_name, + }), plugins: marketplace .plugins .into_iter() diff --git a/codex-rs/app-server/tests/suite/v2/plugin_list.rs b/codex-rs/app-server/tests/suite/v2/plugin_list.rs index 42a62aeff..a628275cd 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_list.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_list.rs @@ -152,7 +152,9 @@ async fn plugin_list_includes_install_and_enabled_state_from_config() -> Result< repo_root.path().join(".agents/plugins/marketplace.json"), r#"{ "name": "codex-curated", - "display_name": "ChatGPT Official", + "interface": { + "displayName": "ChatGPT Official" + }, "plugins": [ { "name": "enabled-plugin", @@ -222,7 +224,10 @@ enabled = false assert_eq!(marketplace.name, "codex-curated"); assert_eq!( - marketplace.display_name.as_deref(), + marketplace + .interface + .as_ref() + .and_then(|interface| interface.display_name.as_deref()), Some("ChatGPT Official") ); assert_eq!(marketplace.plugins.len(), 3); diff --git a/codex-rs/core/src/plugins/manager.rs b/codex-rs/core/src/plugins/manager.rs index 4b2496824..8347dc427 100644 --- a/codex-rs/core/src/plugins/manager.rs +++ b/codex-rs/core/src/plugins/manager.rs @@ -3,6 +3,7 @@ use super::curated_plugins_repo_path; use super::load_plugin_manifest; use super::manifest::PluginManifestInterfaceSummary; use super::marketplace::MarketplaceError; +use super::marketplace::MarketplaceInterfaceSummary; use super::marketplace::MarketplacePluginAuthPolicy; use super::marketplace::MarketplacePluginInstallPolicy; use super::marketplace::MarketplacePluginSourceSummary; @@ -121,7 +122,7 @@ pub struct PluginDetailSummary { pub struct ConfiguredMarketplaceSummary { pub name: String, pub path: AbsolutePathBuf, - pub display_name: Option, + pub interface: Option, pub plugins: Vec, } @@ -846,7 +847,7 @@ impl PluginsManager { (!plugins.is_empty()).then_some(ConfiguredMarketplaceSummary { name: marketplace.name, path: marketplace.path, - display_name: marketplace.display_name, + interface: marketplace.interface, plugins, }) }) diff --git a/codex-rs/core/src/plugins/manager_tests.rs b/codex-rs/core/src/plugins/manager_tests.rs index 70b437016..2a327f4e4 100644 --- a/codex-rs/core/src/plugins/manager_tests.rs +++ b/codex-rs/core/src/plugins/manager_tests.rs @@ -999,7 +999,7 @@ enabled = false tmp.path().join("repo/.agents/plugins/marketplace.json"), ) .unwrap(), - display_name: None, + interface: None, plugins: vec![ ConfiguredMarketplacePluginSummary { id: "enabled-plugin@debug".to_string(), @@ -1044,7 +1044,9 @@ async fn list_marketplaces_includes_curated_repo_marketplace() { curated_root.join(".agents/plugins/marketplace.json"), r#"{ "name": "openai-curated", - "display_name": "ChatGPT Official", + "interface": { + "displayName": "ChatGPT Official" + }, "plugins": [ { "name": "linear", @@ -1079,7 +1081,9 @@ async fn list_marketplaces_includes_curated_repo_marketplace() { name: "openai-curated".to_string(), path: AbsolutePathBuf::try_from(curated_root.join(".agents/plugins/marketplace.json")) .unwrap(), - display_name: Some("ChatGPT Official".to_string()), + interface: Some(MarketplaceInterfaceSummary { + display_name: Some("ChatGPT Official".to_string()), + }), plugins: vec![ConfiguredMarketplacePluginSummary { id: "linear@openai-curated".to_string(), name: "linear".to_string(), @@ -1284,7 +1288,7 @@ enabled = true tmp.path().join("repo/.agents/plugins/marketplace.json"), ) .unwrap(), - display_name: None, + interface: None, plugins: vec![ConfiguredMarketplacePluginSummary { id: "sample-plugin@debug".to_string(), name: "sample-plugin".to_string(), diff --git a/codex-rs/core/src/plugins/marketplace.rs b/codex-rs/core/src/plugins/marketplace.rs index 4e4bfdd0a..c7c9a70b3 100644 --- a/codex-rs/core/src/plugins/marketplace.rs +++ b/codex-rs/core/src/plugins/marketplace.rs @@ -28,10 +28,15 @@ pub struct ResolvedMarketplacePlugin { pub struct MarketplaceSummary { pub name: String, pub path: AbsolutePathBuf, - pub display_name: Option, + pub interface: Option, pub plugins: Vec, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MarketplaceInterfaceSummary { + pub display_name: Option, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct MarketplacePluginSummary { pub name: String, @@ -213,7 +218,7 @@ pub(crate) fn load_marketplace_summary( Ok(MarketplaceSummary { name: marketplace.name, path: path.clone(), - display_name: marketplace.display_name, + interface: marketplace_interface_summary(marketplace.interface), plugins, }) } @@ -367,14 +372,21 @@ fn marketplace_root_dir( } #[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] struct MarketplaceFile { name: String, #[serde(default)] - #[serde(alias = "displayName")] - display_name: Option, + interface: Option, plugins: Vec, } +#[derive(Debug, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +struct MarketplaceInterface { + #[serde(default)] + display_name: Option, +} + #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] struct MarketplacePlugin { @@ -394,6 +406,19 @@ enum MarketplacePluginSource { Local { path: String }, } +fn marketplace_interface_summary( + interface: Option, +) -> Option { + let interface = interface?; + if interface.display_name.is_some() { + Some(MarketplaceInterfaceSummary { + display_name: interface.display_name, + }) + } else { + None + } +} + #[cfg(test)] #[path = "marketplace_tests.rs"] mod tests; diff --git a/codex-rs/core/src/plugins/marketplace_tests.rs b/codex-rs/core/src/plugins/marketplace_tests.rs index ce12d885d..1fac8bec6 100644 --- a/codex-rs/core/src/plugins/marketplace_tests.rs +++ b/codex-rs/core/src/plugins/marketplace_tests.rs @@ -137,7 +137,7 @@ fn list_marketplaces_returns_home_and_repo_marketplaces() { path: AbsolutePathBuf::try_from(home_root.join(".agents/plugins/marketplace.json"),) .unwrap(), - display_name: None, + interface: None, plugins: vec![ MarketplacePluginSummary { name: "shared-plugin".to_string(), @@ -164,7 +164,7 @@ fn list_marketplaces_returns_home_and_repo_marketplaces() { path: AbsolutePathBuf::try_from(repo_root.join(".agents/plugins/marketplace.json"),) .unwrap(), - display_name: None, + interface: None, plugins: vec![ MarketplacePluginSummary { name: "shared-plugin".to_string(), @@ -247,7 +247,7 @@ fn list_marketplaces_keeps_distinct_entries_for_same_name() { MarketplaceSummary { name: "codex-curated".to_string(), path: AbsolutePathBuf::try_from(home_marketplace).unwrap(), - display_name: None, + interface: None, plugins: vec![MarketplacePluginSummary { name: "local-plugin".to_string(), source: MarketplacePluginSourceSummary::Local { @@ -261,7 +261,7 @@ fn list_marketplaces_keeps_distinct_entries_for_same_name() { MarketplaceSummary { name: "codex-curated".to_string(), path: AbsolutePathBuf::try_from(repo_marketplace.clone()).unwrap(), - display_name: None, + interface: None, plugins: vec![MarketplacePluginSummary { name: "local-plugin".to_string(), source: MarketplacePluginSourceSummary::Local { @@ -328,7 +328,7 @@ fn list_marketplaces_dedupes_multiple_roots_in_same_repo() { name: "codex-curated".to_string(), path: AbsolutePathBuf::try_from(repo_root.join(".agents/plugins/marketplace.json")) .unwrap(), - display_name: None, + interface: None, plugins: vec![MarketplacePluginSummary { name: "local-plugin".to_string(), source: MarketplacePluginSourceSummary::Local { @@ -353,7 +353,9 @@ fn list_marketplaces_reads_marketplace_display_name() { repo_root.join(".agents/plugins/marketplace.json"), r#"{ "name": "openai-curated", - "display_name": "ChatGPT Official", + "interface": { + "displayName": "ChatGPT Official" + }, "plugins": [ { "name": "local-plugin", @@ -372,8 +374,10 @@ fn list_marketplaces_reads_marketplace_display_name() { .unwrap(); assert_eq!( - marketplaces[0].display_name, - Some("ChatGPT Official".to_string()) + marketplaces[0].interface, + Some(MarketplaceInterfaceSummary { + display_name: Some("ChatGPT Official".to_string()), + }) ); }