mirror of
https://github.com/openai/codex.git
synced 2026-05-21 19:45:26 +00:00
Compare commits
1 Commits
pr22330
...
xli-codex/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f45d2ef316 |
@@ -2119,7 +2119,7 @@
|
||||
},
|
||||
"PluginInstallParams": {
|
||||
"properties": {
|
||||
"marketplacePath": {
|
||||
"localMarketplacePath": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
@@ -2129,19 +2129,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"pluginName": {
|
||||
"type": "string"
|
||||
"localPluginName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remoteMarketplaceName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remotePluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pluginName"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"PluginListParams": {
|
||||
@@ -2161,7 +2167,7 @@
|
||||
},
|
||||
"PluginReadParams": {
|
||||
"properties": {
|
||||
"marketplacePath": {
|
||||
"localMarketplacePath": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
@@ -2171,19 +2177,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"pluginName": {
|
||||
"type": "string"
|
||||
"localPluginName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remoteMarketplaceName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remotePluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pluginName"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"PluginShareDeleteParams": {
|
||||
|
||||
@@ -12028,7 +12028,7 @@
|
||||
"PluginInstallParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"marketplacePath": {
|
||||
"localMarketplacePath": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
@@ -12038,19 +12038,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"pluginName": {
|
||||
"type": "string"
|
||||
"localPluginName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remoteMarketplaceName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remotePluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pluginName"
|
||||
],
|
||||
"title": "PluginInstallParams",
|
||||
"type": "object"
|
||||
},
|
||||
@@ -12301,7 +12307,7 @@
|
||||
"PluginReadParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"marketplacePath": {
|
||||
"localMarketplacePath": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/AbsolutePathBuf"
|
||||
@@ -12311,19 +12317,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"pluginName": {
|
||||
"type": "string"
|
||||
"localPluginName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remoteMarketplaceName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remotePluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pluginName"
|
||||
],
|
||||
"title": "PluginReadParams",
|
||||
"type": "object"
|
||||
},
|
||||
|
||||
@@ -8681,7 +8681,7 @@
|
||||
"PluginInstallParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"marketplacePath": {
|
||||
"localMarketplacePath": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
@@ -8691,19 +8691,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"pluginName": {
|
||||
"type": "string"
|
||||
"localPluginName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remoteMarketplaceName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remotePluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pluginName"
|
||||
],
|
||||
"title": "PluginInstallParams",
|
||||
"type": "object"
|
||||
},
|
||||
@@ -8954,7 +8960,7 @@
|
||||
"PluginReadParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"marketplacePath": {
|
||||
"localMarketplacePath": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
@@ -8964,19 +8970,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"pluginName": {
|
||||
"type": "string"
|
||||
"localPluginName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remoteMarketplaceName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remotePluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pluginName"
|
||||
],
|
||||
"title": "PluginReadParams",
|
||||
"type": "object"
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"marketplacePath": {
|
||||
"localMarketplacePath": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
@@ -17,19 +17,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"pluginName": {
|
||||
"type": "string"
|
||||
"localPluginName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remoteMarketplaceName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remotePluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pluginName"
|
||||
],
|
||||
"title": "PluginInstallParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"marketplacePath": {
|
||||
"localMarketplacePath": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
@@ -17,19 +17,25 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"pluginName": {
|
||||
"type": "string"
|
||||
"localPluginName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remoteMarketplaceName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"remotePluginId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"pluginName"
|
||||
],
|
||||
"title": "PluginReadParams",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -3,4 +3,4 @@
|
||||
// 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";
|
||||
|
||||
export type PluginInstallParams = { marketplacePath?: AbsolutePathBuf | null, remoteMarketplaceName?: string | null, pluginName: string, };
|
||||
export type PluginInstallParams = { localMarketplacePath?: AbsolutePathBuf | null, remoteMarketplaceName?: string | null, localPluginName?: string | null, remotePluginId?: string | null, };
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
// 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";
|
||||
|
||||
export type PluginReadParams = { marketplacePath?: AbsolutePathBuf | null, remoteMarketplaceName?: string | null, pluginName: string, };
|
||||
export type PluginReadParams = { localMarketplacePath?: AbsolutePathBuf | null, remoteMarketplaceName?: string | null, localPluginName?: string | null, remotePluginId?: string | null, };
|
||||
|
||||
@@ -1595,9 +1595,10 @@ mod tests {
|
||||
let plugin_install = ClientRequest::PluginInstall {
|
||||
request_id: request_id(),
|
||||
params: v2::PluginInstallParams {
|
||||
marketplace_path: Some(absolute_path("/tmp/marketplace")),
|
||||
local_marketplace_path: Some(absolute_path("/tmp/marketplace")),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "plugin-a".to_string(),
|
||||
local_plugin_name: Some("plugin-a".to_string()),
|
||||
remote_plugin_id: None,
|
||||
},
|
||||
};
|
||||
assert_eq!(
|
||||
|
||||
@@ -4597,10 +4597,13 @@ pub struct MarketplaceLoadErrorInfo {
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct PluginReadParams {
|
||||
#[ts(optional = nullable)]
|
||||
pub marketplace_path: Option<AbsolutePathBuf>,
|
||||
pub local_marketplace_path: Option<AbsolutePathBuf>,
|
||||
#[ts(optional = nullable)]
|
||||
pub remote_marketplace_name: Option<String>,
|
||||
pub plugin_name: String,
|
||||
#[ts(optional = nullable)]
|
||||
pub local_plugin_name: Option<String>,
|
||||
#[ts(optional = nullable)]
|
||||
pub remote_plugin_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
@@ -4987,10 +4990,13 @@ pub struct SkillsConfigWriteResponse {
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct PluginInstallParams {
|
||||
#[ts(optional = nullable)]
|
||||
pub marketplace_path: Option<AbsolutePathBuf>,
|
||||
pub local_marketplace_path: Option<AbsolutePathBuf>,
|
||||
#[ts(optional = nullable)]
|
||||
pub remote_marketplace_name: Option<String>,
|
||||
pub plugin_name: String,
|
||||
#[ts(optional = nullable)]
|
||||
pub local_plugin_name: Option<String>,
|
||||
#[ts(optional = nullable)]
|
||||
pub remote_plugin_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
@@ -10658,42 +10664,46 @@ mod tests {
|
||||
let marketplace_path_json = marketplace_path.as_path().display().to_string();
|
||||
assert_eq!(
|
||||
serde_json::to_value(PluginReadParams {
|
||||
marketplace_path: Some(marketplace_path.clone()),
|
||||
local_marketplace_path: Some(marketplace_path.clone()),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "gmail".to_string(),
|
||||
local_plugin_name: Some("gmail".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.unwrap(),
|
||||
json!({
|
||||
"marketplacePath": marketplace_path_json,
|
||||
"localMarketplacePath": marketplace_path_json,
|
||||
"remoteMarketplaceName": null,
|
||||
"pluginName": "gmail",
|
||||
"localPluginName": "gmail",
|
||||
"remotePluginId": null,
|
||||
}),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
serde_json::from_value::<PluginReadParams>(json!({
|
||||
"marketplacePath": marketplace_path_json,
|
||||
"pluginName": "gmail",
|
||||
"localMarketplacePath": marketplace_path_json,
|
||||
"localPluginName": "gmail",
|
||||
"forceRemoteSync": true,
|
||||
}))
|
||||
.unwrap(),
|
||||
PluginReadParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "gmail".to_string(),
|
||||
local_plugin_name: Some("gmail".to_string()),
|
||||
remote_plugin_id: None,
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
serde_json::from_value::<PluginReadParams>(json!({
|
||||
"remoteMarketplaceName": "openai-curated",
|
||||
"pluginName": "gmail",
|
||||
"remotePluginId": "plugins~Plugin_gmail",
|
||||
}))
|
||||
.unwrap(),
|
||||
PluginReadParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: Some("openai-curated".to_string()),
|
||||
plugin_name: "gmail".to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some("plugins~Plugin_gmail".to_string()),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -10709,43 +10719,47 @@ mod tests {
|
||||
let marketplace_path_json = marketplace_path.as_path().display().to_string();
|
||||
assert_eq!(
|
||||
serde_json::to_value(PluginInstallParams {
|
||||
marketplace_path: Some(marketplace_path.clone()),
|
||||
local_marketplace_path: Some(marketplace_path.clone()),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "gmail".to_string(),
|
||||
local_plugin_name: Some("gmail".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.unwrap(),
|
||||
json!({
|
||||
"marketplacePath": marketplace_path_json,
|
||||
"localMarketplacePath": marketplace_path_json,
|
||||
"remoteMarketplaceName": null,
|
||||
"pluginName": "gmail",
|
||||
"localPluginName": "gmail",
|
||||
"remotePluginId": null,
|
||||
}),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
serde_json::from_value::<PluginInstallParams>(json!({
|
||||
"marketplacePath": marketplace_path_json,
|
||||
"pluginName": "gmail",
|
||||
"localMarketplacePath": marketplace_path_json,
|
||||
"localPluginName": "gmail",
|
||||
"forceRemoteSync": true,
|
||||
}))
|
||||
.unwrap(),
|
||||
PluginInstallParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "gmail".to_string(),
|
||||
local_plugin_name: Some("gmail".to_string()),
|
||||
remote_plugin_id: None,
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
serde_json::from_value::<PluginInstallParams>(json!({
|
||||
"remoteMarketplaceName": "openai-curated",
|
||||
"pluginName": "gmail",
|
||||
"remotePluginId": "plugins~Plugin_gmail",
|
||||
"forceRemoteSync": true,
|
||||
}))
|
||||
.unwrap(),
|
||||
PluginInstallParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: Some("openai-curated".to_string()),
|
||||
plugin_name: "gmail".to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some("plugins~Plugin_gmail".to_string()),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -202,7 +202,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/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, 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. `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/read` — read one plugin by `marketplacePath` plus `pluginName`, returning marketplace info, a list-style `summary`, manifest descriptions/interface metadata, and bundled skills/apps/MCP server names. Returned plugin skills include their current `enabled` state after local config filtering. Plugin app summaries also include `needsAuth` when the server can determine connector accessibility (**under development; do not call from production clients yet**).
|
||||
- `plugin/read` — read one plugin by `localMarketplacePath` plus `localPluginName`, or by `remoteMarketplaceName` plus `remotePluginId`, returning marketplace info, a list-style `summary`, manifest descriptions/interface metadata, and bundled skills/apps/MCP server names. Returned plugin skills include their current `enabled` state after local config filtering. Plugin app summaries also include `needsAuth` when the server can determine 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.
|
||||
- `skills/changed` — notification emitted when watched local skill files change.
|
||||
- `app/list` — list available apps.
|
||||
@@ -211,7 +211,7 @@ Example with notification opt-out:
|
||||
- `device/key/sign` — sign one of the accepted structured payload variants with a controller-local device key. The only accepted payload today is `remoteControlClientConnection`, which binds a server-issued `/client` websocket challenge to the enrolled controller device without signing the bearer token itself; this is intentionally not an arbitrary-byte signing API.
|
||||
- `remoteControl/status/changed` — notification emitted when the remote-control status or client-visible environment id changes. `status` is one of `disabled`, `connecting`, `connected`, or `errored`; `environmentId` is a string when the app-server has a current enrollment and `null` when that enrollment is cleared, invalidated, or remote control is disabled. Newly initialized app-server clients always receive the current status snapshot.
|
||||
- `skills/config/write` — write user-level skill config by name or absolute path.
|
||||
- `plugin/install` — install a plugin from a discovered marketplace entry, rejecting marketplace entries marked unavailable for install, install MCPs if any, and return the effective plugin auth policy plus any apps that still need auth (**under development; do not call from production clients yet**).
|
||||
- `plugin/install` — install a local plugin from a discovered marketplace entry by `localMarketplacePath` plus `localPluginName`, or install a remote ChatGPT plugin by `remoteMarketplaceName` plus backend `remotePluginId`, rejecting marketplace entries marked unavailable for install, install MCPs if any, and return the effective plugin auth policy plus any apps that still need auth (**under development; do not call from production clients yet**).
|
||||
- `plugin/uninstall` — uninstall a local plugin by `pluginId` in `<plugin>@<marketplace>` form by removing its cached files and clearing its user-level config entry, or uninstall a remote ChatGPT plugin by backend `pluginId` by forwarding the uninstall to the ChatGPT plugin backend and removing any downloaded remote-plugin cache (**under development; do not call from production clients yet**).
|
||||
- `mcpServer/oauth/login` — start an OAuth login for a configured MCP server; returns an `authorization_url` and later emits `mcpServer/oauthLogin/completed` once the browser flow finishes.
|
||||
- `tool/requestUserInput` — prompt the user with 1–3 short questions for a tool call and return their answers (experimental).
|
||||
|
||||
@@ -188,30 +188,35 @@ impl CodexMessageProcessor {
|
||||
) -> Result<PluginReadResponse, JSONRPCErrorError> {
|
||||
let plugins_manager = self.thread_manager.plugins_manager();
|
||||
let PluginReadParams {
|
||||
marketplace_path,
|
||||
local_marketplace_path,
|
||||
remote_marketplace_name,
|
||||
plugin_name,
|
||||
local_plugin_name,
|
||||
remote_plugin_id,
|
||||
} = params;
|
||||
let read_source = match (marketplace_path, remote_marketplace_name) {
|
||||
(Some(marketplace_path), None) => Ok(marketplace_path),
|
||||
(None, Some(remote_marketplace_name)) => Err(remote_marketplace_name),
|
||||
(Some(_), Some(_)) | (None, None) => {
|
||||
return Err(invalid_request(
|
||||
"plugin/read requires exactly one of marketplacePath or remoteMarketplaceName",
|
||||
));
|
||||
}
|
||||
let read_source = plugin_request_source(
|
||||
"plugin/read",
|
||||
local_marketplace_path,
|
||||
remote_marketplace_name,
|
||||
local_plugin_name,
|
||||
remote_plugin_id,
|
||||
)?;
|
||||
let config_cwd = match &read_source {
|
||||
PluginRequestSource::Local {
|
||||
marketplace_path, ..
|
||||
} => marketplace_path.as_path().parent().map(Path::to_path_buf),
|
||||
PluginRequestSource::Remote { .. } => None,
|
||||
};
|
||||
let config_cwd = read_source.as_ref().ok().and_then(|marketplace_path| {
|
||||
marketplace_path.as_path().parent().map(Path::to_path_buf)
|
||||
});
|
||||
|
||||
let config = self.load_latest_config(config_cwd).await?;
|
||||
let plugins_input = config.plugins_config_input();
|
||||
|
||||
let plugin = match read_source {
|
||||
Ok(marketplace_path) => {
|
||||
PluginRequestSource::Local {
|
||||
marketplace_path,
|
||||
local_plugin_name,
|
||||
} => {
|
||||
let request = PluginReadRequest {
|
||||
plugin_name,
|
||||
plugin_name: local_plugin_name,
|
||||
marketplace_path,
|
||||
};
|
||||
let outcome = plugins_manager
|
||||
@@ -259,7 +264,10 @@ impl CodexMessageProcessor {
|
||||
mcp_servers: outcome.plugin.mcp_server_names,
|
||||
}
|
||||
}
|
||||
Err(remote_marketplace_name) => {
|
||||
PluginRequestSource::Remote {
|
||||
remote_marketplace_name,
|
||||
remote_plugin_id,
|
||||
} => {
|
||||
if !config.features.enabled(Feature::Plugins)
|
||||
|| !config.features.enabled(Feature::RemotePlugin)
|
||||
{
|
||||
@@ -271,12 +279,12 @@ impl CodexMessageProcessor {
|
||||
let remote_plugin_service_config = RemotePluginServiceConfig {
|
||||
chatgpt_base_url: config.chatgpt_base_url.clone(),
|
||||
};
|
||||
validate_remote_plugin_id(&plugin_name)?;
|
||||
validate_remote_plugin_id(&remote_plugin_id)?;
|
||||
let remote_detail = codex_core_plugins::remote::fetch_remote_plugin_detail(
|
||||
&remote_plugin_service_config,
|
||||
auth.as_ref(),
|
||||
&remote_marketplace_name,
|
||||
&plugin_name,
|
||||
&remote_plugin_id,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
@@ -503,22 +511,31 @@ impl CodexMessageProcessor {
|
||||
params: PluginInstallParams,
|
||||
) -> Result<PluginInstallResponse, JSONRPCErrorError> {
|
||||
let PluginInstallParams {
|
||||
marketplace_path,
|
||||
local_marketplace_path,
|
||||
remote_marketplace_name,
|
||||
plugin_name,
|
||||
local_plugin_name,
|
||||
remote_plugin_id,
|
||||
} = params;
|
||||
let marketplace_path = match (marketplace_path, remote_marketplace_name) {
|
||||
(Some(marketplace_path), None) => marketplace_path,
|
||||
(None, Some(remote_marketplace_name)) => {
|
||||
let install_source = plugin_request_source(
|
||||
"plugin/install",
|
||||
local_marketplace_path,
|
||||
remote_marketplace_name,
|
||||
local_plugin_name,
|
||||
remote_plugin_id,
|
||||
)?;
|
||||
let (marketplace_path, local_plugin_name) = match install_source {
|
||||
PluginRequestSource::Local {
|
||||
marketplace_path,
|
||||
local_plugin_name,
|
||||
} => (marketplace_path, local_plugin_name),
|
||||
PluginRequestSource::Remote {
|
||||
remote_marketplace_name,
|
||||
remote_plugin_id,
|
||||
} => {
|
||||
return self
|
||||
.remote_plugin_install_response(remote_marketplace_name, plugin_name)
|
||||
.remote_plugin_install_response(remote_marketplace_name, remote_plugin_id)
|
||||
.await;
|
||||
}
|
||||
(Some(_), Some(_)) | (None, None) => {
|
||||
return Err(invalid_request(
|
||||
"plugin/install requires exactly one of marketplacePath or remoteMarketplaceName",
|
||||
));
|
||||
}
|
||||
};
|
||||
let config_cwd = marketplace_path.as_path().parent().map(Path::to_path_buf);
|
||||
let config = self.load_latest_config(config_cwd.clone()).await?;
|
||||
@@ -535,7 +552,7 @@ impl CodexMessageProcessor {
|
||||
|
||||
let plugins_manager = self.thread_manager.plugins_manager();
|
||||
let request = PluginInstallRequest {
|
||||
plugin_name,
|
||||
plugin_name: local_plugin_name,
|
||||
marketplace_path,
|
||||
};
|
||||
|
||||
@@ -907,13 +924,70 @@ impl CodexMessageProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid_remote_uninstall_plugin_id(plugin_name: &str) -> bool {
|
||||
is_valid_remote_plugin_id(plugin_name)
|
||||
&& (plugin_name.starts_with("plugins~")
|
||||
|| plugin_name.starts_with("plugins_")
|
||||
|| plugin_name.starts_with("app_")
|
||||
|| plugin_name.starts_with("asdk_app_")
|
||||
|| plugin_name.starts_with("connector_"))
|
||||
enum PluginRequestSource {
|
||||
Local {
|
||||
marketplace_path: AbsolutePathBuf,
|
||||
local_plugin_name: String,
|
||||
},
|
||||
Remote {
|
||||
remote_marketplace_name: String,
|
||||
remote_plugin_id: String,
|
||||
},
|
||||
}
|
||||
|
||||
fn plugin_request_source(
|
||||
method: &str,
|
||||
local_marketplace_path: Option<AbsolutePathBuf>,
|
||||
remote_marketplace_name: Option<String>,
|
||||
local_plugin_name: Option<String>,
|
||||
remote_plugin_id: Option<String>,
|
||||
) -> Result<PluginRequestSource, JSONRPCErrorError> {
|
||||
match (local_marketplace_path, remote_marketplace_name) {
|
||||
(Some(marketplace_path), None) => {
|
||||
if remote_plugin_id.is_some() {
|
||||
return Err(invalid_request(format!(
|
||||
"{method} with localMarketplacePath requires localPluginName, not remotePluginId"
|
||||
)));
|
||||
}
|
||||
let Some(local_plugin_name) = local_plugin_name else {
|
||||
return Err(invalid_request(format!(
|
||||
"{method} with localMarketplacePath requires localPluginName"
|
||||
)));
|
||||
};
|
||||
Ok(PluginRequestSource::Local {
|
||||
marketplace_path,
|
||||
local_plugin_name,
|
||||
})
|
||||
}
|
||||
(None, Some(remote_marketplace_name)) => {
|
||||
if local_plugin_name.is_some() {
|
||||
return Err(invalid_request(format!(
|
||||
"{method} with remoteMarketplaceName requires remotePluginId, not localPluginName"
|
||||
)));
|
||||
}
|
||||
let Some(remote_plugin_id) = remote_plugin_id else {
|
||||
return Err(invalid_request(format!(
|
||||
"{method} with remoteMarketplaceName requires remotePluginId"
|
||||
)));
|
||||
};
|
||||
Ok(PluginRequestSource::Remote {
|
||||
remote_marketplace_name,
|
||||
remote_plugin_id,
|
||||
})
|
||||
}
|
||||
(Some(_), Some(_)) | (None, None) => Err(invalid_request(format!(
|
||||
"{method} requires exactly one of localMarketplacePath or remoteMarketplaceName"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid_remote_uninstall_plugin_id(plugin_id: &str) -> bool {
|
||||
is_valid_remote_plugin_id(plugin_id)
|
||||
&& (plugin_id.starts_with("plugins~")
|
||||
|| plugin_id.starts_with("plugins_")
|
||||
|| plugin_id.starts_with("app_")
|
||||
|| plugin_id.starts_with("asdk_app_")
|
||||
|| plugin_id.starts_with("connector_"))
|
||||
}
|
||||
|
||||
fn remote_marketplace_to_info(marketplace: RemoteMarketplace) -> PluginMarketplaceEntry {
|
||||
|
||||
@@ -75,8 +75,8 @@ async fn plugin_install_rejects_relative_marketplace_paths() -> Result<()> {
|
||||
.send_raw_request(
|
||||
"plugin/install",
|
||||
Some(serde_json::json!({
|
||||
"marketplacePath": "relative-marketplace.json",
|
||||
"pluginName": "missing-plugin",
|
||||
"localMarketplacePath": "relative-marketplace.json",
|
||||
"localPluginName": "missing-plugin",
|
||||
})),
|
||||
)
|
||||
.await?;
|
||||
@@ -100,9 +100,10 @@ async fn plugin_install_rejects_missing_install_source() -> Result<()> {
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: Some("sample-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -116,7 +117,7 @@ async fn plugin_install_rejects_missing_install_source() -> Result<()> {
|
||||
assert!(
|
||||
err.error
|
||||
.message
|
||||
.contains("requires exactly one of marketplacePath or remoteMarketplaceName")
|
||||
.contains("requires exactly one of localMarketplacePath or remoteMarketplaceName")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -129,11 +130,12 @@ async fn plugin_install_rejects_multiple_install_sources() -> Result<()> {
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
local_marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
codex_home.path().join("marketplace.json"),
|
||||
)?),
|
||||
remote_marketplace_name: Some("openai-curated".to_string()),
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some("sample-plugin".to_string()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -147,7 +149,7 @@ async fn plugin_install_rejects_multiple_install_sources() -> Result<()> {
|
||||
assert!(
|
||||
err.error
|
||||
.message
|
||||
.contains("requires exactly one of marketplacePath or remoteMarketplaceName")
|
||||
.contains("requires exactly one of localMarketplacePath or remoteMarketplaceName")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -160,9 +162,10 @@ async fn plugin_install_rejects_remote_marketplace_when_remote_plugin_is_disable
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: Some("chatgpt-global".to_string()),
|
||||
plugin_name: "plugins~Plugin_22222222222222222222222222222222".to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some("plugins~Plugin_22222222222222222222222222222222".to_string()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -383,7 +386,7 @@ async fn plugin_install_rejects_invalid_remote_release_version() -> Result<()> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plugin_install_rejects_invalid_remote_plugin_name() -> Result<()> {
|
||||
async fn plugin_install_rejects_invalid_remote_plugin_id() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
write_remote_plugin_catalog_config(codex_home.path(), "https://example.invalid/backend-api/")?;
|
||||
let mut mcp = McpProcess::new(codex_home.path()).await?;
|
||||
@@ -391,9 +394,10 @@ async fn plugin_install_rejects_invalid_remote_plugin_name() -> Result<()> {
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: Some("chatgpt-global".to_string()),
|
||||
plugin_name: "linear/../../oops".to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some("linear/../../oops".to_string()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -514,9 +518,10 @@ async fn plugin_install_rejects_when_workspace_codex_plugins_disabled() -> Resul
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: Some("sample-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -543,11 +548,12 @@ async fn plugin_install_returns_invalid_request_for_missing_marketplace_file() -
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
local_marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
codex_home.path().join("missing-marketplace.json"),
|
||||
)?),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "missing-plugin".to_string(),
|
||||
local_plugin_name: Some("missing-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -584,9 +590,10 @@ async fn plugin_install_returns_invalid_request_for_not_available_plugin() -> Re
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: Some("sample-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -634,9 +641,10 @@ async fn plugin_install_returns_invalid_request_for_disallowed_product_plugin()
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: Some("sample-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -683,9 +691,10 @@ async fn plugin_install_tracks_analytics_event() -> Result<()> {
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: Some("sample-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
@@ -890,9 +899,10 @@ async fn plugin_install_returns_apps_needing_auth() -> Result<()> {
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: Some("sample-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -974,9 +984,10 @@ async fn plugin_install_filters_disallowed_apps_needing_auth() -> Result<()> {
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: Some("sample-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -1041,9 +1052,10 @@ async fn plugin_install_makes_bundled_mcp_servers_available_to_followup_requests
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: Some("sample-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
@@ -1459,9 +1471,10 @@ async fn send_remote_plugin_install_request(
|
||||
remote_plugin_id: &str,
|
||||
) -> Result<i64> {
|
||||
mcp.send_plugin_install_request(PluginInstallParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: Some("caller-marketplace-is-ignored".to_string()),
|
||||
plugin_name: remote_plugin_id.to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some(remote_plugin_id.to_string()),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -63,9 +63,10 @@ async fn plugin_read_rejects_missing_read_source() -> Result<()> {
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: Some("sample-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -79,7 +80,7 @@ async fn plugin_read_rejects_missing_read_source() -> Result<()> {
|
||||
assert!(
|
||||
err.error
|
||||
.message
|
||||
.contains("requires exactly one of marketplacePath or remoteMarketplaceName")
|
||||
.contains("requires exactly one of localMarketplacePath or remoteMarketplaceName")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -92,11 +93,12 @@ async fn plugin_read_rejects_multiple_read_sources() -> Result<()> {
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
local_marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
codex_home.path().join("marketplace.json"),
|
||||
)?),
|
||||
remote_marketplace_name: Some("openai-curated".to_string()),
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some("sample-plugin".to_string()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -110,7 +112,7 @@ async fn plugin_read_rejects_multiple_read_sources() -> Result<()> {
|
||||
assert!(
|
||||
err.error
|
||||
.message
|
||||
.contains("requires exactly one of marketplacePath or remoteMarketplaceName")
|
||||
.contains("requires exactly one of localMarketplacePath or remoteMarketplaceName")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -123,9 +125,10 @@ async fn plugin_read_rejects_remote_marketplace_when_remote_plugin_is_disabled()
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: Some("chatgpt-global".to_string()),
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some("sample-plugin".to_string()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -254,9 +257,10 @@ async fn plugin_read_reads_remote_plugin_details_when_remote_plugin_enabled() ->
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: Some("chatgpt-global".to_string()),
|
||||
plugin_name: "plugins~Plugin_00000000000000000000000000000000".to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some("plugins~Plugin_00000000000000000000000000000000".to_string()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -383,9 +387,10 @@ async fn plugin_read_maps_missing_remote_plugin_to_invalid_request() -> Result<(
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: Some("chatgpt-global".to_string()),
|
||||
plugin_name: "plugins~Plugin_missing".to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some("plugins~Plugin_missing".to_string()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -435,9 +440,10 @@ remote_plugin = true
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: Some("chatgpt-global".to_string()),
|
||||
plugin_name: "linear".to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some("linear".to_string()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -457,7 +463,7 @@ remote_plugin = true
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plugin_read_rejects_invalid_remote_plugin_name() -> Result<()> {
|
||||
async fn plugin_read_rejects_invalid_remote_plugin_id() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
write_remote_plugin_catalog_config(codex_home.path(), "https://example.invalid/backend-api/")?;
|
||||
let mut mcp = McpProcess::new(codex_home.path()).await?;
|
||||
@@ -465,9 +471,10 @@ async fn plugin_read_rejects_invalid_remote_plugin_name() -> Result<()> {
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: None,
|
||||
local_marketplace_path: None,
|
||||
remote_marketplace_name: Some("chatgpt-global".to_string()),
|
||||
plugin_name: "linear/../../oops".to_string(),
|
||||
local_plugin_name: None,
|
||||
remote_plugin_id: Some("linear/../../oops".to_string()),
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -525,9 +532,10 @@ enabled = true
|
||||
AbsolutePathBuf::try_from(repo_root.path().join(".agents/plugins/marketplace.json"))?;
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: Some(marketplace_path.clone()),
|
||||
local_marketplace_path: Some(marketplace_path.clone()),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "demo-plugin".to_string(),
|
||||
local_plugin_name: Some("demo-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -679,9 +687,10 @@ enabled = true
|
||||
AbsolutePathBuf::try_from(repo_root.path().join(".agents/plugins/marketplace.json"))?;
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: Some(marketplace_path.clone()),
|
||||
local_marketplace_path: Some(marketplace_path.clone()),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "demo-plugin".to_string(),
|
||||
local_plugin_name: Some("demo-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -827,9 +836,10 @@ async fn plugin_read_returns_app_needs_auth() -> Result<()> {
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "sample-plugin".to_string(),
|
||||
local_plugin_name: Some("sample-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -894,11 +904,12 @@ async fn plugin_read_accepts_legacy_string_default_prompt() -> Result<()> {
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
local_marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
repo_root.path().join(".agents/plugins/marketplace.json"),
|
||||
)?),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "demo-plugin".to_string(),
|
||||
local_plugin_name: Some("demo-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -956,11 +967,12 @@ async fn plugin_read_describes_uninstalled_git_source_without_cloning() -> Resul
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
local_marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
repo_root.path().join(".agents/plugins/marketplace.json"),
|
||||
)?),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "toolkit".to_string(),
|
||||
local_plugin_name: Some("toolkit".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -1019,11 +1031,12 @@ async fn plugin_read_returns_invalid_request_when_plugin_is_missing() -> Result<
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
local_marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
repo_root.path().join(".agents/plugins/marketplace.json"),
|
||||
)?),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "missing-plugin".to_string(),
|
||||
local_plugin_name: Some("missing-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -1072,11 +1085,12 @@ async fn plugin_read_returns_invalid_request_when_plugin_manifest_is_missing() -
|
||||
|
||||
let request_id = mcp
|
||||
.send_plugin_read_request(PluginReadParams {
|
||||
marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
local_marketplace_path: Some(AbsolutePathBuf::try_from(
|
||||
repo_root.path().join(".agents/plugins/marketplace.json"),
|
||||
)?),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: "demo-plugin".to_string(),
|
||||
local_plugin_name: Some("demo-plugin".to_string()),
|
||||
remote_plugin_id: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -733,9 +733,10 @@ pub(super) async fn fetch_plugin_install(
|
||||
.request_typed(ClientRequest::PluginInstall {
|
||||
request_id,
|
||||
params: PluginInstallParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name,
|
||||
local_plugin_name: Some(plugin_name),
|
||||
remote_plugin_id: None,
|
||||
},
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -578,9 +578,10 @@ impl App {
|
||||
app_server,
|
||||
cwd,
|
||||
PluginReadParams {
|
||||
marketplace_path: Some(marketplace_path),
|
||||
local_marketplace_path: Some(marketplace_path),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name,
|
||||
local_plugin_name: Some(plugin_name),
|
||||
remote_plugin_id: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1818,9 +1818,10 @@ impl ChatWidget {
|
||||
tx.send(AppEvent::FetchPluginDetail {
|
||||
cwd: cwd.clone(),
|
||||
params: codex_app_server_protocol::PluginReadParams {
|
||||
marketplace_path: Some(marketplace_path.clone()),
|
||||
local_marketplace_path: Some(marketplace_path.clone()),
|
||||
remote_marketplace_name: None,
|
||||
plugin_name: plugin_name.clone(),
|
||||
local_plugin_name: Some(plugin_name.clone()),
|
||||
remote_plugin_id: None,
|
||||
},
|
||||
});
|
||||
})]
|
||||
|
||||
@@ -2428,17 +2428,23 @@ class PluginAuthPolicy(Enum):
|
||||
on_use = "ON_USE"
|
||||
|
||||
|
||||
class PluginAvailability(Enum):
|
||||
disabled_by_admin = "DISABLED_BY_ADMIN"
|
||||
available = "AVAILABLE"
|
||||
|
||||
|
||||
class PluginInstallParams(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
)
|
||||
marketplace_path: Annotated[
|
||||
AbsolutePathBuf | None, Field(alias="marketplacePath")
|
||||
local_marketplace_path: Annotated[
|
||||
AbsolutePathBuf | None, Field(alias="localMarketplacePath")
|
||||
] = None
|
||||
plugin_name: Annotated[str, Field(alias="pluginName")]
|
||||
local_plugin_name: Annotated[str | None, Field(alias="localPluginName")] = None
|
||||
remote_marketplace_name: Annotated[
|
||||
str | None, Field(alias="remoteMarketplaceName")
|
||||
] = None
|
||||
remote_plugin_id: Annotated[str | None, Field(alias="remotePluginId")] = None
|
||||
|
||||
|
||||
class PluginInstallPolicy(Enum):
|
||||
@@ -2531,13 +2537,14 @@ class PluginReadParams(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
)
|
||||
marketplace_path: Annotated[
|
||||
AbsolutePathBuf | None, Field(alias="marketplacePath")
|
||||
local_marketplace_path: Annotated[
|
||||
AbsolutePathBuf | None, Field(alias="localMarketplacePath")
|
||||
] = None
|
||||
plugin_name: Annotated[str, Field(alias="pluginName")]
|
||||
local_plugin_name: Annotated[str | None, Field(alias="localPluginName")] = None
|
||||
remote_marketplace_name: Annotated[
|
||||
str | None, Field(alias="remoteMarketplaceName")
|
||||
] = None
|
||||
remote_plugin_id: Annotated[str | None, Field(alias="remotePluginId")] = None
|
||||
|
||||
|
||||
class PluginShareDeleteParams(BaseModel):
|
||||
@@ -2631,6 +2638,10 @@ class PluginSummary(BaseModel):
|
||||
populate_by_name=True,
|
||||
)
|
||||
auth_policy: Annotated[PluginAuthPolicy, Field(alias="authPolicy")]
|
||||
availability: Annotated[
|
||||
PluginAvailability | None,
|
||||
Field(description="Availability state for installing and using the plugin."),
|
||||
] = "AVAILABLE"
|
||||
enabled: bool
|
||||
id: str
|
||||
install_policy: Annotated[PluginInstallPolicy, Field(alias="installPolicy")]
|
||||
@@ -6443,11 +6454,22 @@ class PluginReadResponse(BaseModel):
|
||||
plugin: PluginDetail
|
||||
|
||||
|
||||
class PluginShareListItem(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
)
|
||||
local_plugin_path: Annotated[
|
||||
AbsolutePathBuf | None, Field(alias="localPluginPath")
|
||||
] = None
|
||||
plugin: PluginSummary
|
||||
share_url: Annotated[str, Field(alias="shareUrl")]
|
||||
|
||||
|
||||
class PluginShareListResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
)
|
||||
data: list[PluginSummary]
|
||||
data: list[PluginShareListItem]
|
||||
|
||||
|
||||
class RateLimitSnapshot(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user