mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Expose plugin installation timestamps in app-server summaries (#35859)
## What changed - Add nullable `installedAt` metadata to `PluginSummary` and its generated schemas, expressed as a Unix timestamp in seconds. - Preserve the backend installation time for remote plugins across plugin list, installed, read, and share-list responses. Return `null` when the timestamp is unavailable, including for local or uninstalled plugins and older backend responses. ## Testing - Cover timestamp conversion in the remote plugin list integration test and the field's serialization and backward-compatible default in protocol tests. GitOrigin-RevId: 8a1640320adb298874e1074e9891d1017ad06937
This commit is contained in:
1 parent
b9b7c21821
commit
28f3f1f9ef
19 files changed
+103
-2
No files matched your search
+9
@@ -15143,6 +15143,15 @@
|
|||||||
"installed": {
|
"installed": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"installedAt": {
|
||||||
|
"default": null,
|
||||||
|
"description": "Unix timestamp in seconds when the remote plugin was installed, when available.",
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"interface": {
|
"interface": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
|
|||||||
+9
@@ -11485,6 +11485,15 @@
|
|||||||
"installed": {
|
"installed": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"installedAt": {
|
||||||
|
"default": null,
|
||||||
|
"description": "Unix timestamp in seconds when the remote plugin was installed, when available.",
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"interface": {
|
"interface": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -548,6 +548,15 @@
|
|||||||
"installed": {
|
"installed": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"installedAt": {
|
||||||
|
"default": null,
|
||||||
|
"description": "Unix timestamp in seconds when the remote plugin was installed, when available.",
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"interface": {
|
"interface": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -548,6 +548,15 @@
|
|||||||
"installed": {
|
"installed": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"installedAt": {
|
||||||
|
"default": null,
|
||||||
|
"description": "Unix timestamp in seconds when the remote plugin was installed, when available.",
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"interface": {
|
"interface": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -698,6 +698,15 @@
|
|||||||
"installed": {
|
"installed": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"installedAt": {
|
||||||
|
"default": null,
|
||||||
|
"description": "Unix timestamp in seconds when the remote plugin was installed, when available.",
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"interface": {
|
"interface": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -504,6 +504,15 @@
|
|||||||
"installed": {
|
"installed": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"installedAt": {
|
||||||
|
"default": null,
|
||||||
|
"description": "Unix timestamp in seconds when the remote plugin was installed, when available.",
|
||||||
|
"format": "int64",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"interface": {
|
"interface": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ localVersion: string | null, name: string,
|
|||||||
/**
|
/**
|
||||||
* Remote sharing context associated with this plugin when available.
|
* Remote sharing context associated with this plugin when available.
|
||||||
*/
|
*/
|
||||||
shareContext: PluginShareContext | null, source: PluginSource, installed: boolean, enabled: boolean, installPolicy: PluginInstallPolicy, installPolicySource: PluginInstallPolicySource | null, mustShowInstallationInterstitial: boolean | null, authPolicy: PluginAuthPolicy,
|
shareContext: PluginShareContext | null, source: PluginSource, installed: boolean,
|
||||||
|
/**
|
||||||
|
* Unix timestamp in seconds when the remote plugin was installed, when available.
|
||||||
|
*/
|
||||||
|
installedAt: number | null, enabled: boolean, installPolicy: PluginInstallPolicy, installPolicySource: PluginInstallPolicySource | null, mustShowInstallationInterstitial: boolean | null, authPolicy: PluginAuthPolicy,
|
||||||
/**
|
/**
|
||||||
* Availability state for installing and using the plugin.
|
* Availability state for installing and using the plugin.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -648,6 +648,10 @@ pub struct PluginSummary {
|
|||||||
pub share_context: Option<PluginShareContext>,
|
pub share_context: Option<PluginShareContext>,
|
||||||
pub source: PluginSource,
|
pub source: PluginSource,
|
||||||
pub installed: bool,
|
pub installed: bool,
|
||||||
|
/// Unix timestamp in seconds when the remote plugin was installed, when available.
|
||||||
|
#[serde(default)]
|
||||||
|
#[ts(type = "number | null")]
|
||||||
|
pub installed_at: Option<i64>,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub install_policy: PluginInstallPolicy,
|
pub install_policy: PluginInstallPolicy,
|
||||||
pub install_policy_source: Option<PluginInstallPolicySource>,
|
pub install_policy_source: Option<PluginInstallPolicySource>,
|
||||||
|
|||||||
@@ -4009,6 +4009,7 @@ fn plugin_share_list_response_serializes_share_items() {
|
|||||||
share_context: None,
|
share_context: None,
|
||||||
source: PluginSource::Remote,
|
source: PluginSource::Remote,
|
||||||
installed: false,
|
installed: false,
|
||||||
|
installed_at: None,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
install_policy: PluginInstallPolicy::Available,
|
install_policy: PluginInstallPolicy::Available,
|
||||||
install_policy_source: Some(PluginInstallPolicySource::WorkspaceSetting),
|
install_policy_source: Some(PluginInstallPolicySource::WorkspaceSetting),
|
||||||
@@ -4035,6 +4036,7 @@ fn plugin_share_list_response_serializes_share_items() {
|
|||||||
"shareContext": null,
|
"shareContext": null,
|
||||||
"source": { "type": "remote" },
|
"source": { "type": "remote" },
|
||||||
"installed": false,
|
"installed": false,
|
||||||
|
"installedAt": null,
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"installPolicy": "AVAILABLE",
|
"installPolicy": "AVAILABLE",
|
||||||
"installPolicySource": "WORKSPACE_SETTING",
|
"installPolicySource": "WORKSPACE_SETTING",
|
||||||
@@ -4067,6 +4069,7 @@ fn plugin_summary_defaults_missing_availability_to_available() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(summary.availability, PluginAvailability::Available);
|
assert_eq!(summary.availability, PluginAvailability::Available);
|
||||||
|
assert_eq!(summary.installed_at, None);
|
||||||
assert_eq!(summary.local_version, None);
|
assert_eq!(summary.local_version, None);
|
||||||
assert_eq!(summary.share_context, None);
|
assert_eq!(summary.share_context, None);
|
||||||
assert_eq!(summary.must_show_installation_interstitial, None);
|
assert_eq!(summary.must_show_installation_interstitial, None);
|
||||||
@@ -4085,6 +4088,7 @@ fn plugin_summary_round_trips_plan_eligibility_metadata() {
|
|||||||
"shareContext": null,
|
"shareContext": null,
|
||||||
"source": { "type": "remote" },
|
"source": { "type": "remote" },
|
||||||
"installed": false,
|
"installed": false,
|
||||||
|
"installedAt": null,
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"installPolicy": "NOT_AVAILABLE",
|
"installPolicy": "NOT_AVAILABLE",
|
||||||
"installPolicySource": null,
|
"installPolicySource": null,
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ Example with notification opt-out:
|
|||||||
- `marketplace/remove` — remove a configured marketplace by name from the user marketplace config, and delete its installed marketplace root when one exists.
|
- `marketplace/remove` — remove a configured marketplace by name from the user marketplace config, and delete its installed marketplace root when one exists.
|
||||||
- `marketplace/upgrade` — upgrade all configured Git plugin marketplaces, or one named marketplace when `marketplaceName` is provided. Returns selected marketplace names, upgraded roots, and per-marketplace errors.
|
- `marketplace/upgrade` — upgrade all configured Git plugin marketplaces, or one named marketplace when `marketplaceName` is provided. Returns selected marketplace names, upgraded roots, and per-marketplace errors.
|
||||||
- `plugin/list` — list discovered plugin marketplaces and plugin state, including effective marketplace install/auth policy metadata, nullable remote install-policy provenance in `installPolicySource` (`WORKSPACE_SETTING` or `IMPLICIT_CANONICAL_APP`), the remote marketplace `version` and locally materialized `localVersion` when available, plugin `availability` (`AVAILABLE` by default or `DISABLED_BY_ADMIN` for remote plugins blocked upstream), fail-open `marketplaceLoadErrors` entries for marketplace files that could not be parsed or loaded, and best-effort `featuredPluginIds` for the official curated marketplace. Every `PluginSummary` returned by plugin list, installed, read, and share-list methods includes nullable `disabledReason` and `eligiblePlanTypes`, preserving plugin-service availability metadata and raw plan identifiers for remote plugins while returning `null` for local plugins or older remote responses. The same summaries include `mustShowInstallationInterstitial`: remote service values preserve `true` or `false`, while local plugins and remote responses that omit the policy return `null`. Clients should fail closed when the value is `null`. Clients can explicitly request the remote `workspace-directory`, `shared-with-me`, or `created-by-me-remote` marketplace kinds. Set `forceRefetch: true` to bypass TTL-backed remote catalog caches for the requested marketplaces and wait for fresh data; cache entries are replaced only after a successful fetch. When local marketplaces are included, the request also waits for configured plugin caches to reconcile before marketplace summaries are returned. At app-server startup, existing cached catalogs remain available to `plugin/list` while they refresh in the background. `interface.category` uses the marketplace category when present; otherwise it falls back to the plugin manifest category (**under development; do not call from production clients yet**).
|
- `plugin/list` — list discovered plugin marketplaces and plugin state, including effective marketplace install/auth policy metadata, nullable remote install-policy provenance in `installPolicySource` (`WORKSPACE_SETTING` or `IMPLICIT_CANONICAL_APP`), the remote marketplace `version` and locally materialized `localVersion` when available, plugin `availability` (`AVAILABLE` by default or `DISABLED_BY_ADMIN` for remote plugins blocked upstream), fail-open `marketplaceLoadErrors` entries for marketplace files that could not be parsed or loaded, and best-effort `featuredPluginIds` for the official curated marketplace. Every `PluginSummary` returned by plugin list, installed, read, and share-list methods includes nullable `disabledReason` and `eligiblePlanTypes`, preserving plugin-service availability metadata and raw plan identifiers for remote plugins while returning `null` for local plugins or older remote responses. The same summaries include `mustShowInstallationInterstitial`: remote service values preserve `true` or `false`, while local plugins and remote responses that omit the policy return `null`. Clients should fail closed when the value is `null`. Clients can explicitly request the remote `workspace-directory`, `shared-with-me`, or `created-by-me-remote` marketplace kinds. Set `forceRefetch: true` to bypass TTL-backed remote catalog caches for the requested marketplaces and wait for fresh data; cache entries are replaced only after a successful fetch. When local marketplaces are included, the request also waits for configured plugin caches to reconcile before marketplace summaries are returned. At app-server startup, existing cached catalogs remain available to `plugin/list` while they refresh in the background. `interface.category` uses the marketplace category when present; otherwise it falls back to the plugin manifest category (**under development; do not call from production clients yet**).
|
||||||
- `plugin/installed` — list installed plugin rows plus any explicitly requested local install-suggestion plugin names, without fetching the broader remote catalog. Remote rows include nullable `installPolicySource`; local rows return `null`. Mention surfaces can use this narrower view when they need plugin mention payloads rather than plugin-page discovery data (**under development; do not call from production clients yet**).
|
- `plugin/installed` — list installed plugin rows plus any explicitly requested local install-suggestion plugin names, without fetching the broader remote catalog. Remote rows include nullable `installPolicySource` and `installedAt`, the backend installation timestamp in Unix seconds. `installedAt` is also returned by `plugin/list`, `plugin/read`, and `plugin/share/list`; it is `null` for local plugins, uninstalled plugins, plugins installed by default, and older backend responses that do not include an installation timestamp. Mention surfaces can use this narrower view when they need plugin mention payloads rather than plugin-page discovery data (**under development; do not call from production clients yet**).
|
||||||
- `plugin/read` — read one plugin by `marketplacePath` plus `pluginName`, returning marketplace info, a list-style `summary`, manifest descriptions/interface metadata, and bundled skills/hooks/apps/MCP server names. Remote plugin details can include scheduled task summaries from the catalog; `scheduledTasks: null` means the metadata is unavailable, while an empty array means the catalog found no scheduled tasks. Remote plugin details expose the canonical `shareUrl` supplied by the remote catalog when available; it is `null` for local plugins or when the catalog omits it. This field is separate from `summary.shareContext`, which continues to describe user and workspace sharing state. For owned workspace plugins, `summary.shareContext.canPublishToWorkspace` reports whether the current user may add the plugin to the workspace directory; `plugin/share/save` returns the same capability after creating or updating a share, and clients should fail closed when either value is `null`. Remote skill interfaces expose `iconSmallUrl` and `iconLargeUrl` when the catalog supplies icon URLs. Returned plugin skills include their current `enabled` state after local config filtering; bundled hooks are returned as lightweight declaration summaries keyed for correlation with `hooks/list`. Use `plugin/install`'s `appsNeedingAuth` to drive post-install authentication and `app/list`'s `isAccessible` to determine current connector accessibility (**under development; do not call from production clients yet**).
|
- `plugin/read` — read one plugin by `marketplacePath` plus `pluginName`, returning marketplace info, a list-style `summary`, manifest descriptions/interface metadata, and bundled skills/hooks/apps/MCP server names. Remote plugin details can include scheduled task summaries from the catalog; `scheduledTasks: null` means the metadata is unavailable, while an empty array means the catalog found no scheduled tasks. Remote plugin details expose the canonical `shareUrl` supplied by the remote catalog when available; it is `null` for local plugins or when the catalog omits it. This field is separate from `summary.shareContext`, which continues to describe user and workspace sharing state. For owned workspace plugins, `summary.shareContext.canPublishToWorkspace` reports whether the current user may add the plugin to the workspace directory; `plugin/share/save` returns the same capability after creating or updating a share, and clients should fail closed when either value is `null`. Remote skill interfaces expose `iconSmallUrl` and `iconLargeUrl` when the catalog supplies icon URLs. Returned plugin skills include their current `enabled` state after local config filtering; bundled hooks are returned as lightweight declaration summaries keyed for correlation with `hooks/list`. Use `plugin/install`'s `appsNeedingAuth` to drive post-install authentication and `app/list`'s `isAccessible` to determine current connector accessibility (**under development; do not call from production clients yet**).
|
||||||
- `plugin/skill/read` — read remote plugin skill markdown on demand by `remoteMarketplaceName`, `remotePluginId`, and `skillName`. This lets clients preview uninstalled remote plugin skills without downloading the plugin bundle.
|
- `plugin/skill/read` — read remote plugin skill markdown on demand by `remoteMarketplaceName`, `remotePluginId`, and `skillName`. This lets clients preview uninstalled remote plugin skills without downloading the plugin bundle.
|
||||||
- `skills/changed` — notification emitted when watched local skill files change.
|
- `skills/changed` — notification emitted when watched local skill files change.
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ fn convert_configured_marketplace_plugin_to_plugin_summary(
|
|||||||
version: None,
|
version: None,
|
||||||
local_version: plugin.local_version,
|
local_version: plugin.local_version,
|
||||||
installed: plugin.installed,
|
installed: plugin.installed,
|
||||||
|
installed_at: None,
|
||||||
enabled: plugin.enabled,
|
enabled: plugin.enabled,
|
||||||
name: plugin.name,
|
name: plugin.name,
|
||||||
share_context,
|
share_context,
|
||||||
@@ -1128,6 +1129,7 @@ impl PluginRequestProcessor {
|
|||||||
share_context,
|
share_context,
|
||||||
source: marketplace_plugin_source_to_info(outcome.plugin.source),
|
source: marketplace_plugin_source_to_info(outcome.plugin.source),
|
||||||
installed: outcome.plugin.installed,
|
installed: outcome.plugin.installed,
|
||||||
|
installed_at: None,
|
||||||
enabled: outcome.plugin.enabled,
|
enabled: outcome.plugin.enabled,
|
||||||
install_policy: outcome.plugin.policy.installation.into(),
|
install_policy: outcome.plugin.policy.installation.into(),
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
@@ -2240,6 +2242,9 @@ fn remote_plugin_summary_to_info(summary: RemoteCatalogPluginSummary) -> PluginS
|
|||||||
.map(remote_plugin_share_context_to_info),
|
.map(remote_plugin_share_context_to_info),
|
||||||
source: PluginSource::Remote,
|
source: PluginSource::Remote,
|
||||||
installed: summary.installed,
|
installed: summary.installed,
|
||||||
|
installed_at: summary
|
||||||
|
.installed_at
|
||||||
|
.map(|installed_at| installed_at.timestamp()),
|
||||||
enabled: summary.enabled,
|
enabled: summary.enabled,
|
||||||
install_policy: summary.install_policy,
|
install_policy: summary.install_policy,
|
||||||
install_policy_source: summary.install_policy_source,
|
install_policy_source: summary.install_policy_source,
|
||||||
|
|||||||
@@ -586,6 +586,7 @@ async fn plugin_list_keeps_valid_marketplaces_when_another_marketplace_fails_to_
|
|||||||
path: valid_plugin_path,
|
path: valid_plugin_path,
|
||||||
},
|
},
|
||||||
installed: false,
|
installed: false,
|
||||||
|
installed_at: None,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
install_policy: PluginInstallPolicy::Available,
|
install_policy: PluginInstallPolicy::Available,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
@@ -875,6 +876,7 @@ async fn plugin_list_uses_alternate_discoverable_manifest_and_keeps_undiscoverab
|
|||||||
path: valid_plugin_path,
|
path: valid_plugin_path,
|
||||||
},
|
},
|
||||||
installed: false,
|
installed: false,
|
||||||
|
installed_at: None,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
install_policy: PluginInstallPolicy::Available,
|
install_policy: PluginInstallPolicy::Available,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
@@ -919,6 +921,7 @@ async fn plugin_list_uses_alternate_discoverable_manifest_and_keeps_undiscoverab
|
|||||||
)?,
|
)?,
|
||||||
},
|
},
|
||||||
installed: false,
|
installed: false,
|
||||||
|
installed_at: None,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
install_policy: PluginInstallPolicy::Available,
|
install_policy: PluginInstallPolicy::Available,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
@@ -1968,6 +1971,7 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
|
|||||||
"scope": "GLOBAL",
|
"scope": "GLOBAL",
|
||||||
"installation_policy": "AVAILABLE",
|
"installation_policy": "AVAILABLE",
|
||||||
"installation_policy_source": "WORKSPACE_SETTING",
|
"installation_policy_source": "WORKSPACE_SETTING",
|
||||||
|
"installed_at": "2026-01-02T00:00:00Z",
|
||||||
"must_show_installation_interstitial": false,
|
"must_show_installation_interstitial": false,
|
||||||
"authentication_policy": "ON_USE",
|
"authentication_policy": "ON_USE",
|
||||||
"status": "ENABLED",
|
"status": "ENABLED",
|
||||||
@@ -2091,6 +2095,10 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
|
|||||||
Some("1.2.3")
|
Some("1.2.3")
|
||||||
);
|
);
|
||||||
assert_eq!(remote_marketplace.plugins[0].installed, true);
|
assert_eq!(remote_marketplace.plugins[0].installed, true);
|
||||||
|
assert_eq!(
|
||||||
|
remote_marketplace.plugins[0].installed_at,
|
||||||
|
Some(1_767_312_000)
|
||||||
|
);
|
||||||
assert_eq!(remote_marketplace.plugins[0].enabled, true);
|
assert_eq!(remote_marketplace.plugins[0].enabled, true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
remote_marketplace.plugins[0].install_policy_source,
|
remote_marketplace.plugins[0].install_policy_source,
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ async fn plugin_share_save_uploads_local_plugin() -> Result<()> {
|
|||||||
share_context: Some(expected_share_context("plugins_123")),
|
share_context: Some(expected_share_context("plugins_123")),
|
||||||
source: PluginSource::Remote,
|
source: PluginSource::Remote,
|
||||||
installed: true,
|
installed: true,
|
||||||
|
installed_at: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
install_policy: PluginInstallPolicy::Available,
|
install_policy: PluginInstallPolicy::Available,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
@@ -591,6 +592,7 @@ async fn plugin_share_list_returns_created_workspace_plugins() -> Result<()> {
|
|||||||
share_context: Some(expected_share_context("plugins_123")),
|
share_context: Some(expected_share_context("plugins_123")),
|
||||||
source: PluginSource::Remote,
|
source: PluginSource::Remote,
|
||||||
installed: true,
|
installed: true,
|
||||||
|
installed_at: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
install_policy: PluginInstallPolicy::Available,
|
install_policy: PluginInstallPolicy::Available,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
@@ -1253,6 +1255,7 @@ async fn plugin_share_delete_removes_created_workspace_plugin() -> Result<()> {
|
|||||||
share_context: Some(expected_share_context("plugins_123")),
|
share_context: Some(expected_share_context("plugins_123")),
|
||||||
source: PluginSource::Remote,
|
source: PluginSource::Remote,
|
||||||
installed: true,
|
installed: true,
|
||||||
|
installed_at: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
install_policy: PluginInstallPolicy::Available,
|
install_policy: PluginInstallPolicy::Available,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
|
|||||||
@@ -830,6 +830,7 @@ fn remote_installed_plugin_in_marketplace(
|
|||||||
id: format!("plugins~Plugin_{name}"),
|
id: format!("plugins~Plugin_{name}"),
|
||||||
version: None,
|
version: None,
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
|
installed_at: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
install_policy: codex_app_server_protocol::PluginInstallPolicy::Available,
|
install_policy: codex_app_server_protocol::PluginInstallPolicy::Available,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ use crate::http_client_selector::HttpClientSelector;
|
|||||||
use crate::loader::plugin_app_declarations_from_value;
|
use crate::loader::plugin_app_declarations_from_value;
|
||||||
use crate::store::PLUGINS_CACHE_DIR;
|
use crate::store::PLUGINS_CACHE_DIR;
|
||||||
use crate::store::PluginStore;
|
use crate::store::PluginStore;
|
||||||
|
use chrono::DateTime;
|
||||||
|
use chrono::Utc;
|
||||||
use codex_app_server_protocol::JSONRPCErrorError;
|
use codex_app_server_protocol::JSONRPCErrorError;
|
||||||
use codex_app_server_protocol::PluginAuthPolicy;
|
use codex_app_server_protocol::PluginAuthPolicy;
|
||||||
use codex_app_server_protocol::PluginAvailability;
|
use codex_app_server_protocol::PluginAvailability;
|
||||||
@@ -208,6 +210,7 @@ pub struct RemoteInstalledPlugin {
|
|||||||
pub id: String,
|
pub id: String,
|
||||||
pub version: Option<String>,
|
pub version: Option<String>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub installed_at: Option<DateTime<Utc>>,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub install_policy: PluginInstallPolicy,
|
pub install_policy: PluginInstallPolicy,
|
||||||
pub install_policy_source: Option<PluginInstallPolicySource>,
|
pub install_policy_source: Option<PluginInstallPolicySource>,
|
||||||
@@ -229,6 +232,7 @@ pub struct RemotePluginSummary {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub share_context: Option<RemotePluginShareContext>,
|
pub share_context: Option<RemotePluginShareContext>,
|
||||||
pub installed: bool,
|
pub installed: bool,
|
||||||
|
pub installed_at: Option<DateTime<Utc>>,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub install_policy: PluginInstallPolicy,
|
pub install_policy: PluginInstallPolicy,
|
||||||
pub install_policy_source: Option<PluginInstallPolicySource>,
|
pub install_policy_source: Option<PluginInstallPolicySource>,
|
||||||
@@ -689,6 +693,8 @@ struct RemotePluginDirectorySharePrincipal {
|
|||||||
struct RemotePluginInstalledItem {
|
struct RemotePluginInstalledItem {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
plugin: RemotePluginDirectoryItem,
|
plugin: RemotePluginDirectoryItem,
|
||||||
|
#[serde(default)]
|
||||||
|
installed_at: Option<DateTime<Utc>>,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
disabled_skill_names: Vec<String>,
|
disabled_skill_names: Vec<String>,
|
||||||
@@ -1204,6 +1210,7 @@ pub fn group_remote_installed_plugins_by_marketplaces(
|
|||||||
name: plugin.name.clone(),
|
name: plugin.name.clone(),
|
||||||
share_context: None,
|
share_context: None,
|
||||||
installed: true,
|
installed: true,
|
||||||
|
installed_at: plugin.installed_at,
|
||||||
enabled: plugin.enabled,
|
enabled: plugin.enabled,
|
||||||
install_policy: plugin.install_policy,
|
install_policy: plugin.install_policy,
|
||||||
install_policy_source: plugin.install_policy_source,
|
install_policy_source: plugin.install_policy_source,
|
||||||
@@ -1638,6 +1645,7 @@ fn build_remote_plugin_summary(
|
|||||||
name: plugin.name.clone(),
|
name: plugin.name.clone(),
|
||||||
share_context: remote_plugin_share_context(plugin)?,
|
share_context: remote_plugin_share_context(plugin)?,
|
||||||
installed: installed_plugin.is_some(),
|
installed: installed_plugin.is_some(),
|
||||||
|
installed_at: installed_plugin.and_then(|installed| installed.installed_at),
|
||||||
enabled: installed_plugin.is_some_and(|plugin| plugin.enabled),
|
enabled: installed_plugin.is_some_and(|plugin| plugin.enabled),
|
||||||
install_policy: plugin.installation_policy,
|
install_policy: plugin.installation_policy,
|
||||||
install_policy_source: plugin
|
install_policy_source: plugin
|
||||||
@@ -1724,6 +1732,7 @@ fn remote_installed_plugin_to_cache_entry(
|
|||||||
id: plugin.id.clone(),
|
id: plugin.id.clone(),
|
||||||
version: plugin.release.version.clone(),
|
version: plugin.release.version.clone(),
|
||||||
name: plugin.name.clone(),
|
name: plugin.name.clone(),
|
||||||
|
installed_at: installed_plugin.installed_at,
|
||||||
enabled: installed_plugin.enabled,
|
enabled: installed_plugin.enabled,
|
||||||
install_policy: plugin.installation_policy,
|
install_policy: plugin.installation_policy,
|
||||||
install_policy_source: plugin
|
install_policy_source: plugin
|
||||||
|
|||||||
@@ -665,6 +665,7 @@ async fn list_remote_plugin_shares_fetches_created_workspace_plugins() {
|
|||||||
can_publish_to_workspace: None,
|
can_publish_to_workspace: None,
|
||||||
}),
|
}),
|
||||||
installed: false,
|
installed: false,
|
||||||
|
installed_at: None,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
install_policy: PluginInstallPolicy::Available,
|
install_policy: PluginInstallPolicy::Available,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
@@ -709,6 +710,7 @@ async fn list_remote_plugin_shares_fetches_created_workspace_plugins() {
|
|||||||
can_publish_to_workspace: None,
|
can_publish_to_workspace: None,
|
||||||
}),
|
}),
|
||||||
installed: true,
|
installed: true,
|
||||||
|
installed_at: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
install_policy: PluginInstallPolicy::Available,
|
install_policy: PluginInstallPolicy::Available,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ fn build_remote_marketplace_preserves_directory_order_and_appends_installed_only
|
|||||||
];
|
];
|
||||||
let installed_plugins = vec![RemotePluginInstalledItem {
|
let installed_plugins = vec![RemotePluginInstalledItem {
|
||||||
plugin: directory_plugin("plugin-a", "alpha"),
|
plugin: directory_plugin("plugin-a", "alpha"),
|
||||||
|
installed_at: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
disabled_skill_names: Vec::new(),
|
disabled_skill_names: Vec::new(),
|
||||||
}];
|
}];
|
||||||
@@ -109,6 +110,7 @@ fn installation_policy_source_is_preserved_across_remote_summary_paths() {
|
|||||||
Some(RemotePluginInstallPolicySource::ImplicitCanonicalApp);
|
Some(RemotePluginInstallPolicySource::ImplicitCanonicalApp);
|
||||||
let installed_plugin = RemotePluginInstalledItem {
|
let installed_plugin = RemotePluginInstalledItem {
|
||||||
plugin: directory_plugin.clone(),
|
plugin: directory_plugin.clone(),
|
||||||
|
installed_at: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
disabled_skill_names: Vec::new(),
|
disabled_skill_names: Vec::new(),
|
||||||
};
|
};
|
||||||
@@ -163,6 +165,7 @@ fn plan_eligibility_is_preserved_across_remote_summary_paths() {
|
|||||||
]);
|
]);
|
||||||
let installed_plugin = RemotePluginInstalledItem {
|
let installed_plugin = RemotePluginInstalledItem {
|
||||||
plugin: directory_plugin.clone(),
|
plugin: directory_plugin.clone(),
|
||||||
|
installed_at: None,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
disabled_skill_names: Vec::new(),
|
disabled_skill_names: Vec::new(),
|
||||||
};
|
};
|
||||||
@@ -255,6 +258,7 @@ fn installation_interstitial_requirement_is_preserved_across_remote_summary_path
|
|||||||
directory_plugin.must_show_installation_interstitial = Some(false);
|
directory_plugin.must_show_installation_interstitial = Some(false);
|
||||||
let installed_plugin = remote_installed_plugin_to_cache_entry(&RemotePluginInstalledItem {
|
let installed_plugin = remote_installed_plugin_to_cache_entry(&RemotePluginInstalledItem {
|
||||||
plugin: directory_plugin,
|
plugin: directory_plugin,
|
||||||
|
installed_at: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
disabled_skill_names: Vec::new(),
|
disabled_skill_names: Vec::new(),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ mod tests {
|
|||||||
share_context: None,
|
share_context: None,
|
||||||
source: PluginSource::Remote,
|
source: PluginSource::Remote,
|
||||||
installed: true,
|
installed: true,
|
||||||
|
installed_at: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
install_policy: PluginInstallPolicy::Available,
|
install_policy: PluginInstallPolicy::Available,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
|
|||||||
@@ -1354,6 +1354,7 @@ pub(super) fn plugins_test_summary(
|
|||||||
path: plugins_test_absolute_path(&format!("plugins/{name}")),
|
path: plugins_test_absolute_path(&format!("plugins/{name}")),
|
||||||
},
|
},
|
||||||
installed,
|
installed,
|
||||||
|
installed_at: None,
|
||||||
enabled,
|
enabled,
|
||||||
install_policy,
|
install_policy,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
@@ -1387,6 +1388,7 @@ pub(super) fn plugins_test_remote_summary(
|
|||||||
share_context: None,
|
share_context: None,
|
||||||
source: PluginSource::Remote,
|
source: PluginSource::Remote,
|
||||||
installed,
|
installed,
|
||||||
|
installed_at: None,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
install_policy: PluginInstallPolicy::Available,
|
install_policy: PluginInstallPolicy::Available,
|
||||||
install_policy_source: None,
|
install_policy_source: None,
|
||||||
|
|||||||
Reference in new issue
Block a user