From 2b0e72ab79ad1f1075846073cb6a3f0fb7ea2c69 Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Fri, 15 May 2026 23:59:01 +0530 Subject: [PATCH] refactor(workspace): centralize adapter invocation (#27768) --- .../workspace-adapter-runtime.ts | 51 ++++++++++++ .../opencode/src/control-plane/workspace.ts | 82 ++++--------------- .../httpapi/middleware/workspace-routing.ts | 6 +- 3 files changed, 71 insertions(+), 68 deletions(-) create mode 100644 packages/opencode/src/control-plane/workspace-adapter-runtime.ts diff --git a/packages/opencode/src/control-plane/workspace-adapter-runtime.ts b/packages/opencode/src/control-plane/workspace-adapter-runtime.ts new file mode 100644 index 0000000000..235edc9d22 --- /dev/null +++ b/packages/opencode/src/control-plane/workspace-adapter-runtime.ts @@ -0,0 +1,51 @@ +import { Effect } from "effect" +import { EffectBridge } from "@/effect/bridge" +import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref" +import { getAdapter } from "./adapters" +import type { WorkspaceAdapter, WorkspaceInfo } from "./types" + +const context = Effect.gen(function* () { + return { + instance: yield* InstanceRef, + workspaceID: yield* WorkspaceRef, + } +}) + +export const target = (info: WorkspaceInfo) => + Effect.gen(function* () { + const adapter = getAdapter(info.projectID, info.type) + const ctx = yield* context + return yield* EffectBridge.fromPromise(() => adapter.target(info, ctx)) + }) + +export const configure = (adapter: WorkspaceAdapter, info: WorkspaceInfo) => + Effect.gen(function* () { + const ctx = yield* context + return yield* EffectBridge.fromPromise(() => adapter.configure(info, ctx)) + }) + +export const create = ( + adapter: WorkspaceAdapter, + info: WorkspaceInfo, + env: Record, + from?: WorkspaceInfo, +) => + Effect.gen(function* () { + const ctx = yield* context + return yield* EffectBridge.fromPromise(() => adapter.create(info, env, from, ctx)) + }) + +export const list = (adapter: WorkspaceAdapter) => + Effect.gen(function* () { + const ctx = yield* context + return yield* EffectBridge.fromPromise(() => Promise.resolve(adapter.list?.(ctx) ?? [])) + }) + +export const remove = (info: WorkspaceInfo) => + Effect.gen(function* () { + const adapter = getAdapter(info.projectID, info.type) + const ctx = yield* context + return yield* EffectBridge.fromPromise(() => adapter.remove(info, ctx)) + }) + +export * as WorkspaceAdapterRuntime from "./workspace-adapter-runtime" diff --git a/packages/opencode/src/control-plane/workspace.ts b/packages/opencode/src/control-plane/workspace.ts index 7bbe4aa325..a50df578f9 100644 --- a/packages/opencode/src/control-plane/workspace.ts +++ b/packages/opencode/src/control-plane/workspace.ts @@ -26,11 +26,11 @@ import { SessionID } from "@/session/schema" import { NotFoundError } from "@/storage/storage" import { errorData } from "@/util/error" import { waitEvent } from "./util" -import { EffectBridge } from "@/effect/bridge" -import { InstanceRef, WorkspaceRef } from "@/effect/instance-ref" +import { WorkspaceRef } from "@/effect/instance-ref" import { Vcs } from "@/project/vcs" import { InstanceStore } from "@/project/instance-store" import { InstanceBootstrap } from "@/project/bootstrap" +import { WorkspaceAdapterRuntime } from "./workspace-adapter-runtime" export const Info = Schema.Struct({ ...WorkspaceInfoSchema.fields, @@ -196,50 +196,6 @@ export const layer = Layer.effect( }) } - const adapterContext = Effect.gen(function* () { - return { - instance: yield* InstanceRef, - workspaceID: yield* WorkspaceRef, - } - }) - - const adapterTarget = (workspace: Info) => - Effect.gen(function* () { - const adapter = getAdapter(workspace.projectID, workspace.type) - const context = yield* adapterContext - return yield* EffectBridge.fromPromise(() => adapter.target(workspace, context)) - }) - - const adapterConfigure = (adapter: ReturnType, info: WorkspaceInfo) => - Effect.gen(function* () { - const context = yield* adapterContext - return yield* EffectBridge.fromPromise(() => adapter.configure(info, context)) - }) - - const adapterCreate = ( - adapter: ReturnType, - info: WorkspaceInfo, - env: Record, - from?: WorkspaceInfo, - ) => - Effect.gen(function* () { - const context = yield* adapterContext - return yield* EffectBridge.fromPromise(() => adapter.create(info, env, from, context)) - }) - - const adapterList = (adapter: ReturnType) => - Effect.gen(function* () { - const context = yield* adapterContext - return yield* EffectBridge.fromPromise(() => Promise.resolve(adapter.list?.(context) ?? [])) - }) - - const adapterRemove = (info: Info, type: string) => - Effect.gen(function* () { - const adapter = getAdapter(info.projectID, type) - const context = yield* adapterContext - return yield* EffectBridge.fromPromise(() => adapter.remove(info, context)) - }) - const connectSSE = Effect.fn("Workspace.connectSSE")(function* ( url: URL | string, headers: HeadersInit | undefined, @@ -325,7 +281,7 @@ export const layer = Layer.effect( const workspace = yield* get(input.workspaceID) if (!workspace) return input.fallback - const target = yield* adapterTarget(workspace) + const target = yield* WorkspaceAdapterRuntime.target(workspace) if (target.type === "local") { const store = yield* InstanceStore.Service @@ -438,7 +394,7 @@ export const layer = Layer.effect( }) const syncWorkspaceLoop = Effect.fn("Workspace.syncWorkspaceLoop")(function* (space: Info) { - const target = yield* adapterTarget(space) + const target = yield* WorkspaceAdapterRuntime.target(space) if (target.type === "local") return @@ -521,7 +477,7 @@ export const layer = Layer.effect( const startSync = Effect.fn("Workspace.startSync")(function* (space: Info) { if (!flags.experimentalWorkspaces) return - const target = yield* adapterTarget(space).pipe( + const target = yield* WorkspaceAdapterRuntime.target(space).pipe( Effect.catch((error) => Effect.sync(() => { setStatus(space.id, "error") @@ -572,7 +528,7 @@ export const layer = Layer.effect( const create = Effect.fn("Workspace.create")(function* (input: CreateInput) { const id = WorkspaceID.ascending(input.id) const adapter = getAdapter(input.projectID, input.type) - const config = yield* adapterConfigure(adapter, { + const config = yield* WorkspaceAdapterRuntime.configure(adapter, { ...input, id, name: Slug.create(), @@ -615,7 +571,7 @@ export const layer = Layer.effect( OTEL_RESOURCE_ATTRIBUTES: process.env.OTEL_RESOURCE_ATTRIBUTES, } - yield* adapterCreate(adapter, config, env) + yield* WorkspaceAdapterRuntime.create(adapter, config, env) yield* Effect.all( [ waitEvent({ @@ -654,7 +610,7 @@ export const layer = Layer.effect( if (current?.workspaceID) { const previous = yield* get(current.workspaceID) if (previous) { - const target = yield* adapterTarget(previous) + const target = yield* WorkspaceAdapterRuntime.target(previous) if (target.type === "remote") { yield* syncHistory(previous, target.url, target.headers).pipe( @@ -732,7 +688,7 @@ export const layer = Layer.effect( workspaceID, }) - const target = yield* adapterTarget(space) + const target = yield* WorkspaceAdapterRuntime.target(space) if (target.type === "local") { yield* sync.run(Session.Event.Updated, { @@ -885,16 +841,14 @@ export const layer = Layer.effect( const discovered = yield* Effect.forEach( registeredAdapters(project.id), ([type, adapter]) => - adapter.list - ? adapterList(adapter).pipe( - Effect.catchCause((error) => - Effect.sync(() => { - log.warn("workspace adapter list failed", { type, error }) - return [] - }), - ), - ) - : Effect.succeed([]), + WorkspaceAdapterRuntime.list(adapter).pipe( + Effect.catchCause((error) => + Effect.sync(() => { + log.warn("workspace adapter list failed", { type, error }) + return [] + }), + ), + ), { concurrency: "unbounded" }, ).pipe(Effect.map((items) => items.flat())) @@ -967,7 +921,7 @@ export const layer = Layer.effect( const info = fromRow(row) yield* Effect.catchCause( Effect.gen(function* () { - yield* adapterRemove(info, row.type) + yield* WorkspaceAdapterRuntime.remove(info) }), () => Effect.sync(() => { diff --git a/packages/opencode/src/server/routes/instance/httpapi/middleware/workspace-routing.ts b/packages/opencode/src/server/routes/instance/httpapi/middleware/workspace-routing.ts index 1d665fd5c9..cd9376f7f9 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/middleware/workspace-routing.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/middleware/workspace-routing.ts @@ -1,8 +1,7 @@ -import { getAdapter } from "@/control-plane/adapters" import { WorkspaceID } from "@/control-plane/schema" import type { Target } from "@/control-plane/types" import { Workspace } from "@/control-plane/workspace" -import { EffectBridge } from "@/effect/bridge" +import { WorkspaceAdapterRuntime } from "@/control-plane/workspace-adapter-runtime" import { Session } from "@/session/session" import { HttpApiProxy } from "./proxy" import * as Fence from "@/server/shared/fence" @@ -93,8 +92,7 @@ function missingWorkspaceResponse(id: WorkspaceID): HttpServerResponse.HttpServe } function resolveTarget(workspace: Workspace.Info): Effect.Effect { - const adapter = getAdapter(workspace.projectID, workspace.type) - return EffectBridge.fromPromise(() => adapter.target(workspace)) + return WorkspaceAdapterRuntime.target(workspace) } function proxyRemote(