76 lines
2.9 KiB
TypeScript
76 lines
2.9 KiB
TypeScript
import { Database } from "@opencode-ai/core/database/database"
|
|
import { Location } from "@opencode-ai/core/location"
|
|
import { LocationServiceMap } from "@opencode-ai/core/location-services"
|
|
import { AbsolutePath } from "@opencode-ai/core/schema"
|
|
import { SessionV2 } from "@opencode-ai/core/session"
|
|
import { SessionTable } from "@opencode-ai/core/session/sql"
|
|
import { WorkspaceV2 } from "@opencode-ai/core/workspace"
|
|
import { InvalidRequestError, SessionNotFoundError } from "@opencode-ai/protocol/errors"
|
|
import { eq } from "drizzle-orm"
|
|
import { Effect, Layer, Schema } from "effect"
|
|
import { HttpRouter, HttpServerRequest } from "effect/unstable/http"
|
|
import { HttpApiMiddleware } from "effect/unstable/httpapi"
|
|
import { requestRef, type LocationServices } from "../location"
|
|
|
|
export class FormLocationMiddleware extends HttpApiMiddleware.Service<
|
|
FormLocationMiddleware,
|
|
{ provides: LocationServices }
|
|
>()("@opencode/HttpApiFormLocation", {
|
|
error: [InvalidRequestError, SessionNotFoundError],
|
|
}) {}
|
|
|
|
const decodeSessionID = Schema.decodeUnknownEffect(SessionV2.ID)
|
|
|
|
export const formLocationLayer = Layer.effect(
|
|
FormLocationMiddleware,
|
|
Effect.gen(function* () {
|
|
const { db } = yield* Database.Service
|
|
const locations = yield* LocationServiceMap.Service
|
|
|
|
return FormLocationMiddleware.of((effect) =>
|
|
Effect.gen(function* () {
|
|
const route = yield* HttpRouter.RouteContext
|
|
if (route.params.sessionID === "global") {
|
|
// Temporary MCP elicitation escape hatch. This is still Location-scoped; it only bypasses
|
|
// the session row lookup because some MCP elicitations cannot currently be attributed to
|
|
// a real session. Keep this undocumented and remove once elicitations carry session ownership.
|
|
const request = yield* HttpServerRequest.HttpServerRequest
|
|
return yield* effect.pipe(Effect.provide(locations.get(requestRef(request))))
|
|
}
|
|
|
|
const sessionID = yield* decodeSessionID(route.params.sessionID).pipe(
|
|
Effect.mapError(
|
|
() =>
|
|
new InvalidRequestError({
|
|
message: "Invalid session ID",
|
|
field: "sessionID",
|
|
}),
|
|
),
|
|
)
|
|
const row = yield* db
|
|
.select({ directory: SessionTable.directory, workspaceID: SessionTable.workspace_id })
|
|
.from(SessionTable)
|
|
.where(eq(SessionTable.id, sessionID))
|
|
.get()
|
|
.pipe(Effect.orDie)
|
|
if (!row) {
|
|
return yield* new SessionNotFoundError({
|
|
sessionID,
|
|
message: `Session not found: ${sessionID}`,
|
|
})
|
|
}
|
|
|
|
return yield* effect.pipe(
|
|
Effect.provide(
|
|
locations.get(
|
|
Location.Ref.make({
|
|
directory: AbsolutePath.make(row.directory),
|
|
workspaceID: row.workspaceID ? WorkspaceV2.ID.make(row.workspaceID) : undefined,
|
|
}),
|
|
),
|
|
),
|
|
)
|
|
}),
|
|
)
|
|
}),
|
|
)
|