Something went wrong. Try again.
opencode v2 mirror
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126import { PersistentPty } from "@opencode-ai/schema/persistent-pty"import { Pty } from "@opencode-ai/schema/pty"import { PtyTicket } from "@opencode-ai/schema/pty-ticket"import { Session } from "@opencode-ai/schema/session"import { Schema } from "effect"import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi"import { ForbiddenError, InvalidRequestError, PtyNotFoundError, ServiceUnavailableError } from "../errors.js"import { PTY_CONNECT_TICKET_QUERY, PTY_CONNECT_TOKEN_HEADER, PTY_CONNECT_TOKEN_HEADER_VALUE } from "./pty.js"
export { PTY_CONNECT_TICKET_QUERY, PTY_CONNECT_TOKEN_HEADER, PTY_CONNECT_TOKEN_HEADER_VALUE }
const CONNECT_PATH = /^\/api\/experimental\/persistent-pty\/[^/]+\/connect$/
export function hasPersistentPtyConnectTicketURL(url: URL) { return CONNECT_PATH.test(url.pathname) && !!url.searchParams.get(PTY_CONNECT_TICKET_QUERY)}
const errors = [InvalidRequestError, ServiceUnavailableError] as constconst terminalErrors = [PtyNotFoundError, ServiceUnavailableError] as const
export const PersistentPtyGroup = HttpApiGroup.make("server.experimental") .add( HttpApiEndpoint.get("persistentPty.read", "/api/experimental/session/:sessionID/terminal/read", { params: { sessionID: Session.ID }, query: { lines: Schema.NumberFromString.pipe(Schema.decodeTo(PersistentPty.ReadLines), Schema.optional), }, success: Schema.Struct({ data: Schema.NullOr(PersistentPty.ReadResult) }), error: [ServiceUnavailableError], }).annotateMerge( OpenApi.annotations({ summary: "Read the session's most recently controlled terminal", description: "Read the last physical rows without changing selection or taking control. Omitted lines uses the live terminal height; larger counts include retained history. Blank rows are preserved. Screen dimensions and cursor remain relative to the live screen. Returns null when no current terminal exists. Selection is server-local and resets on restart. Experimental: may change without compatibility guarantees.", }), ), ) .add( HttpApiEndpoint.get("persistentPty.list", "/api/experimental/session/:sessionID/terminal", { params: { sessionID: Session.ID }, success: Schema.Struct({ data: Schema.Array(PersistentPty.Info) }), error: errors, }), ) .add( HttpApiEndpoint.post("persistentPty.create", "/api/experimental/session/:sessionID/terminal", { params: { sessionID: Session.ID }, payload: PersistentPty.CreateInput, success: Schema.Struct({ data: PersistentPty.Info }), error: errors, }), ) .add( HttpApiEndpoint.post("persistentPty.shutdown", "/api/experimental/persistent-pty/shutdown", { success: HttpApiSchema.NoContent, error: [ServiceUnavailableError], }), ) .add( HttpApiEndpoint.post("persistentPty.handoff", "/api/experimental/persistent-pty/handoff", { success: Schema.Struct({ handoff: Schema.NullOr(PersistentPty.Handoff) }), error: [ServiceUnavailableError], }), ) .add( HttpApiEndpoint.get("persistentPty.get", "/api/experimental/persistent-pty/:ptyID", { params: { ptyID: Pty.ID }, success: Schema.Struct({ data: PersistentPty.Info }), error: terminalErrors, }), ) .add( HttpApiEndpoint.put("persistentPty.update", "/api/experimental/persistent-pty/:ptyID", { params: { ptyID: Pty.ID }, payload: PersistentPty.UpdateInput, success: Schema.Struct({ data: PersistentPty.Info }), error: terminalErrors, }), ) .add( HttpApiEndpoint.get("persistentPty.snapshot", "/api/experimental/persistent-pty/:ptyID/snapshot", { params: { ptyID: Pty.ID }, success: Schema.Struct({ data: PersistentPty.Snapshot }), error: terminalErrors, }), ) .add( HttpApiEndpoint.delete("persistentPty.remove", "/api/experimental/persistent-pty/:ptyID", { params: { ptyID: Pty.ID }, success: HttpApiSchema.NoContent, error: terminalErrors, }), ) .add( HttpApiEndpoint.post("persistentPty.connectToken", "/api/experimental/persistent-pty/:ptyID/connect-token", { params: { ptyID: Pty.ID }, headers: Schema.Struct({ [PTY_CONNECT_TOKEN_HEADER]: Schema.optional(Schema.String) }), success: Schema.Struct({ data: PtyTicket.ConnectToken }), error: [ForbiddenError, PtyNotFoundError, ServiceUnavailableError], }), ) .add( HttpApiEndpoint.get("persistentPty.connect", "/api/experimental/persistent-pty/:ptyID/connect", { params: { ptyID: Pty.ID }, success: Schema.Boolean, error: [ForbiddenError, PtyNotFoundError, ServiceUnavailableError], }).annotateMerge( OpenApi.annotations({ identifier: "v2.persistentPty.connect", summary: "Connect to a persistent PTY", description: "Stream persistent PTY output through the OpenCode server.", transform: (operation) => ({ ...operation, "x-websocket": true, parameters: [ ...(operation.parameters ?? []), ...["cursor", "role", "attachment_id", "takeover", "input_protocol", PTY_CONNECT_TICKET_QUERY].map( (name) => ({ in: "query", name, schema: { type: "string" } }), ), ], }), }), ), ) .annotateMerge(OpenApi.annotations({ title: "persistentPty", description: "Prototype persistent PTY routes." }))