diff --git a/packages/opencode/src/pty/ticket.ts b/packages/opencode/src/pty/ticket.ts index d40301cad2..b5e5747c51 100644 --- a/packages/opencode/src/pty/ticket.ts +++ b/packages/opencode/src/pty/ticket.ts @@ -28,7 +28,9 @@ export interface Interface { export class Service extends Context.Service()("@opencode/PtyTicket") {} function matches(record: Scope, input: Scope) { - return record.ptyID === input.ptyID && record.directory === input.directory && record.workspaceID === input.workspaceID + return ( + record.ptyID === input.ptyID && record.directory === input.directory && record.workspaceID === input.workspaceID + ) } // Tickets are inserted via Cache.set and removed atomically via invalidateWhen. The lookup is diff --git a/packages/opencode/test/pty/ticket.test.ts b/packages/opencode/test/pty/ticket.test.ts index 1b7d6005bf..4886f250f9 100644 --- a/packages/opencode/test/pty/ticket.test.ts +++ b/packages/opencode/test/pty/ticket.test.ts @@ -26,9 +26,7 @@ describe("PTY websocket tickets", () => { const ptyID = PtyID.ascending() const issued = yield* tickets.issue({ ptyID, directory: "/tmp/a" }) - expect( - yield* tickets.consume({ ptyID, directory: "/tmp/b", ticket: issued.ticket }), - ).toBe(false) + expect(yield* tickets.consume({ ptyID, directory: "/tmp/b", ticket: issued.ticket })).toBe(false) expect(yield* tickets.consume({ ptyID, directory: "/tmp/a", ticket: issued.ticket })).toBe(true) }), ) diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 21c547c853..6ff18b5155 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -3414,6 +3414,91 @@ ] } }, + "/pty/{ptyID}/connect-token": { + "post": { + "tags": ["pty"], + "operationId": "pty.connectToken", + "parameters": [ + { + "name": "directory", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "ptyID", + "in": "path", + "schema": { + "type": "string", + "pattern": "^pty.*" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "WebSocket connect token", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ticket": { + "type": "string" + }, + "expires_in": { + "type": "integer", + "exclusiveMinimum": 0 + } + }, + "required": ["ticket", "expires_in"], + "additionalProperties": false, + "description": "WebSocket connect token" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/effect_HttpApiError_Forbidden" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundError" + } + } + } + } + }, + "description": "Create a short-lived ticket for opening a PTY WebSocket connection.", + "summary": "Create PTY WebSocket token", + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.pty.connectToken({\n ...\n})" + } + ] + } + }, "/question": { "get": { "tags": ["question"], @@ -8327,6 +8412,16 @@ } } }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/effect_HttpApiError_Forbidden" + } + } + } + }, "404": { "description": "Not found", "content": { @@ -12752,6 +12847,17 @@ "required": ["error"], "additionalProperties": false }, + "effect_HttpApiError_Forbidden": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": ["Forbidden"] + } + }, + "required": ["_tag"], + "additionalProperties": false + }, "ProviderAuthMethod": { "type": "object", "properties": {