diff --git a/packages/server/src/handlers/mcp.ts b/packages/server/src/handlers/mcp.ts index d6b47b27e632..ce98c860279c 100644 --- a/packages/server/src/handlers/mcp.ts +++ b/packages/server/src/handlers/mcp.ts @@ -15,15 +15,7 @@ export const McpHandler = HttpApiBuilder.group(Api, "server.mcp", (handlers) => "mcp.list", Effect.fn(function* () { const service = yield* Mcp.Service - return yield* response( - service - .servers() - .pipe( - Effect.map((servers) => - servers.map((info) => ({ name: info.name, status: info.status, integrationID: info.integrationID })), - ), - ), - ) + return yield* response(service.servers()) }), ) .handle( diff --git a/packages/server/src/handlers/message.ts b/packages/server/src/handlers/message.ts index 5bf8d6213952..5328c7a98b08 100644 --- a/packages/server/src/handlers/message.ts +++ b/packages/server/src/handlers/message.ts @@ -1,6 +1,6 @@ import { SessionMessage } from "@opencode/core/session/message" import { Session } from "@opencode/core/session" -import { Effect, Schema } from "effect" +import { Effect, Option, Schema } from "effect" import { HttpApiBuilder } from "effect/http-api" import { Api } from "../api" import { InvalidCursorError } from "@opencode/protocol/errors" @@ -14,14 +14,14 @@ const Cursor = Schema.Struct({ direction: Schema.Union([Schema.Literal("previous"), Schema.Literal("next")]), }) -const decodeCursor = Schema.decodeUnknownSync(Cursor) +const decodeCursor = Schema.decodeUnknownOption(Schema.fromJsonString(Cursor)) const cursor = { encode(message: SessionMessage.Info, order: "asc" | "desc", direction: "previous" | "next") { return Buffer.from(JSON.stringify({ id: message.id, order, direction })).toString("base64url") }, decode(input: string) { - return decodeCursor(JSON.parse(Buffer.from(input, "base64url").toString("utf8"))) + return decodeCursor(Buffer.from(input, "base64url").toString("utf8")) }, } @@ -34,10 +34,10 @@ export const MessageHandler = HttpApiBuilder.group(Api, "server.message", (handl Effect.fn(function* (ctx) { if (ctx.query.cursor && ctx.query.order !== undefined) return yield* new InvalidCursorError({ message: "Cursor cannot be combined with order" }) - const decoded = yield* Effect.try({ - try: () => (ctx.query.cursor ? cursor.decode(ctx.query.cursor) : undefined), - catch: () => new InvalidCursorError({ message: "Invalid cursor" }), - }) + const decoded = ctx.query.cursor + ? Option.getOrUndefined(cursor.decode(ctx.query.cursor)) ?? + (yield* new InvalidCursorError({ message: "Invalid cursor" })) + : undefined const order = decoded?.order ?? ctx.query.order ?? "desc" const messages = yield* session .messages({ diff --git a/packages/server/src/handlers/pty.ts b/packages/server/src/handlers/pty.ts index 4a2ebbe6a7ba..411994612fd3 100644 --- a/packages/server/src/handlers/pty.ts +++ b/packages/server/src/handlers/pty.ts @@ -16,20 +16,15 @@ import { PTY_CONNECT_TOKEN_HEADER_VALUE, } from "@opencode/protocol/groups/pty" import { locationErrors, requestRef, response } from "../location" -import { PtyEnvironment } from "../pty-environment" import { type Outbound, runPtySocket } from "./pty-socket" -const ticketScope = Effect.gen(function* () { - const location = yield* Location.Service - return { directory: location.directory as string, workspaceID: location.workspaceID } -}) +const notFound = (ptyID: Pty.ID) => new PtyNotFoundError({ ptyID, message: `PTY session not found: ${ptyID}` }) export const PtyHandler = HttpApiBuilder.group(Api, "server.pty", (handlers) => Effect.gen(function* () { const locations = yield* LocationServiceMap.Service const tickets = yield* PtyTicket.Service const cors = yield* CorsConfig - const environment = yield* PtyEnvironment.Service return handlers .handle( @@ -44,16 +39,10 @@ export const PtyHandler = HttpApiBuilder.group(Api, "server.pty", (handlers) => Effect.fn(function* (ctx) { const pty = yield* Pty.Service const location = yield* Location.Service - const cwd = ctx.payload.cwd || location.directory return yield* response( pty.create({ ...ctx.payload, - args: ctx.payload.args ? [...ctx.payload.args] : undefined, - cwd, - env: { - ...ctx.payload.env, - ...(yield* environment.get({ directory: location.directory, cwd })), - }, + cwd: ctx.payload.cwd || location.directory, }), ) }), @@ -63,16 +52,7 @@ export const PtyHandler = HttpApiBuilder.group(Api, "server.pty", (handlers) => Effect.fn(function* (ctx) { const pty = yield* Pty.Service return yield* response( - pty.get(ctx.params.ptyID).pipe( - Effect.catchTag( - "Pty.NotFoundError", - () => - new PtyNotFoundError({ - ptyID: ctx.params.ptyID, - message: `PTY session not found: ${ctx.params.ptyID}`, - }), - ), - ), + pty.get(ctx.params.ptyID).pipe(Effect.catchTag("Pty.NotFoundError", () => notFound(ctx.params.ptyID))), ) }), ) @@ -82,20 +62,8 @@ export const PtyHandler = HttpApiBuilder.group(Api, "server.pty", (handlers) => const pty = yield* Pty.Service return yield* response( pty - .update(ctx.params.ptyID, { - ...ctx.payload, - size: ctx.payload.size ? { ...ctx.payload.size } : undefined, - }) - .pipe( - Effect.catchTag( - "Pty.NotFoundError", - () => - new PtyNotFoundError({ - ptyID: ctx.params.ptyID, - message: `PTY session not found: ${ctx.params.ptyID}`, - }), - ), - ), + .update(ctx.params.ptyID, ctx.payload) + .pipe(Effect.catchTag("Pty.NotFoundError", () => notFound(ctx.params.ptyID))), ) }), ) @@ -103,16 +71,9 @@ export const PtyHandler = HttpApiBuilder.group(Api, "server.pty", (handlers) => "pty.remove", Effect.fn(function* (ctx) { const pty = yield* Pty.Service - yield* pty.remove(ctx.params.ptyID).pipe( - Effect.catchTag( - "Pty.NotFoundError", - () => - new PtyNotFoundError({ - ptyID: ctx.params.ptyID, - message: `PTY session not found: ${ctx.params.ptyID}`, - }), - ), - ) + yield* pty + .remove(ctx.params.ptyID) + .pipe(Effect.catchTag("Pty.NotFoundError", () => notFound(ctx.params.ptyID))) return HttpApiSchema.NoContent.make() }), ) @@ -128,17 +89,15 @@ export const PtyHandler = HttpApiBuilder.group(Api, "server.pty", (handlers) => ) return yield* new ForbiddenError({ message: "Invalid PTY connect token request" }) const pty = yield* Pty.Service - yield* pty.get(ctx.params.ptyID).pipe( - Effect.catchTag( - "Pty.NotFoundError", - () => - new PtyNotFoundError({ - ptyID: ctx.params.ptyID, - message: `PTY session not found: ${ctx.params.ptyID}`, - }), - ), + const location = yield* Location.Service + yield* pty.get(ctx.params.ptyID).pipe(Effect.catchTag("Pty.NotFoundError", () => notFound(ctx.params.ptyID))) + return yield* response( + tickets.issue({ + ptyID: ctx.params.ptyID, + directory: location.directory, + workspaceID: location.workspaceID, + }), ) - return yield* response(tickets.issue({ ptyID: ctx.params.ptyID, ...(yield* ticketScope) })) }), ) .handleRaw( diff --git a/packages/server/src/handlers/worktree.ts b/packages/server/src/handlers/worktree.ts index dc6867da86c1..490d75ee8c5a 100644 --- a/packages/server/src/handlers/worktree.ts +++ b/packages/server/src/handlers/worktree.ts @@ -26,10 +26,9 @@ export const WorktreeHandler = HttpApiBuilder.group(Api, "server.worktree", (han function badRequest(effect: Effect.Effect) { return effect.pipe( - Effect.catchTag("Project.NotFoundError", missingProject), Effect.mapError((error) => - error instanceof ProjectNotFoundError - ? error + error._tag === "Project.NotFoundError" + ? missingProject(error) : new WorktreeError({ name: "WorktreeError", data: { @@ -45,9 +44,7 @@ function badRequest(effect: Effect.Effect) { } function missingProject(error: Project.NotFoundError) { - return Effect.fail( - new ProjectNotFoundError({ projectID: error.projectID, message: `Project not found: ${error.projectID}` }), - ) + return new ProjectNotFoundError({ projectID: error.projectID, message: `Project not found: ${error.projectID}` }) } function message(error: Exclude) { diff --git a/packages/server/src/pty-environment.ts b/packages/server/src/pty-environment.ts deleted file mode 100644 index c93854e89fe9..000000000000 --- a/packages/server/src/pty-environment.ts +++ /dev/null @@ -1,19 +0,0 @@ -export * as PtyEnvironment from "./pty-environment" - -import { Context, Effect, Layer } from "effect" -import { makeGlobalNode } from "@opencode/util/effect/app-node" - -export interface Interface { - readonly get: (input: { directory: string; cwd: string }) => Effect.Effect> -} - -export class Service extends Context.Service()("@opencode/ServerPtyEnvironment") {} - -export const layer = Layer.succeed( - Service, - Service.of({ - get: () => Effect.succeed({}), - }), -) - -export const node = makeGlobalNode({ service: Service, layer, deps: [] }) diff --git a/packages/server/src/routes.ts b/packages/server/src/routes.ts index 8be99ce66231..861efc4ebbc1 100644 --- a/packages/server/src/routes.ts +++ b/packages/server/src/routes.ts @@ -41,7 +41,6 @@ import { CorsConfig } from "./cors" import { handlers } from "./handlers" import { authorizationLayer } from "./middleware/authorization" import { schemaErrorLayer } from "./middleware/schema-error" -import { PtyEnvironment } from "./pty-environment" import { ServerPairing } from "./pairing" import { layer } from "./location" import { formLocationLayer } from "./middleware/form-location" @@ -69,7 +68,6 @@ const applicationServiceNodes = [ PersistentPty.node, Credential.node, WellKnown.node, - PtyEnvironment.node, ServerPairing.node, LocationServiceMap.node, LocationActivity.node,