diff --git a/codex-rs/app-server-protocol/schema/json/ClientRequest.json b/codex-rs/app-server-protocol/schema/json/ClientRequest.json index 65a28ad2bd3..7ba147d9eee 100644 --- a/codex-rs/app-server-protocol/schema/json/ClientRequest.json +++ b/codex-rs/app-server-protocol/schema/json/ClientRequest.json @@ -953,21 +953,15 @@ }, "PluginInstallParams": { "properties": { - "cwd": { - "type": [ - "string", - "null" - ] - }, - "marketplaceName": { - "type": "string" + "marketplacePath": { + "$ref": "#/definitions/AbsolutePathBuf" }, "pluginName": { "type": "string" } }, "required": [ - "marketplaceName", + "marketplacePath", "pluginName" ], "type": "object" diff --git a/codex-rs/app-server-protocol/schema/json/EventMsg.json b/codex-rs/app-server-protocol/schema/json/EventMsg.json index 26f15b8fe1e..61626cc4eaf 100644 --- a/codex-rs/app-server-protocol/schema/json/EventMsg.json +++ b/codex-rs/app-server-protocol/schema/json/EventMsg.json @@ -199,6 +199,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "model_cap": { + "properties": { + "model": { + "type": "string" + }, + "reset_after_seconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "model_cap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/ServerNotification.json b/codex-rs/app-server-protocol/schema/json/ServerNotification.json index b6d1932315d..9f44908bc34 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerNotification.json +++ b/codex-rs/app-server-protocol/schema/json/ServerNotification.json @@ -410,6 +410,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index e9c79218908..48ef06f9bb7 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -8401,6 +8401,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { @@ -11183,21 +11212,15 @@ "PluginInstallParams": { "$schema": "http://json-schema.org/draft-07/schema#", "properties": { - "cwd": { - "type": [ - "string", - "null" - ] - }, - "marketplaceName": { - "type": "string" + "marketplacePath": { + "$ref": "#/definitions/v2/AbsolutePathBuf" }, "pluginName": { "type": "string" } }, "required": [ - "marketplaceName", + "marketplacePath", "pluginName" ], "title": "PluginInstallParams", diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index 2aac99cb3ac..fba45f80017 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -1917,6 +1917,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { @@ -8366,21 +8395,15 @@ "PluginInstallParams": { "$schema": "http://json-schema.org/draft-07/schema#", "properties": { - "cwd": { - "type": [ - "string", - "null" - ] - }, - "marketplaceName": { - "type": "string" + "marketplacePath": { + "$ref": "#/definitions/AbsolutePathBuf" }, "pluginName": { "type": "string" } }, "required": [ - "marketplaceName", + "marketplacePath", "pluginName" ], "title": "PluginInstallParams", diff --git a/codex-rs/app-server-protocol/schema/json/v2/ErrorNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ErrorNotification.json index 264a901c0ff..af22c96347b 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ErrorNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ErrorNotification.json @@ -18,6 +18,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/PluginInstallParams.json b/codex-rs/app-server-protocol/schema/json/v2/PluginInstallParams.json index 3aaaadf44b7..9e9bf6de86d 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/PluginInstallParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/PluginInstallParams.json @@ -1,21 +1,21 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "properties": { - "cwd": { - "type": [ - "string", - "null" - ] - }, - "marketplaceName": { + "definitions": { + "AbsolutePathBuf": { + "description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.", "type": "string" + } + }, + "properties": { + "marketplacePath": { + "$ref": "#/definitions/AbsolutePathBuf" }, "pluginName": { "type": "string" } }, "required": [ - "marketplaceName", + "marketplacePath", "pluginName" ], "title": "PluginInstallParams", diff --git a/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json index 3c5d11cad47..b6990cef836 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json @@ -37,6 +37,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json index fec4e20e44b..600c0ef676d 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json @@ -83,6 +83,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json index 3965739d1ad..3435d9229da 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json @@ -37,6 +37,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json index a74ee5d63ce..4acb06610ea 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json @@ -37,6 +37,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json index f00275398fd..d4ed64bf901 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json @@ -37,6 +37,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json index e8addc11013..c272fa268d5 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json @@ -83,6 +83,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json index e95b2d850fb..923f97f4f43 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json @@ -37,6 +37,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json index 52d9e295151..758fd0bc678 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json @@ -83,6 +83,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json index 698793bbdc5..74fe06f4954 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json @@ -37,6 +37,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json index 30d1e2841f1..028444e75af 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json @@ -37,6 +37,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json index 89a9e580de8..d645bbefa07 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json @@ -37,6 +37,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json index dbe082c3b01..c9ddefdebf6 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json @@ -37,6 +37,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json index 07323f20267..075f19a4ae1 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json @@ -37,6 +37,35 @@ ], "type": "string" }, + { + "additionalProperties": false, + "properties": { + "modelCap": { + "properties": { + "model": { + "type": "string" + }, + "resetAfterSeconds": { + "format": "uint64", + "minimum": 0.0, + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "model" + ], + "type": "object" + } + }, + "required": [ + "modelCap" + ], + "title": "ModelCapCodexErrorInfo", + "type": "object" + }, { "additionalProperties": false, "properties": { diff --git a/codex-rs/app-server-protocol/schema/typescript/CodexErrorInfo.ts b/codex-rs/app-server-protocol/schema/typescript/CodexErrorInfo.ts index 522b91ce201..d0482aeb807 100644 --- a/codex-rs/app-server-protocol/schema/typescript/CodexErrorInfo.ts +++ b/codex-rs/app-server-protocol/schema/typescript/CodexErrorInfo.ts @@ -5,4 +5,4 @@ /** * Codex errors that we expose to clients. */ -export type CodexErrorInfo = "context_window_exceeded" | "usage_limit_exceeded" | "server_overloaded" | { "http_connection_failed": { http_status_code: number | null, } } | { "response_stream_connection_failed": { http_status_code: number | null, } } | "internal_server_error" | "unauthorized" | "bad_request" | "sandbox_error" | { "response_stream_disconnected": { http_status_code: number | null, } } | { "response_too_many_failed_attempts": { http_status_code: number | null, } } | "thread_rollback_failed" | "other"; +export type CodexErrorInfo = "context_window_exceeded" | "usage_limit_exceeded" | "server_overloaded" | { "model_cap": { model: string, reset_after_seconds: bigint | null, } } | { "http_connection_failed": { http_status_code: number | null, } } | { "response_stream_connection_failed": { http_status_code: number | null, } } | "internal_server_error" | "unauthorized" | "bad_request" | "sandbox_error" | { "response_stream_disconnected": { http_status_code: number | null, } } | { "response_too_many_failed_attempts": { http_status_code: number | null, } } | "thread_rollback_failed" | "other"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/CodexErrorInfo.ts b/codex-rs/app-server-protocol/schema/typescript/v2/CodexErrorInfo.ts index 1ff409a41a4..20d28ff15f9 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/CodexErrorInfo.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/CodexErrorInfo.ts @@ -8,4 +8,4 @@ * When an upstream HTTP status is available (for example, from the Responses API or a provider), * it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant. */ -export type CodexErrorInfo = "contextWindowExceeded" | "usageLimitExceeded" | "serverOverloaded" | { "httpConnectionFailed": { httpStatusCode: number | null, } } | { "responseStreamConnectionFailed": { httpStatusCode: number | null, } } | "internalServerError" | "unauthorized" | "badRequest" | "threadRollbackFailed" | "sandboxError" | { "responseStreamDisconnected": { httpStatusCode: number | null, } } | { "responseTooManyFailedAttempts": { httpStatusCode: number | null, } } | "other"; +export type CodexErrorInfo = "contextWindowExceeded" | "usageLimitExceeded" | "serverOverloaded" | { "modelCap": { model: string, resetAfterSeconds: bigint | null, } } | { "httpConnectionFailed": { httpStatusCode: number | null, } } | { "responseStreamConnectionFailed": { httpStatusCode: number | null, } } | "internalServerError" | "unauthorized" | "badRequest" | "threadRollbackFailed" | "sandboxError" | { "responseStreamDisconnected": { httpStatusCode: number | null, } } | { "responseTooManyFailedAttempts": { httpStatusCode: number | null, } } | "other"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/PluginInstallParams.ts b/codex-rs/app-server-protocol/schema/typescript/v2/PluginInstallParams.ts index 2d17c17012d..86326b13049 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/PluginInstallParams.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/PluginInstallParams.ts @@ -1,5 +1,6 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; -export type PluginInstallParams = { marketplaceName: string, pluginName: string, cwd?: string | null, }; +export type PluginInstallParams = { marketplacePath: AbsolutePathBuf, pluginName: string, }; diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 1fb1dd67dde..60f15392d15 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -2564,10 +2564,8 @@ pub struct SkillsConfigWriteResponse { #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct PluginInstallParams { - pub marketplace_name: String, + pub marketplace_path: AbsolutePathBuf, pub plugin_name: String, - #[ts(optional = nullable)] - pub cwd: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] @@ -4861,6 +4859,29 @@ mod tests { ); } + #[test] + fn plugin_install_params_serialization_uses_marketplace_path() { + let marketplace_path = if cfg!(windows) { + r"C:\repo\.agents\plugins\marketplace.json" + } else { + "/repo/.agents/plugins/marketplace.json" + }; + let marketplace_path = AbsolutePathBuf::try_from(PathBuf::from(marketplace_path)).unwrap(); + let marketplace_path_json = marketplace_path.as_path().display().to_string(); + + assert_eq!( + serde_json::to_value(PluginInstallParams { + marketplace_path, + plugin_name: "gmail".to_string(), + }) + .unwrap(), + json!({ + "marketplacePath": marketplace_path_json, + "pluginName": "gmail", + }), + ); + } + #[test] fn codex_error_info_serializes_http_status_code_in_camel_case() { let value = CodexErrorInfo::ResponseTooManyFailedAttempts { diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index db09ee9672c..3ae75766970 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -5034,16 +5034,14 @@ impl CodexMessageProcessor { async fn plugin_install(&self, request_id: ConnectionRequestId, params: PluginInstallParams) { let PluginInstallParams { - marketplace_name, + marketplace_path, plugin_name, - cwd, } = params; let plugins_manager = self.thread_manager.plugins_manager(); let request = PluginInstallRequest { plugin_name, - marketplace_name, - cwd: cwd.unwrap_or_else(|| self.config.cwd.clone()), + marketplace_path, }; match plugins_manager.install_plugin(request).await { diff --git a/codex-rs/core/src/plugins/manager.rs b/codex-rs/core/src/plugins/manager.rs index 3cfcc81bbd4..53389329a63 100644 --- a/codex-rs/core/src/plugins/manager.rs +++ b/codex-rs/core/src/plugins/manager.rs @@ -1,6 +1,8 @@ use super::load_plugin_manifest; use super::marketplace::MarketplaceError; +use super::marketplace::discover_marketplace_paths; use super::marketplace::resolve_marketplace_plugin; +use super::marketplace::resolve_marketplace_plugin_from_paths; use super::plugin_manifest_name; use super::store::DEFAULT_PLUGIN_VERSION; use super::store::PluginId; @@ -42,8 +44,7 @@ pub struct AppConnectorId(pub String); #[derive(Debug, Clone, PartialEq, Eq)] pub struct PluginInstallRequest { pub plugin_name: String, - pub marketplace_name: String, - pub cwd: PathBuf, + pub marketplace_path: AbsolutePathBuf, } #[derive(Debug, Clone, PartialEq)] @@ -239,11 +240,7 @@ impl PluginsManager { &self, request: PluginInstallRequest, ) -> Result { - let resolved = resolve_marketplace_plugin( - &request.cwd, - &request.plugin_name, - &request.marketplace_name, - )?; + let resolved = resolve_marketplace_plugin(&request.marketplace_path, &request.plugin_name)?; let store = self.store.clone(); let result = tokio::task::spawn_blocking(move || { store.install(resolved.source_path.into_path_buf(), resolved.plugin_id) @@ -1091,8 +1088,10 @@ mod tests { let result = PluginsManager::new(tmp.path().to_path_buf()) .install_plugin(PluginInstallRequest { plugin_name: "sample-plugin".to_string(), - marketplace_name: "debug".to_string(), - cwd: repo_root.clone(), + marketplace_path: AbsolutePathBuf::try_from( + repo_root.join(".agents/plugins/marketplace.json"), + ) + .unwrap(), }) .await .unwrap(); @@ -1111,4 +1110,37 @@ mod tests { assert!(config.contains(r#"[plugins."sample-plugin@debug"]"#)); assert!(config.contains("enabled = true")); } + + #[test] + fn discovered_marketplace_paths_still_support_name_based_resolution() { + let tmp = tempfile::tempdir().unwrap(); + let repo_root = tmp.path().join("repo"); + fs::create_dir_all(repo_root.join(".git")).unwrap(); + fs::create_dir_all(repo_root.join(".agents/plugins")).unwrap(); + fs::write( + repo_root.join(".agents/plugins/marketplace.json"), + r#"{ + "name": "debug", + "plugins": [ + { + "name": "sample-plugin", + "source": { + "source": "local", + "path": "./sample-plugin" + } + } + ] +}"#, + ) + .unwrap(); + + let resolved = resolve_marketplace_plugin_from_paths( + &discover_marketplace_paths(&repo_root), + "sample-plugin", + "debug", + ) + .unwrap(); + + assert_eq!(resolved.plugin_id.as_key(), "sample-plugin@debug"); + } } diff --git a/codex-rs/core/src/plugins/marketplace.rs b/codex-rs/core/src/plugins/marketplace.rs index 9051c8fad38..bbc3c124fbe 100644 --- a/codex-rs/core/src/plugins/marketplace.rs +++ b/codex-rs/core/src/plugins/marketplace.rs @@ -57,18 +57,41 @@ impl MarketplaceError { // For now, marketplace discovery always reads from disk so installs see the latest // marketplace.json contents without any in-memory cache invalidation. pub fn resolve_marketplace_plugin( - cwd: &Path, + marketplace_path: &AbsolutePathBuf, plugin_name: &str, - marketplace_name: &str, ) -> Result { - resolve_marketplace_plugin_from_paths( - &discover_marketplace_paths(cwd), - plugin_name, + let marketplace = load_marketplace(marketplace_path.as_path())?; + let marketplace_name = marketplace.name.clone(); + let mut matches = marketplace + .plugins + .into_iter() + .filter(|plugin| plugin.name == plugin_name) + .collect::>(); + + if matches.len() > 1 { + return Err(MarketplaceError::DuplicatePlugin { + plugin_name: plugin_name.to_string(), + marketplace_name, + }); + } + + if let Some(plugin) = matches.pop() { + let plugin_id = PluginId::new(plugin.name, marketplace.name).map_err(|err| match err { + PluginIdError::Invalid(message) => MarketplaceError::InvalidPlugin(message), + })?; + return Ok(ResolvedMarketplacePlugin { + plugin_id, + source_path: resolve_plugin_source_path(marketplace_path.as_path(), plugin.source)?, + }); + } + + Err(MarketplaceError::PluginNotFound { + plugin_name: plugin_name.to_string(), marketplace_name, - ) + }) } -fn resolve_marketplace_plugin_from_paths( +pub fn resolve_marketplace_plugin_from_paths( marketplace_paths: &[PathBuf], plugin_name: &str, marketplace_name: &str, @@ -112,7 +135,7 @@ fn resolve_marketplace_plugin_from_paths( }) } -fn discover_marketplace_paths(cwd: &Path) -> Vec { +pub fn discover_marketplace_paths(cwd: &Path) -> Vec { let mut paths = Vec::new(); if let Some(repo_root) = get_git_repo_root(cwd) { let path = repo_root.join(MARKETPLACE_RELATIVE_PATH); @@ -233,9 +256,9 @@ mod tests { ) .unwrap(); - let resolved = - resolve_marketplace_plugin(&repo_root.join("nested"), "local-plugin", "codex-curated") - .unwrap(); + let marketplace_path = + AbsolutePathBuf::try_from(repo_root.join(".agents/plugins/marketplace.json")).unwrap(); + let resolved = resolve_marketplace_plugin(&marketplace_path, "local-plugin").unwrap(); assert_eq!( resolved, @@ -260,7 +283,9 @@ mod tests { ) .unwrap(); - let err = resolve_marketplace_plugin(&repo_root, "missing", "codex-curated").unwrap_err(); + let marketplace_path = + AbsolutePathBuf::try_from(repo_root.join(".agents/plugins/marketplace.json")).unwrap(); + let err = resolve_marketplace_plugin(&marketplace_path, "missing").unwrap_err(); assert_eq!( err.to_string(), @@ -356,8 +381,9 @@ mod tests { ) .unwrap(); - let err = - resolve_marketplace_plugin(&repo_root, "local-plugin", "codex-curated").unwrap_err(); + let marketplace_path = + AbsolutePathBuf::try_from(repo_root.join(".agents/plugins/marketplace.json")).unwrap(); + let err = resolve_marketplace_plugin(&marketplace_path, "local-plugin").unwrap_err(); assert_eq!( err.to_string(),