From 080e92d40ad335fe889fa4ceca1b93a8f44fe4cf Mon Sep 17 00:00:00 2001
From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Date: Thu, 22 Oct 2026 10:48:03 -0500
Subject: [PATCH] refactor(server): remove dead PtyEnvironment service and
simplify route handlers (#57992)
---
packages/server/src/handlers/mcp.ts | 10 +---
packages/server/src/handlers/message.ts | 14 ++---
packages/server/src/handlers/pty.ts | 73 ++++++------------------
packages/server/src/handlers/worktree.ts | 9 +--
packages/server/src/pty-environment.ts | 19 ------
packages/server/src/routes.ts | 2 -
6 files changed, 27 insertions(+), 100 deletions(-)
delete mode 100644 packages/server/src/pty-environment.ts
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,