fix(httpapi): return pty error bodies (#28838)
This commit is contained in:
@@ -149,6 +149,23 @@ export class McpServerNotFoundError extends Schema.TaggedErrorClass<McpServerNot
|
||||
{ httpApiStatus: 404 },
|
||||
) {}
|
||||
|
||||
export class PtyNotFoundError extends Schema.TaggedErrorClass<PtyNotFoundError>()(
|
||||
"PtyNotFoundError",
|
||||
{
|
||||
ptyID: Schema.String,
|
||||
message: Schema.String,
|
||||
},
|
||||
{ httpApiStatus: 404 },
|
||||
) {}
|
||||
|
||||
export class PtyForbiddenError extends Schema.TaggedErrorClass<PtyForbiddenError>()(
|
||||
"PtyForbiddenError",
|
||||
{
|
||||
message: Schema.String,
|
||||
},
|
||||
{ httpApiStatus: 403 },
|
||||
) {}
|
||||
|
||||
export class ApiNotFoundError extends Schema.ErrorClass<ApiNotFoundError>("NotFoundError")(
|
||||
{
|
||||
name: Schema.Literal("NotFoundError"),
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
WorkspaceRoutingQuery,
|
||||
WorkspaceRoutingQueryFields,
|
||||
} from "../middleware/workspace-routing"
|
||||
import { ApiNotFoundError } from "../errors"
|
||||
import { PtyForbiddenError, PtyNotFoundError } from "../errors"
|
||||
import { described } from "./metadata"
|
||||
|
||||
const root = "/pty"
|
||||
@@ -76,7 +76,7 @@ export const PtyApi = HttpApi.make("pty")
|
||||
params: { ptyID: PtyID },
|
||||
query: WorkspaceRoutingQuery,
|
||||
success: described(Pty.Info, "Session info"),
|
||||
error: ApiNotFoundError,
|
||||
error: PtyNotFoundError,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "pty.get",
|
||||
@@ -89,7 +89,7 @@ export const PtyApi = HttpApi.make("pty")
|
||||
query: WorkspaceRoutingQuery,
|
||||
payload: Pty.UpdateInput,
|
||||
success: described(Pty.Info, "Updated session"),
|
||||
error: [HttpApiError.BadRequest, ApiNotFoundError],
|
||||
error: [PtyNotFoundError, HttpApiError.BadRequest],
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "pty.update",
|
||||
@@ -101,7 +101,7 @@ export const PtyApi = HttpApi.make("pty")
|
||||
params: { ptyID: PtyID },
|
||||
query: WorkspaceRoutingQuery,
|
||||
success: described(Schema.Boolean, "Session removed"),
|
||||
error: ApiNotFoundError,
|
||||
error: PtyNotFoundError,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "pty.remove",
|
||||
@@ -113,7 +113,7 @@ export const PtyApi = HttpApi.make("pty")
|
||||
params: { ptyID: PtyID },
|
||||
query: WorkspaceRoutingQuery,
|
||||
success: described(PtyTicket.ConnectToken, "WebSocket connect token"),
|
||||
error: [HttpApiError.Forbidden, ApiNotFoundError],
|
||||
error: [PtyForbiddenError, PtyNotFoundError],
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "pty.connectToken",
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from "@/server/shared/pty-ticket"
|
||||
import { Effect } from "effect"
|
||||
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
|
||||
import { HttpApiBuilder, HttpApiError } from "effect/unstable/httpapi"
|
||||
import { HttpApiBuilder } from "effect/unstable/httpapi"
|
||||
import * as Socket from "effect/unstable/socket/Socket"
|
||||
import { InstanceHttpApi } from "../api"
|
||||
import * as ApiError from "../errors"
|
||||
@@ -47,7 +47,11 @@ export const ptyHandlers = HttpApiBuilder.group(InstanceHttpApi, "pty", (handler
|
||||
|
||||
const get = Effect.fn("PtyHttpApi.get")(function* (ctx: { params: { ptyID: PtyID } }) {
|
||||
const info = yield* pty.get(ctx.params.ptyID)
|
||||
if (!info) return yield* ApiError.notFound("Session not found")
|
||||
if (!info)
|
||||
return yield* new ApiError.PtyNotFoundError({
|
||||
ptyID: ctx.params.ptyID,
|
||||
message: `PTY session not found: ${ctx.params.ptyID}`,
|
||||
})
|
||||
return info
|
||||
})
|
||||
|
||||
@@ -59,11 +63,20 @@ export const ptyHandlers = HttpApiBuilder.group(InstanceHttpApi, "pty", (handler
|
||||
...ctx.payload,
|
||||
size: ctx.payload.size ? { ...ctx.payload.size } : undefined,
|
||||
})
|
||||
if (!info) return yield* ApiError.notFound("Session not found")
|
||||
if (!info)
|
||||
return yield* new ApiError.PtyNotFoundError({
|
||||
ptyID: ctx.params.ptyID,
|
||||
message: `PTY session not found: ${ctx.params.ptyID}`,
|
||||
})
|
||||
return info
|
||||
})
|
||||
|
||||
const remove = Effect.fn("PtyHttpApi.remove")(function* (ctx: { params: { ptyID: PtyID } }) {
|
||||
if (!(yield* pty.get(ctx.params.ptyID)))
|
||||
return yield* new ApiError.PtyNotFoundError({
|
||||
ptyID: ctx.params.ptyID,
|
||||
message: `PTY session not found: ${ctx.params.ptyID}`,
|
||||
})
|
||||
yield* pty.remove(ctx.params.ptyID)
|
||||
return true
|
||||
})
|
||||
@@ -71,8 +84,12 @@ export const ptyHandlers = HttpApiBuilder.group(InstanceHttpApi, "pty", (handler
|
||||
const connectToken = Effect.fn("PtyHttpApi.connectToken")(function* (ctx: { params: { ptyID: PtyID } }) {
|
||||
const request = yield* HttpServerRequest.HttpServerRequest
|
||||
if (request.headers[PTY_CONNECT_TOKEN_HEADER] !== PTY_CONNECT_TOKEN_HEADER_VALUE || !validOrigin(request, cors))
|
||||
return yield* new HttpApiError.Forbidden({})
|
||||
if (!(yield* pty.get(ctx.params.ptyID))) return yield* ApiError.notFound("Session not found")
|
||||
return yield* new ApiError.PtyForbiddenError({ message: "Invalid PTY connect token request" })
|
||||
if (!(yield* pty.get(ctx.params.ptyID)))
|
||||
return yield* new ApiError.PtyNotFoundError({
|
||||
ptyID: ctx.params.ptyID,
|
||||
message: `PTY session not found: ${ctx.params.ptyID}`,
|
||||
})
|
||||
return yield* tickets.issue({ ptyID: ctx.params.ptyID, ...(yield* PtyTicket.scope) })
|
||||
})
|
||||
|
||||
|
||||
@@ -371,7 +371,6 @@ function referencesComponent(input: unknown, name: string): boolean {
|
||||
|
||||
function normalizeLegacyOperation(operation: OpenApiOperation, path: string, method: string) {
|
||||
if (path === "/experimental/console/switch" && method === "post") delete operation.responses?.["400"]
|
||||
if (path === "/pty/{ptyID}" && method === "put") delete operation.responses?.["404"]
|
||||
if ((path !== "/session/{sessionID}/message" && path !== "/session/{sessionID}/command") || method !== "post") return
|
||||
const response = operation.responses?.["200"]?.content?.["application/json"]
|
||||
if (!response) return
|
||||
|
||||
Reference in New Issue
Block a user