mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
2c351cb864
Adds additive dark-mode plugin logo metadata across manifests, remote catalogs, and the app-server protocol while keeping uninstalled Git listings free of synthetic local paths. Supersedes #28945. This replacement uses an upstream branch so trusted CI can use the repository-provided remote Bazel configuration. ## Current state Plugin interfaces expose only the default logo asset. Clients therefore cannot select a dedicated dark-mode logo even when a plugin provides one. ## What this PR changes - Adds nullable `logoDark` and `logoUrlDark` fields to `PluginInterface`. - Resolves local `interface.logoDark` assets and maps remote `logo_url_dark` values. - Removes path-backed interface assets, including `logoDark`, from uninstalled Git fallback listings until the plugin has a real local root. - Updates the bundled plugin validator and manifest reference. - Regenerates the app-server JSON schemas and TypeScript types. Local manifests expose `interface.logoDark` as a package-relative asset path. Remote catalog responses expose `logo_url_dark`. These values map into separate app-server fields so clients can preserve local-path and remote-URL handling. ## Risk The fields are additive and nullable, so existing clients retain their current logo behavior. The main risks are an incomplete mapping path or exposing a synthetic local path for an uninstalled Git plugin. Local-manifest, remote-catalog, fallback-listing, protocol serialization, and app-server integration tests cover those paths. Spiciness: 2/5 ## Testing - `just write-app-server-schema` - `just fmt` - Regression test first failed with `logo_dark` resolved to `/assets/logo-dark.png`, then passed after the fallback-listing fix. - `just test -p codex-core-plugins` (267 tests passed) - `just test -p codex-app-server 'suite::v2::plugin'` (114 tests passed) - `just test -p codex-app-server-protocol -p codex-core-plugins -p codex-plugin -p codex-skills` (517 tests passed before the follow-up) - `just test -p codex-tui plugin` (47 tests passed) - Validated a local plugin manifest containing `interface.logoDark` with the bundled validator. ## Manual verification Create a local plugin with both `interface.logo` and `interface.logoDark`, then call `plugin/list` or `plugin/read`. Confirm the response contains separate `logo` and `logoDark` paths. For a remote catalog entry, confirm `logoUrlDark` is populated from `logo_url_dark`. For an uninstalled Git marketplace entry, confirm path-backed interface assets remain absent until installation. Issue: N/A - coordinated maintainer change.
767 lines
17 KiB
JSON
Generated
767 lines
17 KiB
JSON
Generated
{
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"definitions": {
|
|
"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"
|
|
},
|
|
"AppSummary": {
|
|
"description": "EXPERIMENTAL - app metadata summary for plugin responses.",
|
|
"properties": {
|
|
"category": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"description": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"id": {
|
|
"type": "string"
|
|
},
|
|
"installUrl": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"required": [
|
|
"id",
|
|
"name"
|
|
],
|
|
"type": "object"
|
|
},
|
|
"AppTemplateSummary": {
|
|
"properties": {
|
|
"canonicalConnectorId": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"category": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"description": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"logoUrl": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"logoUrlDark": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"materializedAppIds": {
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"type": "array"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"reason": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/AppTemplateUnavailableReason"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
]
|
|
},
|
|
"templateId": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"required": [
|
|
"materializedAppIds",
|
|
"name",
|
|
"templateId"
|
|
],
|
|
"type": "object"
|
|
},
|
|
"AppTemplateUnavailableReason": {
|
|
"enum": [
|
|
"NOT_CONFIGURED_FOR_WORKSPACE",
|
|
"NO_ACTIVE_WORKSPACE"
|
|
],
|
|
"type": "string"
|
|
},
|
|
"HookEventName": {
|
|
"enum": [
|
|
"preToolUse",
|
|
"permissionRequest",
|
|
"postToolUse",
|
|
"preCompact",
|
|
"postCompact",
|
|
"sessionStart",
|
|
"userPromptSubmit",
|
|
"subagentStart",
|
|
"subagentStop",
|
|
"stop"
|
|
],
|
|
"type": "string"
|
|
},
|
|
"PluginAuthPolicy": {
|
|
"enum": [
|
|
"ON_INSTALL",
|
|
"ON_USE"
|
|
],
|
|
"type": "string"
|
|
},
|
|
"PluginAvailability": {
|
|
"oneOf": [
|
|
{
|
|
"enum": [
|
|
"DISABLED_BY_ADMIN"
|
|
],
|
|
"type": "string"
|
|
},
|
|
{
|
|
"description": "Plugin-service currently sends `\"ENABLED\"` for available remote plugins. Codex app-server exposes `\"AVAILABLE\"` in its API; the alias keeps decoding compatible with that upstream response.",
|
|
"enum": [
|
|
"AVAILABLE"
|
|
],
|
|
"type": "string"
|
|
}
|
|
]
|
|
},
|
|
"PluginDetail": {
|
|
"properties": {
|
|
"appTemplates": {
|
|
"items": {
|
|
"$ref": "#/definitions/AppTemplateSummary"
|
|
},
|
|
"type": "array"
|
|
},
|
|
"apps": {
|
|
"items": {
|
|
"$ref": "#/definitions/AppSummary"
|
|
},
|
|
"type": "array"
|
|
},
|
|
"description": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"hooks": {
|
|
"items": {
|
|
"$ref": "#/definitions/PluginHookSummary"
|
|
},
|
|
"type": "array"
|
|
},
|
|
"marketplaceName": {
|
|
"type": "string"
|
|
},
|
|
"marketplacePath": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/AbsolutePathBuf"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
]
|
|
},
|
|
"mcpServers": {
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"type": "array"
|
|
},
|
|
"shareUrl": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"skills": {
|
|
"items": {
|
|
"$ref": "#/definitions/SkillSummary"
|
|
},
|
|
"type": "array"
|
|
},
|
|
"summary": {
|
|
"$ref": "#/definitions/PluginSummary"
|
|
}
|
|
},
|
|
"required": [
|
|
"appTemplates",
|
|
"apps",
|
|
"hooks",
|
|
"marketplaceName",
|
|
"mcpServers",
|
|
"skills",
|
|
"summary"
|
|
],
|
|
"type": "object"
|
|
},
|
|
"PluginHookSummary": {
|
|
"properties": {
|
|
"eventName": {
|
|
"$ref": "#/definitions/HookEventName"
|
|
},
|
|
"key": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"required": [
|
|
"eventName",
|
|
"key"
|
|
],
|
|
"type": "object"
|
|
},
|
|
"PluginInstallPolicy": {
|
|
"enum": [
|
|
"NOT_AVAILABLE",
|
|
"AVAILABLE",
|
|
"INSTALLED_BY_DEFAULT"
|
|
],
|
|
"type": "string"
|
|
},
|
|
"PluginInterface": {
|
|
"properties": {
|
|
"brandColor": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"capabilities": {
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"type": "array"
|
|
},
|
|
"category": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"composerIcon": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/AbsolutePathBuf"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"description": "Local composer icon path, resolved from the installed plugin package."
|
|
},
|
|
"composerIconUrl": {
|
|
"description": "Remote composer icon URL from the plugin catalog.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"defaultPrompt": {
|
|
"description": "Starter prompts for the plugin. Capped at 3 entries with a maximum of 128 characters per entry.",
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"type": [
|
|
"array",
|
|
"null"
|
|
]
|
|
},
|
|
"developerName": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"displayName": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"logo": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/AbsolutePathBuf"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"description": "Local logo path, resolved from the installed plugin package."
|
|
},
|
|
"logoDark": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/AbsolutePathBuf"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"description": "Local dark-mode logo path, resolved from the installed plugin package."
|
|
},
|
|
"logoUrl": {
|
|
"description": "Remote logo URL from the plugin catalog.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"logoUrlDark": {
|
|
"description": "Remote dark-mode logo URL from the plugin catalog.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"longDescription": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"privacyPolicyUrl": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"screenshotUrls": {
|
|
"description": "Remote screenshot URLs from the plugin catalog.",
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"type": "array"
|
|
},
|
|
"screenshots": {
|
|
"description": "Local screenshot paths, resolved from the installed plugin package.",
|
|
"items": {
|
|
"$ref": "#/definitions/AbsolutePathBuf"
|
|
},
|
|
"type": "array"
|
|
},
|
|
"shortDescription": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"termsOfServiceUrl": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"websiteUrl": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
},
|
|
"required": [
|
|
"capabilities",
|
|
"screenshotUrls",
|
|
"screenshots"
|
|
],
|
|
"type": "object"
|
|
},
|
|
"PluginShareContext": {
|
|
"properties": {
|
|
"creatorAccountUserId": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"creatorName": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"discoverability": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/PluginShareDiscoverability"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
]
|
|
},
|
|
"remotePluginId": {
|
|
"type": "string"
|
|
},
|
|
"remoteVersion": {
|
|
"default": null,
|
|
"description": "Version of the remote shared plugin release when available.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"sharePrincipals": {
|
|
"items": {
|
|
"$ref": "#/definitions/PluginSharePrincipal"
|
|
},
|
|
"type": [
|
|
"array",
|
|
"null"
|
|
]
|
|
},
|
|
"shareUrl": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
},
|
|
"required": [
|
|
"remotePluginId"
|
|
],
|
|
"type": "object"
|
|
},
|
|
"PluginShareDiscoverability": {
|
|
"enum": [
|
|
"LISTED",
|
|
"UNLISTED",
|
|
"PRIVATE"
|
|
],
|
|
"type": "string"
|
|
},
|
|
"PluginSharePrincipal": {
|
|
"properties": {
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"principalId": {
|
|
"type": "string"
|
|
},
|
|
"principalType": {
|
|
"$ref": "#/definitions/PluginSharePrincipalType"
|
|
},
|
|
"role": {
|
|
"$ref": "#/definitions/PluginSharePrincipalRole"
|
|
}
|
|
},
|
|
"required": [
|
|
"name",
|
|
"principalId",
|
|
"principalType",
|
|
"role"
|
|
],
|
|
"type": "object"
|
|
},
|
|
"PluginSharePrincipalRole": {
|
|
"enum": [
|
|
"reader",
|
|
"editor",
|
|
"owner"
|
|
],
|
|
"type": "string"
|
|
},
|
|
"PluginSharePrincipalType": {
|
|
"enum": [
|
|
"user",
|
|
"group",
|
|
"workspace"
|
|
],
|
|
"type": "string"
|
|
},
|
|
"PluginSource": {
|
|
"oneOf": [
|
|
{
|
|
"properties": {
|
|
"path": {
|
|
"$ref": "#/definitions/AbsolutePathBuf"
|
|
},
|
|
"type": {
|
|
"enum": [
|
|
"local"
|
|
],
|
|
"title": "LocalPluginSourceType",
|
|
"type": "string"
|
|
}
|
|
},
|
|
"required": [
|
|
"path",
|
|
"type"
|
|
],
|
|
"title": "LocalPluginSource",
|
|
"type": "object"
|
|
},
|
|
{
|
|
"properties": {
|
|
"path": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"refName": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"sha": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"type": {
|
|
"enum": [
|
|
"git"
|
|
],
|
|
"title": "GitPluginSourceType",
|
|
"type": "string"
|
|
},
|
|
"url": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"required": [
|
|
"type",
|
|
"url"
|
|
],
|
|
"title": "GitPluginSource",
|
|
"type": "object"
|
|
},
|
|
{
|
|
"description": "The plugin is available in the remote catalog. Download metadata is kept server-side and is not exposed through the app-server API.",
|
|
"properties": {
|
|
"type": {
|
|
"enum": [
|
|
"remote"
|
|
],
|
|
"title": "RemotePluginSourceType",
|
|
"type": "string"
|
|
}
|
|
},
|
|
"required": [
|
|
"type"
|
|
],
|
|
"title": "RemotePluginSource",
|
|
"type": "object"
|
|
}
|
|
]
|
|
},
|
|
"PluginSummary": {
|
|
"properties": {
|
|
"authPolicy": {
|
|
"$ref": "#/definitions/PluginAuthPolicy"
|
|
},
|
|
"availability": {
|
|
"allOf": [
|
|
{
|
|
"$ref": "#/definitions/PluginAvailability"
|
|
}
|
|
],
|
|
"default": "AVAILABLE",
|
|
"description": "Availability state for installing and using the plugin."
|
|
},
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"id": {
|
|
"type": "string"
|
|
},
|
|
"installPolicy": {
|
|
"$ref": "#/definitions/PluginInstallPolicy"
|
|
},
|
|
"installed": {
|
|
"type": "boolean"
|
|
},
|
|
"interface": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/PluginInterface"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
]
|
|
},
|
|
"keywords": {
|
|
"default": [],
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"type": "array"
|
|
},
|
|
"localVersion": {
|
|
"default": null,
|
|
"description": "Version of the locally materialized plugin package when available.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"remotePluginId": {
|
|
"description": "Backend remote plugin identifier when available.",
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"shareContext": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/PluginShareContext"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
],
|
|
"description": "Remote sharing context associated with this plugin when available."
|
|
},
|
|
"source": {
|
|
"$ref": "#/definitions/PluginSource"
|
|
}
|
|
},
|
|
"required": [
|
|
"authPolicy",
|
|
"enabled",
|
|
"id",
|
|
"installPolicy",
|
|
"installed",
|
|
"name",
|
|
"source"
|
|
],
|
|
"type": "object"
|
|
},
|
|
"SkillInterface": {
|
|
"properties": {
|
|
"brandColor": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"defaultPrompt": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"displayName": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
},
|
|
"iconLarge": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/AbsolutePathBuf"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
]
|
|
},
|
|
"iconSmall": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/AbsolutePathBuf"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
]
|
|
},
|
|
"shortDescription": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
},
|
|
"type": "object"
|
|
},
|
|
"SkillSummary": {
|
|
"properties": {
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"interface": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/SkillInterface"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
]
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"path": {
|
|
"anyOf": [
|
|
{
|
|
"$ref": "#/definitions/AbsolutePathBuf"
|
|
},
|
|
{
|
|
"type": "null"
|
|
}
|
|
]
|
|
},
|
|
"shortDescription": {
|
|
"type": [
|
|
"string",
|
|
"null"
|
|
]
|
|
}
|
|
},
|
|
"required": [
|
|
"description",
|
|
"enabled",
|
|
"name"
|
|
],
|
|
"type": "object"
|
|
}
|
|
},
|
|
"properties": {
|
|
"plugin": {
|
|
"$ref": "#/definitions/PluginDetail"
|
|
}
|
|
},
|
|
"required": [
|
|
"plugin"
|
|
],
|
|
"title": "PluginReadResponse",
|
|
"type": "object"
|
|
} |