mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Accept forceRefetch in plugin list requests (#34573)
## What changed - Add the optional `forceRefetch` boolean to `PluginListParams` and generated schemas. - Accept both omitted and enabled values without changing `plugin/list` behavior yet. - Cover deserialization of the enabled value in the protocol tests. GitOrigin-RevId: fd4145161e9779638bc0bf18118d5ab5b8478745
This commit is contained in:
@@ -2118,6 +2118,10 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"forceRefetch": {
|
||||
"description": "Whether the client requests a fresh remote plugin catalog fetch.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"marketplaceKinds": {
|
||||
"description": "Optional marketplace kind filter. When omitted, only local marketplaces are queried, plus the default remote catalog when enabled by feature flag.",
|
||||
"items": {
|
||||
|
||||
+4
@@ -14257,6 +14257,10 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"forceRefetch": {
|
||||
"description": "Whether the client requests a fresh remote plugin catalog fetch.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"marketplaceKinds": {
|
||||
"description": "Optional marketplace kind filter. When omitted, only local marketplaces are queried, plus the default remote catalog when enabled by feature flag.",
|
||||
"items": {
|
||||
|
||||
+4
@@ -10599,6 +10599,10 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"forceRefetch": {
|
||||
"description": "Whether the client requests a fresh remote plugin catalog fetch.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"marketplaceKinds": {
|
||||
"description": "Optional marketplace kind filter. When omitted, only local marketplaces are queried, plus the default remote catalog when enabled by feature flag.",
|
||||
"items": {
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"forceRefetch": {
|
||||
"description": "Whether the client requests a fresh remote plugin catalog fetch.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"marketplaceKinds": {
|
||||
"description": "Optional marketplace kind filter. When omitted, only local marketplaces are queried, plus the default remote catalog when enabled by feature flag.",
|
||||
"items": {
|
||||
|
||||
@@ -14,4 +14,8 @@ 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, };
|
||||
marketplaceKinds?: Array<PluginListMarketplaceKind> | null,
|
||||
/**
|
||||
* Whether the client requests a fresh remote plugin catalog fetch.
|
||||
*/
|
||||
forceRefetch?: boolean, };
|
||||
|
||||
@@ -1938,6 +1938,7 @@ mod tests {
|
||||
params: v2::PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
},
|
||||
};
|
||||
assert_eq!(plugin_list.serialization_scope(), None);
|
||||
|
||||
@@ -135,6 +135,9 @@ pub struct PluginListParams {
|
||||
/// the default remote catalog when enabled by feature flag.
|
||||
#[ts(optional = nullable)]
|
||||
pub marketplace_kinds: Option<Vec<PluginListMarketplaceKind>>,
|
||||
/// Whether the client requests a fresh remote plugin catalog fetch.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub force_refetch: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
|
||||
@@ -3354,6 +3354,22 @@ fn plugin_list_params_ignore_removed_force_remote_sync_field() {
|
||||
PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_list_params_deserializes_force_refetch() {
|
||||
assert_eq!(
|
||||
serde_json::from_value::<PluginListParams>(json!({
|
||||
"forceRefetch": true,
|
||||
}))
|
||||
.unwrap(),
|
||||
PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -3370,6 +3386,7 @@ fn plugin_list_params_serializes_marketplace_kind_filter() {
|
||||
PluginListMarketplaceKind::SharedWithMe,
|
||||
PluginListMarketplaceKind::CreatedByMeRemote,
|
||||
]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.unwrap(),
|
||||
json!({
|
||||
|
||||
@@ -216,7 +216,7 @@ Example with notification opt-out:
|
||||
- `marketplace/add` — add a remote plugin marketplace from an HTTP(S) Git URL, SSH Git URL, or GitHub `owner/repo` shorthand, then persist it into the user marketplace config. Returns the installed root path plus whether the marketplace was already present.
|
||||
- `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.
|
||||
- `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 `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. `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 `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. The optional `forceRefetch` request field is currently accepted but has no effect. `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/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. 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.
|
||||
|
||||
@@ -540,6 +540,7 @@ impl PluginRequestProcessor {
|
||||
let PluginListParams {
|
||||
cwds,
|
||||
marketplace_kinds,
|
||||
force_refetch: _,
|
||||
} = params;
|
||||
let roots = cwds.unwrap_or_default();
|
||||
let explicit_marketplace_kinds = marketplace_kinds.is_some();
|
||||
|
||||
@@ -499,6 +499,7 @@ source = {:?}
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
@@ -1473,6 +1474,7 @@ async fn external_agent_config_import_reinstalls_plugins_from_known_marketplaces
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
|
||||
@@ -121,6 +121,7 @@ async fn plugin_list_skips_invalid_marketplace_file_and_reports_error() -> Resul
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: Some(vec![AbsolutePathBuf::try_from(repo_root.path())?]),
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -479,6 +480,7 @@ async fn plugin_list_keeps_valid_marketplaces_when_another_marketplace_fails_to_
|
||||
AbsolutePathBuf::try_from(invalid_repo_root.path())?,
|
||||
]),
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -598,6 +600,7 @@ async fn plugin_list_returns_empty_when_workspace_codex_plugins_disabled() -> Re
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: Some(vec![AbsolutePathBuf::try_from(repo_root.path())?]),
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -692,6 +695,7 @@ async fn plugin_list_reuses_cached_workspace_codex_plugins_setting() -> Result<(
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: Some(vec![AbsolutePathBuf::try_from(repo_root.path())?]),
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -777,6 +781,7 @@ async fn plugin_list_uses_alternate_discoverable_manifest_and_keeps_undiscoverab
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: Some(vec![AbsolutePathBuf::try_from(repo_root.path())?]),
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -899,6 +904,7 @@ async fn plugin_list_accepts_omitted_cwds() -> Result<()> {
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -956,6 +962,7 @@ async fn plugin_list_returns_share_context_for_shared_local_plugin() -> Result<(
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: Some(vec![AbsolutePathBuf::try_from(repo_root.path())?]),
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -1052,6 +1059,7 @@ enabled = false
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: Some(vec![AbsolutePathBuf::try_from(repo_root.path())?]),
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -1210,6 +1218,7 @@ enabled = false
|
||||
AbsolutePathBuf::try_from(workspace_default.path())?,
|
||||
]),
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -1298,6 +1307,7 @@ async fn plugin_list_returns_plugin_interface_with_absolute_asset_paths() -> Res
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: Some(vec![AbsolutePathBuf::try_from(repo_root.path())?]),
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -1415,6 +1425,7 @@ async fn plugin_list_accepts_legacy_string_default_prompt() -> Result<()> {
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: Some(vec![AbsolutePathBuf::try_from(repo_root.path())?]),
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -1507,6 +1518,7 @@ enabled = true
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: Some(vec![AbsolutePathBuf::try_from(repo_root.path())?]),
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -1700,6 +1712,7 @@ async fn plugin_list_sync_upgrades_and_removes_remote_installed_plugin_bundles()
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
@@ -1891,6 +1904,7 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() -
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -2062,6 +2076,7 @@ async fn plugin_list_uses_cached_global_remote_catalog_and_refreshes_it() -> Res
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let response: PluginListResponse = to_response(
|
||||
@@ -2099,6 +2114,7 @@ async fn plugin_list_uses_cached_global_remote_catalog_and_refreshes_it() -> Res
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let response: PluginListResponse = to_response(
|
||||
@@ -2186,6 +2202,7 @@ async fn plugin_list_includes_openai_curated_remote_collection_when_remote_plugi
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: Some(vec![PluginListMarketplaceKind::Vertical]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
@@ -2280,6 +2297,7 @@ async fn plugin_list_propagates_openai_curated_remote_collection_errors_when_rem
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: Some(vec![PluginListMarketplaceKind::Vertical]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let err = timeout(
|
||||
@@ -2324,6 +2342,7 @@ async fn plugin_list_skips_openai_curated_remote_collection_for_api_auth_when_re
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: Some(vec![PluginListMarketplaceKind::Vertical]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
@@ -2367,6 +2386,7 @@ async fn plugin_list_includes_api_curated_marketplace_for_api_auth_when_remote_p
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
@@ -2426,6 +2446,7 @@ async fn plugin_list_does_not_query_openai_curated_remote_collection_by_default(
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
@@ -2483,6 +2504,7 @@ async fn plugin_list_vertical_kind_noops_when_remote_plugin_enabled() -> Result<
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: Some(vec![PluginListMarketplaceKind::Vertical]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
@@ -2541,6 +2563,7 @@ async fn plugin_list_does_not_append_global_remote_when_marketplace_kinds_are_ex
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: Some(vec![PluginListMarketplaceKind::Local]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -3089,6 +3112,7 @@ async fn plugin_list_fetches_workspace_directory_kind_when_remote_plugin_disable
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: Some(vec![PluginListMarketplaceKind::WorkspaceDirectory]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -3219,6 +3243,7 @@ plugin_sharing = false
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: Some(vec![PluginListMarketplaceKind::CreatedByMeRemote]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -3350,6 +3375,7 @@ async fn plugin_list_fetches_shared_with_me_kind() -> Result<()> {
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: Some(vec![PluginListMarketplaceKind::SharedWithMe]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -3511,6 +3537,7 @@ plugin_sharing = false
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: Some(vec![PluginListMarketplaceKind::SharedWithMe]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -3575,6 +3602,7 @@ plugin_sharing = true
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: Some(vec![PluginListMarketplaceKind::CreatedByMeRemote]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -3709,6 +3737,7 @@ async fn plugin_list_marks_remote_plugin_disabled_by_admin() -> Result<()> {
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -3773,6 +3802,7 @@ remote_plugin = true
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -3813,6 +3843,7 @@ async fn plugin_list_fetches_featured_plugin_ids_without_chatgpt_auth() -> Resul
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -3858,6 +3889,7 @@ async fn plugin_list_uses_warmed_featured_plugin_ids_cache_on_first_request() ->
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -753,6 +753,7 @@ async fn plugin_share_checkout_adds_personal_marketplace_entry() -> Result<()> {
|
||||
marketplace_kinds: Some(vec![
|
||||
codex_app_server_protocol::PluginListMarketplaceKind::Local,
|
||||
]),
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
|
||||
@@ -394,6 +394,7 @@ async fn skills_list_loads_remote_installed_plugin_skills_from_cache() -> Result
|
||||
.send_plugin_list_request(PluginListParams {
|
||||
cwds: None,
|
||||
marketplace_kinds: None,
|
||||
force_refetch: false,
|
||||
})
|
||||
.await?;
|
||||
let plugin_list_response: JSONRPCResponse = timeout(
|
||||
|
||||
@@ -1028,6 +1028,7 @@ async fn request_plugin_list_with_marketplace_kinds(
|
||||
params: PluginListParams {
|
||||
cwds: Some(vec![cwd]),
|
||||
marketplace_kinds,
|
||||
force_refetch: false,
|
||||
},
|
||||
})
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user