Răsfoiți Sursa

refactor(workspace): centralize adapter invocation (#27768)

Shoubhit Dash 3 luni în urmă
părinte
comite
2b0e72ab79

+ 51 - 0
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<string, string | undefined>,
+  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"

+ 18 - 64
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<typeof getAdapter>, info: WorkspaceInfo) =>
-      Effect.gen(function* () {
-        const context = yield* adapterContext
-        return yield* EffectBridge.fromPromise(() => adapter.configure(info, context))
-      })
-
-    const adapterCreate = (
-      adapter: ReturnType<typeof getAdapter>,
-      info: WorkspaceInfo,
-      env: Record<string, string | undefined>,
-      from?: WorkspaceInfo,
-    ) =>
-      Effect.gen(function* () {
-        const context = yield* adapterContext
-        return yield* EffectBridge.fromPromise(() => adapter.create(info, env, from, context))
-      })
-
-    const adapterList = (adapter: ReturnType<typeof getAdapter>) =>
-      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(() => {

+ 2 - 4
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<Target> {
-  const adapter = getAdapter(workspace.projectID, workspace.type)
-  return EffectBridge.fromPromise(() => adapter.target(workspace))
+  return WorkspaceAdapterRuntime.target(workspace)
 }
 
 function proxyRemote(