mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-08 01:52:33 +00:00
refactor: rename workspace adapters (#25272)
This commit is contained in:
parent
97ed9ba624
commit
8c79c58c4d
25 changed files with 225 additions and 225 deletions
|
|
@ -198,7 +198,7 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho
|
|||
| `project` | `bridged` | list, current, git init, update |
|
||||
| `file` | `bridged` partial | find text/file/symbol, list/content/status |
|
||||
| `mcp` | `bridged` | status, add, OAuth, connect/disconnect |
|
||||
| `workspace` | `bridged` | adaptor/list/status/create/remove/session-restore |
|
||||
| `workspace` | `bridged` | adapter/list/status/create/remove/session-restore |
|
||||
| top-level instance routes | `bridged` | path, vcs, command, agent, skill, lsp, formatter, dispose |
|
||||
| experimental JSON routes | `bridged` | console, tool, worktree list/mutations, global session list, resource list |
|
||||
| `session` | `bridged` | read, lifecycle, prompt, message/part mutations, revert, permission reply |
|
||||
|
|
@ -290,7 +290,7 @@ This checklist tracks bridge parity only. Checked routes are available through t
|
|||
|
||||
### Workspace Routes
|
||||
|
||||
- [x] `GET /experimental/workspace/adaptor` - list workspace adaptors.
|
||||
- [x] `GET /experimental/workspace/adapter` - list workspace adapters.
|
||||
- [x] `POST /experimental/workspace` - create workspace.
|
||||
- [x] `GET /experimental/workspace` - list workspaces.
|
||||
- [x] `GET /experimental/workspace/status` - workspace status.
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ piecewise.
|
|||
- [ ] `src/cli/cmd/tui/event.ts`
|
||||
- [ ] `src/cli/ui.ts`
|
||||
- [ ] `src/command/index.ts`
|
||||
- [x] `src/control-plane/adaptors/worktree.ts`
|
||||
- [x] `src/control-plane/adapters/worktree.ts`
|
||||
- [x] `src/control-plane/types.ts`
|
||||
- [x] `src/control-plane/workspace.ts`
|
||||
- [ ] `src/file/index.ts`
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { errorMessage } from "@/util/error"
|
|||
import { useSDK } from "../context/sdk"
|
||||
import { useToast } from "../ui/toast"
|
||||
|
||||
type Adaptor = {
|
||||
type Adapter = {
|
||||
type: string
|
||||
name: string
|
||||
description: string
|
||||
|
|
@ -108,26 +108,26 @@ export function DialogWorkspaceCreate(props: { onSelect: (workspaceID: string) =
|
|||
const sdk = useSDK()
|
||||
const toast = useToast()
|
||||
const [creating, setCreating] = createSignal<string>()
|
||||
const [adaptors, setAdaptors] = createSignal<Adaptor[]>()
|
||||
const [adapters, setAdapters] = createSignal<Adapter[]>()
|
||||
|
||||
onMount(() => {
|
||||
dialog.setSize("medium")
|
||||
void (async () => {
|
||||
const dir = sync.path.directory || sdk.directory
|
||||
const url = new URL("/experimental/workspace/adaptor", sdk.url)
|
||||
const url = new URL("/experimental/workspace/adapter", sdk.url)
|
||||
if (dir) url.searchParams.set("directory", dir)
|
||||
const res = await sdk
|
||||
.fetch(url)
|
||||
.then((x) => x.json() as Promise<Adaptor[]>)
|
||||
.then((x) => x.json() as Promise<Adapter[]>)
|
||||
.catch(() => undefined)
|
||||
if (!res) {
|
||||
toast.show({
|
||||
message: "Failed to load workspace adaptors",
|
||||
message: "Failed to load workspace adapters",
|
||||
variant: "error",
|
||||
})
|
||||
return
|
||||
}
|
||||
setAdaptors(res)
|
||||
setAdapters(res)
|
||||
})()
|
||||
})
|
||||
|
||||
|
|
@ -142,13 +142,13 @@ export function DialogWorkspaceCreate(props: { onSelect: (workspaceID: string) =
|
|||
},
|
||||
]
|
||||
}
|
||||
const list = adaptors()
|
||||
const list = adapters()
|
||||
if (!list) {
|
||||
return [
|
||||
{
|
||||
title: "Loading workspaces...",
|
||||
value: "loading" as const,
|
||||
description: "Fetching available workspace adaptors",
|
||||
description: "Fetching available workspace adapters",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
|||
45
packages/opencode/src/control-plane/adapters/index.ts
Normal file
45
packages/opencode/src/control-plane/adapters/index.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import type { ProjectID } from "@/project/schema"
|
||||
import type { WorkspaceAdapter, WorkspaceAdapterEntry } from "../types"
|
||||
import { WorktreeAdapter } from "./worktree"
|
||||
|
||||
const BUILTIN: Record<string, WorkspaceAdapter> = {
|
||||
worktree: WorktreeAdapter,
|
||||
}
|
||||
|
||||
const state = new Map<ProjectID, Map<string, WorkspaceAdapter>>()
|
||||
|
||||
export function getAdapter(projectID: ProjectID, type: string): WorkspaceAdapter {
|
||||
const custom = state.get(projectID)?.get(type)
|
||||
if (custom) return custom
|
||||
|
||||
const builtin = BUILTIN[type]
|
||||
if (builtin) return builtin
|
||||
|
||||
throw new Error(`Unknown workspace adapter: ${type}`)
|
||||
}
|
||||
|
||||
export async function listAdapters(projectID: ProjectID): Promise<WorkspaceAdapterEntry[]> {
|
||||
const builtin = await Promise.all(
|
||||
Object.entries(BUILTIN).map(async ([type, adapter]) => {
|
||||
return {
|
||||
type,
|
||||
name: adapter.name,
|
||||
description: adapter.description,
|
||||
}
|
||||
}),
|
||||
)
|
||||
const custom = [...(state.get(projectID)?.entries() ?? [])].map(([type, adapter]) => ({
|
||||
type,
|
||||
name: adapter.name,
|
||||
description: adapter.description,
|
||||
}))
|
||||
return [...builtin, ...custom]
|
||||
}
|
||||
|
||||
// Plugins can be loaded per-project so we need to scope them. If you
|
||||
// want to install a global one pass `ProjectID.global`
|
||||
export function registerAdapter(projectID: ProjectID, type: string, adapter: WorkspaceAdapter) {
|
||||
const adapters = state.get(projectID) ?? new Map<string, WorkspaceAdapter>()
|
||||
adapters.set(type, adapter)
|
||||
state.set(projectID, adapters)
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Schema } from "effect"
|
||||
import { type WorkspaceAdaptor, WorkspaceInfo } from "../types"
|
||||
import { type WorkspaceAdapter, WorkspaceInfo } from "../types"
|
||||
|
||||
const WorktreeConfig = Schema.Struct({
|
||||
name: WorkspaceInfo.fields.name,
|
||||
|
|
@ -13,7 +13,7 @@ async function loadWorktree() {
|
|||
return { AppRuntime, Worktree }
|
||||
}
|
||||
|
||||
export const WorktreeAdaptor: WorkspaceAdaptor = {
|
||||
export const WorktreeAdapter: WorkspaceAdapter = {
|
||||
name: "Worktree",
|
||||
description: "Create a git worktree",
|
||||
async configure(info) {
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
import type { ProjectID } from "@/project/schema"
|
||||
import type { WorkspaceAdaptor, WorkspaceAdaptorEntry } from "../types"
|
||||
import { WorktreeAdaptor } from "./worktree"
|
||||
|
||||
const BUILTIN: Record<string, WorkspaceAdaptor> = {
|
||||
worktree: WorktreeAdaptor,
|
||||
}
|
||||
|
||||
const state = new Map<ProjectID, Map<string, WorkspaceAdaptor>>()
|
||||
|
||||
export function getAdaptor(projectID: ProjectID, type: string): WorkspaceAdaptor {
|
||||
const custom = state.get(projectID)?.get(type)
|
||||
if (custom) return custom
|
||||
|
||||
const builtin = BUILTIN[type]
|
||||
if (builtin) return builtin
|
||||
|
||||
throw new Error(`Unknown workspace adaptor: ${type}`)
|
||||
}
|
||||
|
||||
export async function listAdaptors(projectID: ProjectID): Promise<WorkspaceAdaptorEntry[]> {
|
||||
const builtin = await Promise.all(
|
||||
Object.entries(BUILTIN).map(async ([type, adaptor]) => {
|
||||
return {
|
||||
type,
|
||||
name: adaptor.name,
|
||||
description: adaptor.description,
|
||||
}
|
||||
}),
|
||||
)
|
||||
const custom = [...(state.get(projectID)?.entries() ?? [])].map(([type, adaptor]) => ({
|
||||
type,
|
||||
name: adaptor.name,
|
||||
description: adaptor.description,
|
||||
}))
|
||||
return [...builtin, ...custom]
|
||||
}
|
||||
|
||||
// Plugins can be loaded per-project so we need to scope them. If you
|
||||
// want to install a global one pass `ProjectID.global`
|
||||
export function registerAdaptor(projectID: ProjectID, type: string, adaptor: WorkspaceAdaptor) {
|
||||
const adaptors = state.get(projectID) ?? new Map<string, WorkspaceAdaptor>()
|
||||
adaptors.set(type, adaptor)
|
||||
state.set(projectID, adaptors)
|
||||
}
|
||||
|
|
@ -17,12 +17,12 @@ export const WorkspaceInfo = Schema.Struct({
|
|||
.pipe(withStatics((s) => ({ zod: zod(s) })))
|
||||
export type WorkspaceInfo = DeepMutable<Schema.Schema.Type<typeof WorkspaceInfo>>
|
||||
|
||||
export const WorkspaceAdaptorEntry = Schema.Struct({
|
||||
export const WorkspaceAdapterEntry = Schema.Struct({
|
||||
type: Schema.String,
|
||||
name: Schema.String,
|
||||
description: Schema.String,
|
||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
||||
export type WorkspaceAdaptorEntry = Schema.Schema.Type<typeof WorkspaceAdaptorEntry>
|
||||
export type WorkspaceAdapterEntry = Schema.Schema.Type<typeof WorkspaceAdapterEntry>
|
||||
|
||||
export type Target =
|
||||
| {
|
||||
|
|
@ -35,7 +35,7 @@ export type Target =
|
|||
headers?: HeadersInit
|
||||
}
|
||||
|
||||
export type WorkspaceAdaptor = {
|
||||
export type WorkspaceAdapter = {
|
||||
name: string
|
||||
description: string
|
||||
configure(info: WorkspaceInfo): WorkspaceInfo | Promise<WorkspaceInfo>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { Filesystem } from "@/util/filesystem"
|
|||
import { ProjectID } from "@/project/schema"
|
||||
import { Slug } from "@opencode-ai/core/util/slug"
|
||||
import { WorkspaceTable } from "./workspace.sql"
|
||||
import { getAdaptor } from "./adaptors"
|
||||
import { getAdapter } from "./adapters"
|
||||
import { type WorkspaceInfo, WorkspaceInfo as WorkspaceInfoSchema } from "./types"
|
||||
import { WorkspaceID } from "./schema"
|
||||
import { Session } from "@/session/session"
|
||||
|
|
@ -335,8 +335,8 @@ export const layer = Layer.effect(
|
|||
})
|
||||
|
||||
const syncWorkspaceLoop = Effect.fn("Workspace.syncWorkspaceLoop")(function* (space: Info) {
|
||||
const adaptor = getAdaptor(space.projectID, space.type)
|
||||
const target = yield* Effect.promise(() => Promise.resolve(adaptor.target(space)))
|
||||
const adapter = getAdapter(space.projectID, space.type)
|
||||
const target = yield* Effect.promise(() => Promise.resolve(adapter.target(space)))
|
||||
|
||||
if (target.type === "local") return
|
||||
|
||||
|
|
@ -419,8 +419,8 @@ export const layer = Layer.effect(
|
|||
const startSync = Effect.fn("Workspace.startSync")(function* (space: Info) {
|
||||
if (!Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) return
|
||||
|
||||
const adaptor = getAdaptor(space.projectID, space.type)
|
||||
const target = yield* Effect.promise(() => Promise.resolve(adaptor.target(space)))
|
||||
const adapter = getAdapter(space.projectID, space.type)
|
||||
const target = yield* Effect.promise(() => Promise.resolve(adapter.target(space)))
|
||||
|
||||
if (target.type === "local") {
|
||||
setStatus(space.id, (yield* Effect.promise(() => Filesystem.exists(target.directory))) ? "connected" : "error")
|
||||
|
|
@ -458,9 +458,9 @@ export const layer = Layer.effect(
|
|||
|
||||
const create = Effect.fn("Workspace.create")(function* (input: CreateInput) {
|
||||
const id = WorkspaceID.ascending(input.id)
|
||||
const adaptor = getAdaptor(input.projectID, input.type)
|
||||
const adapter = getAdapter(input.projectID, input.type)
|
||||
const config = yield* Effect.promise(() =>
|
||||
Promise.resolve(adaptor.configure({ ...input, id, name: Slug.create(), directory: null })),
|
||||
Promise.resolve(adapter.configure({ ...input, id, name: Slug.create(), directory: null })),
|
||||
)
|
||||
|
||||
const info: Info = {
|
||||
|
|
@ -496,7 +496,7 @@ export const layer = Layer.effect(
|
|||
OTEL_RESOURCE_ATTRIBUTES: process.env.OTEL_RESOURCE_ATTRIBUTES,
|
||||
}
|
||||
|
||||
yield* Effect.promise(() => adaptor.create(config, env))
|
||||
yield* Effect.promise(() => adapter.create(config, env))
|
||||
yield* Effect.all(
|
||||
[
|
||||
waitEvent({
|
||||
|
|
@ -531,8 +531,8 @@ export const layer = Layer.effect(
|
|||
workspaceID: input.workspaceID,
|
||||
})
|
||||
|
||||
const adaptor = getAdaptor(space.projectID, space.type)
|
||||
const target = yield* Effect.promise(() => Promise.resolve(adaptor.target(space)))
|
||||
const adapter = getAdapter(space.projectID, space.type)
|
||||
const target = yield* Effect.promise(() => Promise.resolve(adapter.target(space)))
|
||||
|
||||
yield* sync.run(Session.Event.Updated, {
|
||||
sessionID: input.sessionID,
|
||||
|
|
@ -726,12 +726,12 @@ export const layer = Layer.effect(
|
|||
const info = fromRow(row)
|
||||
yield* Effect.catch(
|
||||
Effect.gen(function* () {
|
||||
const adaptor = getAdaptor(info.projectID, row.type)
|
||||
yield* Effect.tryPromise(() => Promise.resolve(adaptor.remove(info)))
|
||||
const adapter = getAdapter(info.projectID, row.type)
|
||||
yield* Effect.tryPromise(() => Promise.resolve(adapter.remove(info)))
|
||||
}),
|
||||
() =>
|
||||
Effect.sync(() => {
|
||||
log.error("adaptor not available when removing workspace", { type: row.type })
|
||||
log.error("adapter not available when removing workspace", { type: row.type })
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type {
|
|||
PluginInput,
|
||||
Plugin as PluginInstance,
|
||||
PluginModule,
|
||||
WorkspaceAdaptor as PluginWorkspaceAdaptor,
|
||||
WorkspaceAdapter as PluginWorkspaceAdapter,
|
||||
} from "@opencode-ai/plugin"
|
||||
import { Config } from "@/config/config"
|
||||
import { Bus } from "../bus"
|
||||
|
|
@ -24,8 +24,8 @@ import { InstanceState } from "@/effect/instance-state"
|
|||
import { errorMessage } from "@/util/error"
|
||||
import { PluginLoader } from "./loader"
|
||||
import { parsePluginSpecifier, readPluginId, readV1Plugin, resolvePluginId } from "./shared"
|
||||
import { registerAdaptor } from "@/control-plane/adaptors"
|
||||
import type { WorkspaceAdaptor } from "@/control-plane/types"
|
||||
import { registerAdapter } from "@/control-plane/adapters"
|
||||
import type { WorkspaceAdapter } from "@/control-plane/types"
|
||||
|
||||
const log = Log.create({ service: "plugin" })
|
||||
|
||||
|
|
@ -138,8 +138,8 @@ export const layer = Layer.effect(
|
|||
worktree: ctx.worktree,
|
||||
directory: ctx.directory,
|
||||
experimental_workspace: {
|
||||
register(type: string, adaptor: PluginWorkspaceAdaptor) {
|
||||
registerAdaptor(ctx.project.id, type, adaptor as WorkspaceAdaptor)
|
||||
register(type: string, adapter: PluginWorkspaceAdapter) {
|
||||
registerAdapter(ctx.project.id, type, adapter as WorkspaceAdapter)
|
||||
},
|
||||
},
|
||||
get serverUrl(): URL {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ import { Hono } from "hono"
|
|||
import { describeRoute, resolver, validator } from "hono-openapi"
|
||||
import z from "zod"
|
||||
import { Effect } from "effect"
|
||||
import { listAdaptors } from "@/control-plane/adaptors"
|
||||
import { listAdapters } from "@/control-plane/adapters"
|
||||
import { Workspace } from "@/control-plane/workspace"
|
||||
import { AppRuntime } from "@/effect/app-runtime"
|
||||
import { WorkspaceAdaptorEntry } from "@/control-plane/types"
|
||||
import { WorkspaceAdapterEntry } from "@/control-plane/types"
|
||||
import { zodObject } from "@/util/effect-zod"
|
||||
import { Instance } from "@/project/instance"
|
||||
import { errors } from "../../error"
|
||||
|
|
@ -18,24 +18,24 @@ const log = Log.create({ service: "server.workspace" })
|
|||
export const WorkspaceRoutes = lazy(() =>
|
||||
new Hono()
|
||||
.get(
|
||||
"/adaptor",
|
||||
"/adapter",
|
||||
describeRoute({
|
||||
summary: "List workspace adaptors",
|
||||
description: "List all available workspace adaptors for the current project.",
|
||||
operationId: "experimental.workspace.adaptor.list",
|
||||
summary: "List workspace adapters",
|
||||
description: "List all available workspace adapters for the current project.",
|
||||
operationId: "experimental.workspace.adapter.list",
|
||||
responses: {
|
||||
200: {
|
||||
description: "Workspace adaptors",
|
||||
description: "Workspace adapters",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resolver(z.array(zodObject(WorkspaceAdaptorEntry))),
|
||||
schema: resolver(z.array(zodObject(WorkspaceAdapterEntry))),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
return c.json(await listAdaptors(Instance.project.id))
|
||||
return c.json(await listAdapters(Instance.project.id))
|
||||
},
|
||||
)
|
||||
.post(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Workspace } from "@/control-plane/workspace"
|
||||
import { WorkspaceAdaptorEntry } from "@/control-plane/types"
|
||||
import { WorkspaceAdapterEntry } from "@/control-plane/types"
|
||||
import { NonNegativeInt } from "@/util/schema"
|
||||
import { Schema, Struct } from "effect"
|
||||
import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
|
|
@ -16,7 +16,7 @@ export const SessionRestoreResponse = Schema.Struct({
|
|||
})
|
||||
|
||||
export const WorkspacePaths = {
|
||||
adaptors: `${root}/adaptor`,
|
||||
adapters: `${root}/adapter`,
|
||||
list: root,
|
||||
status: `${root}/status`,
|
||||
remove: `${root}/:id`,
|
||||
|
|
@ -27,13 +27,13 @@ export const WorkspaceApi = HttpApi.make("workspace")
|
|||
.add(
|
||||
HttpApiGroup.make("workspace")
|
||||
.add(
|
||||
HttpApiEndpoint.get("adaptors", WorkspacePaths.adaptors, {
|
||||
success: described(Schema.Array(WorkspaceAdaptorEntry), "Workspace adaptors"),
|
||||
HttpApiEndpoint.get("adapters", WorkspacePaths.adapters, {
|
||||
success: described(Schema.Array(WorkspaceAdapterEntry), "Workspace adapters"),
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "experimental.workspace.adaptor.list",
|
||||
summary: "List workspace adaptors",
|
||||
description: "List all available workspace adaptors for the current project.",
|
||||
identifier: "experimental.workspace.adapter.list",
|
||||
summary: "List workspace adapters",
|
||||
description: "List all available workspace adapters for the current project.",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("list", WorkspacePaths.list, {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { listAdaptors } from "@/control-plane/adaptors"
|
||||
import { listAdapters } from "@/control-plane/adapters"
|
||||
import { Workspace } from "@/control-plane/workspace"
|
||||
import * as InstanceState from "@/effect/instance-state"
|
||||
import { Effect } from "effect"
|
||||
|
|
@ -10,9 +10,9 @@ export const workspaceHandlers = HttpApiBuilder.group(InstanceHttpApi, "workspac
|
|||
Effect.gen(function* () {
|
||||
const workspace = yield* Workspace.Service
|
||||
|
||||
const adaptors = Effect.fn("WorkspaceHttpApi.adaptors")(function* () {
|
||||
const adapters = Effect.fn("WorkspaceHttpApi.adapters")(function* () {
|
||||
const instance = yield* InstanceState.context
|
||||
return yield* Effect.promise(() => listAdaptors(instance.project.id))
|
||||
return yield* Effect.promise(() => listAdapters(instance.project.id))
|
||||
})
|
||||
|
||||
const list = Effect.fn("WorkspaceHttpApi.list")(function* () {
|
||||
|
|
@ -51,7 +51,7 @@ export const workspaceHandlers = HttpApiBuilder.group(InstanceHttpApi, "workspac
|
|||
})
|
||||
|
||||
return handlers
|
||||
.handle("adaptors", adaptors)
|
||||
.handle("adapters", adapters)
|
||||
.handle("list", list)
|
||||
.handle("create", create)
|
||||
.handle("status", status)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getAdaptor } from "@/control-plane/adaptors"
|
||||
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"
|
||||
|
|
@ -89,8 +89,8 @@ function missingWorkspaceResponse(id: WorkspaceID): HttpServerResponse.HttpServe
|
|||
|
||||
function resolveTarget(workspace: Workspace.Info): Effect.Effect<Target> {
|
||||
return Effect.gen(function* () {
|
||||
const adaptor = yield* Effect.sync(() => getAdaptor(workspace.projectID, workspace.type))
|
||||
return yield* Effect.promise(() => Promise.resolve(adaptor.target(workspace)))
|
||||
const adapter = yield* Effect.sync(() => getAdapter(workspace.projectID, workspace.type))
|
||||
return yield* Effect.promise(() => Promise.resolve(adapter.target(workspace)))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { MiddlewareHandler } from "hono"
|
||||
import type { UpgradeWebSocket } from "hono/ws"
|
||||
import { getAdaptor } from "@/control-plane/adaptors"
|
||||
import { getAdapter } from "@/control-plane/adapters"
|
||||
import { WorkspaceID } from "@/control-plane/schema"
|
||||
import { WorkspaceContext } from "@/control-plane/workspace-context"
|
||||
import { Workspace } from "@/control-plane/workspace"
|
||||
|
|
@ -91,8 +91,8 @@ export function WorkspaceRouterMiddleware(upgrade: UpgradeWebSocket): Middleware
|
|||
return next()
|
||||
}
|
||||
|
||||
const adaptor = getAdaptor(workspace.projectID, workspace.type)
|
||||
const target = await adaptor.target(workspace)
|
||||
const adapter = getAdapter(workspace.projectID, workspace.type)
|
||||
const target = await adapter.target(workspace)
|
||||
|
||||
if (target.type === "local") {
|
||||
return WorkspaceContext.provide({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import { getAdaptor, registerAdaptor } from "../../src/control-plane/adaptors"
|
||||
import { getAdapter, registerAdapter } from "../../src/control-plane/adapters"
|
||||
import { ProjectID } from "../../src/project/schema"
|
||||
import type { WorkspaceInfo } from "../../src/control-plane/types"
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ function info(projectID: WorkspaceInfo["projectID"], type: string): WorkspaceInf
|
|||
}
|
||||
}
|
||||
|
||||
function adaptor(dir: string) {
|
||||
function adapter(dir: string) {
|
||||
return {
|
||||
name: dir,
|
||||
description: dir,
|
||||
|
|
@ -33,19 +33,19 @@ function adaptor(dir: string) {
|
|||
}
|
||||
}
|
||||
|
||||
describe("control-plane/adaptors", () => {
|
||||
test("isolates custom adaptors by project", async () => {
|
||||
describe("control-plane/adapters", () => {
|
||||
test("isolates custom adapters by project", async () => {
|
||||
const type = `demo-${Math.random().toString(36).slice(2)}`
|
||||
const one = ProjectID.make(`project-${Math.random().toString(36).slice(2)}`)
|
||||
const two = ProjectID.make(`project-${Math.random().toString(36).slice(2)}`)
|
||||
registerAdaptor(one, type, adaptor("/one"))
|
||||
registerAdaptor(two, type, adaptor("/two"))
|
||||
registerAdapter(one, type, adapter("/one"))
|
||||
registerAdapter(two, type, adapter("/two"))
|
||||
|
||||
expect(await (await getAdaptor(one, type)).target(info(one, type))).toEqual({
|
||||
expect(await (await getAdapter(one, type)).target(info(one, type))).toEqual({
|
||||
type: "local",
|
||||
directory: "/one",
|
||||
})
|
||||
expect(await (await getAdaptor(two, type)).target(info(two, type))).toEqual({
|
||||
expect(await (await getAdapter(two, type)).target(info(two, type))).toEqual({
|
||||
type: "local",
|
||||
directory: "/two",
|
||||
})
|
||||
|
|
@ -54,16 +54,16 @@ describe("control-plane/adaptors", () => {
|
|||
test("latest install wins within a project", async () => {
|
||||
const type = `demo-${Math.random().toString(36).slice(2)}`
|
||||
const id = ProjectID.make(`project-${Math.random().toString(36).slice(2)}`)
|
||||
registerAdaptor(id, type, adaptor("/one"))
|
||||
registerAdapter(id, type, adapter("/one"))
|
||||
|
||||
expect(await (await getAdaptor(id, type)).target(info(id, type))).toEqual({
|
||||
expect(await (await getAdapter(id, type)).target(info(id, type))).toEqual({
|
||||
type: "local",
|
||||
directory: "/one",
|
||||
})
|
||||
|
||||
registerAdaptor(id, type, adaptor("/two"))
|
||||
registerAdapter(id, type, adapter("/two"))
|
||||
|
||||
expect(await (await getAdaptor(id, type)).target(info(id, type))).toEqual({
|
||||
expect(await (await getAdapter(id, type)).target(info(id, type))).toEqual({
|
||||
type: "local",
|
||||
directory: "/two",
|
||||
})
|
||||
|
|
@ -23,10 +23,10 @@ import { EventSequenceTable, EventTable } from "@/sync/event.sql"
|
|||
import { resetDatabase } from "../fixture/db"
|
||||
import { provideTmpdirInstance, tmpdir } from "../fixture/fixture"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { registerAdaptor } from "../../src/control-plane/adaptors"
|
||||
import { registerAdapter } from "../../src/control-plane/adapters"
|
||||
import { WorkspaceID } from "../../src/control-plane/schema"
|
||||
import { WorkspaceTable } from "../../src/control-plane/workspace.sql"
|
||||
import type { Target, WorkspaceAdaptor, WorkspaceInfo } from "../../src/control-plane/types"
|
||||
import type { Target, WorkspaceAdapter, WorkspaceInfo } from "../../src/control-plane/types"
|
||||
import * as WorkspaceOld from "../../src/control-plane/workspace"
|
||||
import { AppRuntime } from "@/effect/app-runtime"
|
||||
|
||||
|
|
@ -53,8 +53,8 @@ type RecordedCreate = {
|
|||
from?: WorkspaceInfo
|
||||
}
|
||||
|
||||
type RecordedAdaptor = {
|
||||
adaptor: WorkspaceAdaptor
|
||||
type RecordedAdapter = {
|
||||
adapter: WorkspaceAdapter
|
||||
calls: {
|
||||
configure: WorkspaceInfo[]
|
||||
create: RecordedCreate[]
|
||||
|
|
@ -165,13 +165,13 @@ function eventuallyEffect(effect: Effect.Effect<void>, timeout = 1500) {
|
|||
})
|
||||
}
|
||||
|
||||
function recordedAdaptor(input: {
|
||||
function recordedAdapter(input: {
|
||||
target: (info: WorkspaceInfo) => Target | Promise<Target>
|
||||
configure?: (info: WorkspaceInfo) => WorkspaceInfo | Promise<WorkspaceInfo>
|
||||
create?: (info: WorkspaceInfo, env: Record<string, string | undefined>, from?: WorkspaceInfo) => Promise<void>
|
||||
remove?: (info: WorkspaceInfo) => Promise<void>
|
||||
}): RecordedAdaptor {
|
||||
const calls: RecordedAdaptor["calls"] = {
|
||||
}): RecordedAdapter {
|
||||
const calls: RecordedAdapter["calls"] = {
|
||||
configure: [],
|
||||
create: [],
|
||||
remove: [],
|
||||
|
|
@ -180,7 +180,7 @@ function recordedAdaptor(input: {
|
|||
|
||||
return {
|
||||
calls,
|
||||
adaptor: {
|
||||
adapter: {
|
||||
name: "recorded",
|
||||
description: "recorded",
|
||||
configure(info) {
|
||||
|
|
@ -207,8 +207,8 @@ function recordedAdaptor(input: {
|
|||
}
|
||||
}
|
||||
|
||||
function localAdaptor(dir: string, input?: { createDir?: boolean; remove?: (info: WorkspaceInfo) => Promise<void> }) {
|
||||
return recordedAdaptor({
|
||||
function localAdapter(dir: string, input?: { createDir?: boolean; remove?: (info: WorkspaceInfo) => Promise<void> }) {
|
||||
return recordedAdapter({
|
||||
configure(info) {
|
||||
return { ...info, directory: dir }
|
||||
},
|
||||
|
|
@ -223,8 +223,8 @@ function localAdaptor(dir: string, input?: { createDir?: boolean; remove?: (info
|
|||
})
|
||||
}
|
||||
|
||||
function remoteAdaptor(url: string, input?: { directory?: string | null; headers?: HeadersInit }) {
|
||||
return recordedAdaptor({
|
||||
function remoteAdapter(url: string, input?: { directory?: string | null; headers?: HeadersInit }) {
|
||||
return recordedAdapter({
|
||||
configure(info) {
|
||||
return { ...info, directory: input?.directory ?? info.directory }
|
||||
},
|
||||
|
|
@ -429,7 +429,7 @@ describe("workspace-old CRUD", () => {
|
|||
const workspaceID = WorkspaceID.ascending("wrk_create_local")
|
||||
const type = unique("create-local")
|
||||
const targetDir = path.join(dir, "created-local")
|
||||
const recorded = recordedAdaptor({
|
||||
const recorded = recordedAdapter({
|
||||
configure(info) {
|
||||
return {
|
||||
...info,
|
||||
|
|
@ -446,7 +446,7 @@ describe("workspace-old CRUD", () => {
|
|||
return { type: "local", directory: targetDir }
|
||||
},
|
||||
})
|
||||
registerAdaptor(Instance.project.id, type, recorded.adaptor)
|
||||
registerAdapter(Instance.project.id, type, recorded.adapter)
|
||||
|
||||
const info = await createWorkspace({
|
||||
id: workspaceID,
|
||||
|
|
@ -489,17 +489,17 @@ describe("workspace-old CRUD", () => {
|
|||
test("create propagates configure failures and does not insert a workspace", async () => {
|
||||
await withInstance(async () => {
|
||||
const type = unique("configure-failure")
|
||||
registerAdaptor(
|
||||
registerAdapter(
|
||||
Instance.project.id,
|
||||
type,
|
||||
recordedAdaptor({
|
||||
recordedAdapter({
|
||||
configure() {
|
||||
throw new Error("configure exploded")
|
||||
},
|
||||
target() {
|
||||
return { type: "local", directory: "/unused" }
|
||||
},
|
||||
}).adaptor,
|
||||
}).adapter,
|
||||
)
|
||||
|
||||
await expect(
|
||||
|
|
@ -509,10 +509,10 @@ describe("workspace-old CRUD", () => {
|
|||
})
|
||||
})
|
||||
|
||||
test("create leaves the inserted row when adaptor create fails", async () => {
|
||||
test("create leaves the inserted row when adapter create fails", async () => {
|
||||
await withInstance(async () => {
|
||||
const type = unique("create-failure")
|
||||
const recorded = recordedAdaptor({
|
||||
const recorded = recordedAdapter({
|
||||
async create() {
|
||||
throw new Error("create exploded")
|
||||
},
|
||||
|
|
@ -520,7 +520,7 @@ describe("workspace-old CRUD", () => {
|
|||
return { type: "local", directory: "/unused" }
|
||||
},
|
||||
})
|
||||
registerAdaptor(Instance.project.id, type, recorded.adaptor)
|
||||
registerAdapter(Instance.project.id, type, recorded.adapter)
|
||||
|
||||
await expect(
|
||||
createWorkspace({ type, branch: "branch", projectID: Instance.project.id, extra: { x: 1 } }),
|
||||
|
|
@ -538,8 +538,8 @@ describe("workspace-old CRUD", () => {
|
|||
await withInstance(async (dir) => {
|
||||
const type = unique("local-error")
|
||||
const missing = path.join(dir, "missing-local-target")
|
||||
const recorded = localAdaptor(missing, { createDir: false })
|
||||
registerAdaptor(Instance.project.id, type, recorded.adaptor)
|
||||
const recorded = localAdapter(missing, { createDir: false })
|
||||
registerAdapter(Instance.project.id, type, recorded.adapter)
|
||||
|
||||
const info = await createWorkspace({ type, branch: null, projectID: Instance.project.id, extra: null })
|
||||
|
||||
|
|
@ -576,8 +576,8 @@ describe("workspace-old CRUD", () => {
|
|||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const type = unique("remote-create")
|
||||
const recorded = remoteAdaptor(`${url}/base/?ignored=1#hash`, { directory: dir })
|
||||
registerAdaptor(Instance.project.id, type, recorded.adaptor)
|
||||
const recorded = remoteAdapter(`${url}/base/?ignored=1#hash`, { directory: dir })
|
||||
registerAdapter(Instance.project.id, type, recorded.adapter)
|
||||
|
||||
const info = yield* workspace.create({ type, branch: null, projectID: Instance.project.id, extra: null })
|
||||
|
||||
|
|
@ -603,11 +603,11 @@ describe("workspace-old CRUD", () => {
|
|||
})
|
||||
})
|
||||
|
||||
test("remove deletes the workspace, associated sessions, adaptor resources, and status", async () => {
|
||||
test("remove deletes the workspace, associated sessions, adapter resources, and status", async () => {
|
||||
await withInstance(async (dir) => {
|
||||
const type = unique("remove-local")
|
||||
const recorded = localAdaptor(path.join(dir, "remove-local"))
|
||||
registerAdaptor(Instance.project.id, type, recorded.adaptor)
|
||||
const recorded = localAdapter(path.join(dir, "remove-local"))
|
||||
registerAdapter(Instance.project.id, type, recorded.adapter)
|
||||
const info = await createWorkspace({ type, branch: null, projectID: Instance.project.id, extra: null })
|
||||
const one = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))
|
||||
const two = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))
|
||||
|
|
@ -628,21 +628,21 @@ describe("workspace-old CRUD", () => {
|
|||
})
|
||||
})
|
||||
|
||||
test("remove still deletes the row when the adaptor cannot remove resources", async () => {
|
||||
test("remove still deletes the row when the adapter cannot remove resources", async () => {
|
||||
await withInstance(async () => {
|
||||
const type = unique("remove-throws")
|
||||
const info = workspaceInfo(Instance.project.id, type, { id: WorkspaceID.ascending("wrk_remove_throws") })
|
||||
registerAdaptor(
|
||||
registerAdapter(
|
||||
Instance.project.id,
|
||||
type,
|
||||
recordedAdaptor({
|
||||
recordedAdapter({
|
||||
async remove() {
|
||||
throw new Error("remove exploded")
|
||||
},
|
||||
target() {
|
||||
return { type: "local", directory: "/unused" }
|
||||
},
|
||||
}).adaptor,
|
||||
}).adapter,
|
||||
)
|
||||
insertWorkspace(info)
|
||||
|
||||
|
|
@ -661,7 +661,7 @@ describe("workspace-old sync state", () => {
|
|||
const session = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))
|
||||
attachSessionToWorkspace(session.id, info.id)
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, localAdaptor(path.join(dir, "flag-disabled")).adaptor)
|
||||
registerAdapter(Instance.project.id, type, localAdapter(path.join(dir, "flag-disabled")).adapter)
|
||||
|
||||
startWorkspaceSyncing(Instance.project.id)
|
||||
await delay(25)
|
||||
|
|
@ -682,8 +682,8 @@ describe("workspace-old sync state", () => {
|
|||
await fs.mkdir(withoutSessionDir, { recursive: true })
|
||||
insertWorkspace(withSession)
|
||||
insertWorkspace(withoutSession)
|
||||
registerAdaptor(Instance.project.id, withSessionType, localAdaptor(withSessionDir).adaptor)
|
||||
registerAdaptor(Instance.project.id, withoutSessionType, localAdaptor(withoutSessionDir).adaptor)
|
||||
registerAdapter(Instance.project.id, withSessionType, localAdapter(withSessionDir).adapter)
|
||||
registerAdapter(Instance.project.id, withoutSessionType, localAdapter(withoutSessionDir).adapter)
|
||||
attachSessionToWorkspace(
|
||||
(await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))).id,
|
||||
withSession.id,
|
||||
|
|
@ -707,10 +707,10 @@ describe("workspace-old sync state", () => {
|
|||
const type = unique("missing-local")
|
||||
const info = workspaceInfo(Instance.project.id, type)
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(
|
||||
registerAdapter(
|
||||
Instance.project.id,
|
||||
type,
|
||||
localAdaptor(path.join(dir, "missing-target"), { createDir: false }).adaptor,
|
||||
localAdapter(path.join(dir, "missing-target"), { createDir: false }).adapter,
|
||||
)
|
||||
attachSessionToWorkspace(
|
||||
(await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))).id,
|
||||
|
|
@ -738,7 +738,7 @@ describe("workspace-old sync state", () => {
|
|||
const target = path.join(dir, "dedupe-local")
|
||||
await fs.mkdir(target, { recursive: true })
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, localAdaptor(target).adaptor)
|
||||
registerAdapter(Instance.project.id, type, localAdapter(target).adapter)
|
||||
attachSessionToWorkspace(
|
||||
(await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))).id,
|
||||
info.id,
|
||||
|
|
@ -795,7 +795,7 @@ describe("workspace-old sync state", () => {
|
|||
const type = unique("remote-start")
|
||||
const info = workspaceInfo(Instance.project.id, type)
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, remoteAdaptor(`${url}/sync`).adaptor)
|
||||
registerAdapter(Instance.project.id, type, remoteAdapter(`${url}/sync`).adapter)
|
||||
attachSessionToWorkspace((yield* sessionSvc.create({})).id, info.id)
|
||||
|
||||
yield* workspace.startWorkspaceSyncing(Instance.project.id)
|
||||
|
|
@ -850,7 +850,7 @@ describe("workspace-old sync state", () => {
|
|||
const type = unique("remote-connect-fail")
|
||||
const info = workspaceInfo(Instance.project.id, type)
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, remoteAdaptor(`${url}/failed`).adaptor)
|
||||
registerAdapter(Instance.project.id, type, remoteAdapter(`${url}/failed`).adapter)
|
||||
attachSessionToWorkspace((yield* sessionSvc.create({})).id, info.id)
|
||||
|
||||
yield* workspace.startWorkspaceSyncing(Instance.project.id)
|
||||
|
|
@ -890,7 +890,7 @@ describe("workspace-old sync state", () => {
|
|||
const type = unique("remote-history-fail")
|
||||
const info = workspaceInfo(Instance.project.id, type)
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, remoteAdaptor(`${url}/history-failed`).adaptor)
|
||||
registerAdapter(Instance.project.id, type, remoteAdapter(`${url}/history-failed`).adapter)
|
||||
attachSessionToWorkspace((yield* sessionSvc.create({})).id, info.id)
|
||||
|
||||
yield* workspace.startWorkspaceSyncing(Instance.project.id)
|
||||
|
|
@ -947,7 +947,7 @@ describe("workspace-old sync state", () => {
|
|||
const type = unique("history-replay")
|
||||
const info = workspaceInfo(Instance.project.id, type)
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, remoteAdaptor(`${url}/history`).adaptor)
|
||||
registerAdapter(Instance.project.id, type, remoteAdapter(`${url}/history`).adapter)
|
||||
const session = yield* sessionSvc.create({ title: "before history" })
|
||||
attachSessionToWorkspace(session.id, info.id)
|
||||
historySessionID = session.id
|
||||
|
|
@ -1014,7 +1014,7 @@ describe("workspace-old sync state", () => {
|
|||
const type = unique("sse-forward")
|
||||
const info = workspaceInfo(Instance.project.id, type)
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, remoteAdaptor(`${url}/sse-forward`).adaptor)
|
||||
registerAdapter(Instance.project.id, type, remoteAdapter(`${url}/sse-forward`).adapter)
|
||||
attachSessionToWorkspace((yield* sessionSvc.create({})).id, info.id)
|
||||
|
||||
yield* workspace.startWorkspaceSyncing(Instance.project.id)
|
||||
|
|
@ -1095,7 +1095,7 @@ describe("workspace-old sync state", () => {
|
|||
const type = unique("sse-sync")
|
||||
const info = workspaceInfo(Instance.project.id, type)
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, remoteAdaptor(`${url}/sse-sync`).adaptor)
|
||||
registerAdapter(Instance.project.id, type, remoteAdapter(`${url}/sse-sync`).adapter)
|
||||
const session = yield* sessionSvc.create({ title: "before sse" })
|
||||
attachSessionToWorkspace(session.id, info.id)
|
||||
sseSessionID = session.id
|
||||
|
|
@ -1232,7 +1232,7 @@ describe("workspace-old sessionRestore", () => {
|
|||
const type = unique("restore-missing-session")
|
||||
const info = workspaceInfo(Instance.project.id, type, { directory: dir })
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, localAdaptor(dir).adaptor)
|
||||
registerAdapter(Instance.project.id, type, localAdapter(dir).adapter)
|
||||
|
||||
await expect(
|
||||
restoreWorkspaceSession({ workspaceID: info.id, sessionID: SessionID.descending("ses_missing_restore") }),
|
||||
|
|
@ -1273,13 +1273,13 @@ describe("workspace-old sessionRestore", () => {
|
|||
const type = unique("restore-remote")
|
||||
const info = workspaceInfo(Instance.project.id, type, { directory: dir })
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(
|
||||
registerAdapter(
|
||||
Instance.project.id,
|
||||
type,
|
||||
remoteAdaptor(`${url}/restore/?ignored=1#hash`, {
|
||||
remoteAdapter(`${url}/restore/?ignored=1#hash`, {
|
||||
directory: dir,
|
||||
headers: { authorization: "Bearer restore" },
|
||||
}).adaptor,
|
||||
}).adapter,
|
||||
)
|
||||
const session = yield* sessionSvc.create({ title: "restore remote" })
|
||||
replaceSessionEvents(session.id, 24)
|
||||
|
|
@ -1353,7 +1353,7 @@ describe("workspace-old sessionRestore", () => {
|
|||
const type = unique("restore-null-dir")
|
||||
const info = workspaceInfo(Instance.project.id, type, { directory: null })
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, remoteAdaptor(`${url}/null-dir`, { directory: null }).adaptor)
|
||||
registerAdapter(Instance.project.id, type, remoteAdapter(`${url}/null-dir`, { directory: null }).adapter)
|
||||
const session = yield* sessionSvc.create({ title: "null dir" })
|
||||
replaceSessionEvents(session.id, 0)
|
||||
|
||||
|
|
@ -1397,7 +1397,7 @@ describe("workspace-old sessionRestore", () => {
|
|||
const type = unique("restore-remote-fail")
|
||||
const info = workspaceInfo(Instance.project.id, type, { directory: dir })
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, remoteAdaptor(`${url}/fail`, { directory: dir }).adaptor)
|
||||
registerAdapter(Instance.project.id, type, remoteAdapter(`${url}/fail`, { directory: dir }).adapter)
|
||||
const session = yield* sessionSvc.create({ title: "restore fail" })
|
||||
replaceSessionEvents(session.id, 11)
|
||||
|
||||
|
|
@ -1437,7 +1437,7 @@ describe("workspace-old sessionRestore", () => {
|
|||
const type = unique("restore-local")
|
||||
const info = workspaceInfo(Instance.project.id, type, { directory: dir })
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, localAdaptor(dir).adaptor)
|
||||
registerAdapter(Instance.project.id, type, localAdapter(dir).adapter)
|
||||
const session = yield* sessionSvc.create({ title: "restore local" })
|
||||
replaceSessionEvents(session.id, 20)
|
||||
|
||||
|
|
@ -1488,7 +1488,7 @@ describe("workspace-old sessionRestore", () => {
|
|||
const type = unique("restore-real-events")
|
||||
const info = workspaceInfo(Instance.project.id, type, { directory: dir })
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, remoteAdaptor(`${url}/real`, { directory: dir }).adaptor)
|
||||
registerAdapter(Instance.project.id, type, remoteAdapter(`${url}/real`, { directory: dir }).adapter)
|
||||
const session = yield* sessionSvc.create({ title: "real events" })
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const msg = yield* sessionSvc.updateMessage({
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ afterAll(() => {
|
|||
})
|
||||
|
||||
describe("plugin.workspace", () => {
|
||||
it.live("plugin can install a workspace adaptor", () =>
|
||||
it.live("plugin can install a workspace adapter", () =>
|
||||
provideTmpdirInstance((dir) =>
|
||||
Effect.gen(function* () {
|
||||
const type = `plug-${Math.random().toString(36).slice(2)}`
|
||||
|
|
@ -48,7 +48,7 @@ describe("plugin.workspace", () => {
|
|||
"export default async ({ experimental_workspace }) => {",
|
||||
` experimental_workspace.register(${JSON.stringify(type)}, {`,
|
||||
' name: "plug",',
|
||||
' description: "plugin workspace adaptor",',
|
||||
' description: "plugin workspace adapter",',
|
||||
" configure(input) {",
|
||||
` return { ...input, name: "plug", branch: "plug/main", directory: ${JSON.stringify(space)} }`,
|
||||
" },",
|
||||
|
|
@ -7,8 +7,8 @@ import { HttpClient, HttpClientRequest, HttpRouter, HttpServerResponse } from "e
|
|||
import * as Socket from "effect/unstable/socket/Socket"
|
||||
import { mkdir } from "node:fs/promises"
|
||||
import path from "node:path"
|
||||
import { registerAdaptor } from "../../src/control-plane/adaptors"
|
||||
import type { WorkspaceAdaptor } from "../../src/control-plane/types"
|
||||
import { registerAdapter } from "../../src/control-plane/adapters"
|
||||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
import { Workspace } from "../../src/control-plane/workspace"
|
||||
import { InstanceRef, WorkspaceRef } from "../../src/effect/instance-ref"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
|
|
@ -49,7 +49,7 @@ const instanceContextTestLayer = instanceRouterMiddleware
|
|||
.combine(workspaceRouterMiddleware)
|
||||
.layer.pipe(Layer.provide(Socket.layerWebSocketConstructorGlobal))
|
||||
|
||||
const localAdaptor = (directory: string): WorkspaceAdaptor => ({
|
||||
const localAdapter = (directory: string): WorkspaceAdapter => ({
|
||||
name: "Local Test",
|
||||
description: "Create a local test workspace",
|
||||
configure: (info) => ({ ...info, name: "local-test", directory }),
|
||||
|
|
@ -63,7 +63,7 @@ const localAdaptor = (directory: string): WorkspaceAdaptor => ({
|
|||
const createLocalWorkspace = (input: { projectID: Project.Info["id"]; type: string; directory: string }) =>
|
||||
Effect.acquireRelease(
|
||||
Effect.gen(function* () {
|
||||
registerAdaptor(input.projectID, input.type, localAdaptor(input.directory))
|
||||
registerAdapter(input.projectID, input.type, localAdapter(input.directory))
|
||||
const workspace = yield* Workspace.Service
|
||||
return yield* workspace.create({
|
||||
type: input.type,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { mkdir } from "node:fs/promises"
|
|||
import path from "node:path"
|
||||
import { Effect } from "effect"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { registerAdaptor } from "../../src/control-plane/adaptors"
|
||||
import type { WorkspaceAdaptor } from "../../src/control-plane/types"
|
||||
import { registerAdapter } from "../../src/control-plane/adapters"
|
||||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
import { Workspace } from "../../src/control-plane/workspace"
|
||||
import { PermissionID } from "../../src/permission/schema"
|
||||
import { ModelID, ProviderID } from "../../src/provider/schema"
|
||||
|
|
@ -82,7 +82,7 @@ function createTextMessage(directory: string, sessionID: SessionID, text: string
|
|||
)
|
||||
}
|
||||
|
||||
const localAdaptor = (directory: string): WorkspaceAdaptor => ({
|
||||
const localAdapter = (directory: string): WorkspaceAdapter => ({
|
||||
name: "Local Test",
|
||||
description: "Create a local test workspace",
|
||||
configure: (info) => ({ ...info, name: "local-test", directory }),
|
||||
|
|
@ -95,7 +95,7 @@ const localAdaptor = (directory: string): WorkspaceAdaptor => ({
|
|||
|
||||
const createLocalWorkspace = (input: { projectID: Project.Info["id"]; type: string; directory: string }) =>
|
||||
Effect.gen(function* () {
|
||||
registerAdaptor(input.projectID, input.type, localAdaptor(input.directory))
|
||||
registerAdapter(input.projectID, input.type, localAdapter(input.directory))
|
||||
return yield* Workspace.Service.use((svc) =>
|
||||
svc.create({
|
||||
type: input.type,
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ import * as Socket from "effect/unstable/socket/Socket"
|
|||
import Http from "node:http"
|
||||
import { mkdir } from "node:fs/promises"
|
||||
import path from "node:path"
|
||||
import { registerAdaptor } from "../../src/control-plane/adaptors"
|
||||
import { registerAdapter } from "../../src/control-plane/adapters"
|
||||
import { WorkspaceID } from "../../src/control-plane/schema"
|
||||
import type { WorkspaceAdaptor } from "../../src/control-plane/types"
|
||||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
import { Workspace } from "../../src/control-plane/workspace"
|
||||
import { WorkspaceTable } from "../../src/control-plane/workspace.sql"
|
||||
import { Project } from "../../src/project/project"
|
||||
|
|
@ -82,7 +82,7 @@ const listenAdditionalServer = <E, R>(handler: TestHandler<E, R>) =>
|
|||
return HttpServer.formatAddress(server.address)
|
||||
})
|
||||
|
||||
const localAdaptor = (directory: string): WorkspaceAdaptor => ({
|
||||
const localAdapter = (directory: string): WorkspaceAdapter => ({
|
||||
name: "Local Test",
|
||||
description: "Create a local test workspace",
|
||||
configure: (info) => ({ ...info, name: "local-test", directory }),
|
||||
|
|
@ -93,7 +93,7 @@ const localAdaptor = (directory: string): WorkspaceAdaptor => ({
|
|||
target: () => ({ type: "local" as const, directory }),
|
||||
})
|
||||
|
||||
const remoteAdaptor = (directory: string, url: string, headers?: HeadersInit): WorkspaceAdaptor => ({
|
||||
const remoteAdapter = (directory: string, url: string, headers?: HeadersInit): WorkspaceAdapter => ({
|
||||
name: "Remote Test",
|
||||
description: "Create a remote test workspace",
|
||||
configure: (info) => ({ ...info, name: "remote-test", directory }),
|
||||
|
|
@ -116,10 +116,10 @@ const syncResponse = (request: HttpServerRequest.HttpServerRequest) => {
|
|||
return undefined
|
||||
}
|
||||
|
||||
const createWorkspace = (input: { projectID: Project.Info["id"]; type: string; adaptor: WorkspaceAdaptor }) =>
|
||||
const createWorkspace = (input: { projectID: Project.Info["id"]; type: string; adapter: WorkspaceAdapter }) =>
|
||||
Effect.acquireRelease(
|
||||
Effect.gen(function* () {
|
||||
registerAdaptor(input.projectID, input.type, input.adaptor)
|
||||
registerAdapter(input.projectID, input.type, input.adapter)
|
||||
const workspace = yield* Workspace.Service
|
||||
return yield* workspace.create({
|
||||
type: input.type,
|
||||
|
|
@ -144,14 +144,14 @@ const createRemoteWorkspace = (input: {
|
|||
createWorkspace({
|
||||
projectID: input.projectID,
|
||||
type: input.type,
|
||||
adaptor: remoteAdaptor(path.join(input.dir, `.${input.type}`), input.url, input.headers),
|
||||
adapter: remoteAdapter(path.join(input.dir, `.${input.type}`), input.url, input.headers),
|
||||
})
|
||||
|
||||
const createLocalWorkspace = (input: { projectID: Project.Info["id"]; type: string; directory: string }) =>
|
||||
createWorkspace({
|
||||
projectID: input.projectID,
|
||||
type: input.type,
|
||||
adaptor: localAdaptor(input.directory),
|
||||
adapter: localAdapter(input.directory),
|
||||
})
|
||||
|
||||
const insertRemoteWorkspaceWithoutSync = (input: {
|
||||
|
|
@ -162,7 +162,7 @@ const insertRemoteWorkspaceWithoutSync = (input: {
|
|||
}) =>
|
||||
Effect.sync(() => {
|
||||
const id = WorkspaceID.ascending()
|
||||
registerAdaptor(input.projectID, input.type, remoteAdaptor(path.join(input.dir, `.${input.type}`), input.url))
|
||||
registerAdapter(input.projectID, input.type, remoteAdapter(path.join(input.dir, `.${input.type}`), input.url))
|
||||
Database.use((db) => db.insert(WorkspaceTable).values({ id, type: input.type, project_id: input.projectID }).run())
|
||||
return id
|
||||
})
|
||||
|
|
@ -237,7 +237,7 @@ describe("HttpApi workspace routing middleware", () => {
|
|||
{ status: 201, headers: { "x-remote": "yes" } },
|
||||
)
|
||||
})
|
||||
// The adaptor target tells the middleware where to proxy selected remote
|
||||
// The adapter target tells the middleware where to proxy selected remote
|
||||
// workspace requests. Appending /probe to this base should produce
|
||||
// `${remoteUrl}/base/probe` on the fake remote server above.
|
||||
const workspace = yield* createRemoteWorkspace({
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { mkdir } from "node:fs/promises"
|
|||
import path from "node:path"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { registerAdaptor } from "../../src/control-plane/adaptors"
|
||||
import type { WorkspaceAdaptor } from "../../src/control-plane/types"
|
||||
import { registerAdapter } from "../../src/control-plane/adapters"
|
||||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
import { Workspace } from "../../src/control-plane/workspace"
|
||||
import { WorkspacePaths } from "../../src/server/routes/instance/httpapi/groups/workspace"
|
||||
import { Session } from "@/session/session"
|
||||
|
|
@ -36,7 +36,7 @@ function request(path: string, directory: string, init: RequestInit = {}) {
|
|||
})
|
||||
}
|
||||
|
||||
function localAdaptor(directory: string): WorkspaceAdaptor {
|
||||
function localAdapter(directory: string): WorkspaceAdapter {
|
||||
return {
|
||||
name: "Local Test",
|
||||
description: "Create a local test workspace",
|
||||
|
|
@ -60,7 +60,7 @@ function localAdaptor(directory: string): WorkspaceAdaptor {
|
|||
}
|
||||
}
|
||||
|
||||
function remoteAdaptor(directory: string, url: string, headers?: HeadersInit): WorkspaceAdaptor {
|
||||
function remoteAdapter(directory: string, url: string, headers?: HeadersInit): WorkspaceAdapter {
|
||||
return {
|
||||
name: "Remote Test",
|
||||
description: "Create a remote test workspace",
|
||||
|
|
@ -137,14 +137,14 @@ describe("workspace HttpApi", () => {
|
|||
Effect.gen(function* () {
|
||||
const dir = yield* tmpdirScoped({ git: true })
|
||||
|
||||
const [adaptors, workspaces, status] = yield* Effect.all([
|
||||
request(WorkspacePaths.adaptors, dir),
|
||||
const [adapters, workspaces, status] = yield* Effect.all([
|
||||
request(WorkspacePaths.adapters, dir),
|
||||
request(WorkspacePaths.list, dir),
|
||||
request(WorkspacePaths.status, dir),
|
||||
])
|
||||
|
||||
expect(adaptors.status).toBe(200)
|
||||
expect(yield* Effect.promise(() => adaptors.json())).toContainEqual({
|
||||
expect(adapters.status).toBe(200)
|
||||
expect(yield* Effect.promise(() => adapters.json())).toContainEqual({
|
||||
type: "worktree",
|
||||
name: "Worktree",
|
||||
description: "Create a git worktree",
|
||||
|
|
@ -163,7 +163,7 @@ describe("workspace HttpApi", () => {
|
|||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true
|
||||
const dir = yield* tmpdirScoped({ git: true })
|
||||
const project = yield* Project.use.fromDirectory(dir)
|
||||
registerAdaptor(project.project.id, "local-test", localAdaptor(path.join(dir, ".workspace")))
|
||||
registerAdapter(project.project.id, "local-test", localAdapter(path.join(dir, ".workspace")))
|
||||
|
||||
const created = yield* request(WorkspacePaths.list, dir, {
|
||||
method: "POST",
|
||||
|
|
@ -201,7 +201,7 @@ describe("workspace HttpApi", () => {
|
|||
const dir = yield* tmpdirScoped({ git: true })
|
||||
const workspaceDir = path.join(dir, ".workspace-local")
|
||||
const project = yield* Project.use.fromDirectory(dir)
|
||||
registerAdaptor(project.project.id, "local-target", localAdaptor(workspaceDir))
|
||||
registerAdapter(project.project.id, "local-target", localAdapter(workspaceDir))
|
||||
const created = yield* request(WorkspacePaths.list, dir, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
|
|
@ -250,10 +250,10 @@ describe("workspace HttpApi", () => {
|
|||
})
|
||||
|
||||
const project = yield* Project.use.fromDirectory(dir)
|
||||
registerAdaptor(
|
||||
registerAdapter(
|
||||
project.project.id,
|
||||
"remote-target",
|
||||
remoteAdaptor(path.join(dir, ".remote"), `http://127.0.0.1:${remote.port}/base`, {
|
||||
remoteAdapter(path.join(dir, ".remote"), `http://127.0.0.1:${remote.port}/base`, {
|
||||
"x-target-auth": "secret",
|
||||
}),
|
||||
)
|
||||
|
|
@ -319,10 +319,10 @@ describe("workspace HttpApi", () => {
|
|||
})
|
||||
|
||||
const project = yield* Project.use.fromDirectory(dir)
|
||||
registerAdaptor(
|
||||
registerAdapter(
|
||||
project.project.id,
|
||||
"remote-session-target",
|
||||
remoteAdaptor(path.join(dir, ".remote-session"), `http://127.0.0.1:${remote.port}/base`),
|
||||
remoteAdapter(path.join(dir, ".remote-session"), `http://127.0.0.1:${remote.port}/base`),
|
||||
)
|
||||
const created = yield* request(WorkspacePaths.list, dir, {
|
||||
method: "POST",
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export type WorkspaceTarget =
|
|||
headers?: HeadersInit
|
||||
}
|
||||
|
||||
export type WorkspaceAdaptor = {
|
||||
export type WorkspaceAdapter = {
|
||||
name: string
|
||||
description: string
|
||||
configure(config: WorkspaceInfo): WorkspaceInfo | Promise<WorkspaceInfo>
|
||||
|
|
@ -60,7 +60,7 @@ export type PluginInput = {
|
|||
directory: string
|
||||
worktree: string
|
||||
experimental_workspace: {
|
||||
register(type: string, adaptor: WorkspaceAdaptor): void
|
||||
register(type: string, adapter: WorkspaceAdapter): void
|
||||
}
|
||||
serverUrl: URL
|
||||
$: BunShell
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import type {
|
|||
ExperimentalConsoleSwitchOrgResponses,
|
||||
ExperimentalResourceListResponses,
|
||||
ExperimentalSessionListResponses,
|
||||
ExperimentalWorkspaceAdaptorListResponses,
|
||||
ExperimentalWorkspaceAdapterListResponses,
|
||||
ExperimentalWorkspaceCreateErrors,
|
||||
ExperimentalWorkspaceCreateResponses,
|
||||
ExperimentalWorkspaceListResponses,
|
||||
|
|
@ -512,11 +512,11 @@ export class App extends HeyApiClient {
|
|||
}
|
||||
}
|
||||
|
||||
export class Adaptor extends HeyApiClient {
|
||||
export class Adapter extends HeyApiClient {
|
||||
/**
|
||||
* List workspace adaptors
|
||||
* List workspace adapters
|
||||
*
|
||||
* List all available workspace adaptors for the current project.
|
||||
* List all available workspace adapters for the current project.
|
||||
*/
|
||||
public list<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
|
|
@ -536,8 +536,8 @@ export class Adaptor extends HeyApiClient {
|
|||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<ExperimentalWorkspaceAdaptorListResponses, unknown, ThrowOnError>({
|
||||
url: "/experimental/workspace/adaptor",
|
||||
return (options?.client ?? this.client).get<ExperimentalWorkspaceAdapterListResponses, unknown, ThrowOnError>({
|
||||
url: "/experimental/workspace/adapter",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
|
|
@ -731,9 +731,9 @@ export class Workspace extends HeyApiClient {
|
|||
})
|
||||
}
|
||||
|
||||
private _adaptor?: Adaptor
|
||||
get adaptor(): Adaptor {
|
||||
return (this._adaptor ??= new Adaptor({ client: this.client }))
|
||||
private _adapter?: Adapter
|
||||
get adapter(): Adapter {
|
||||
return (this._adapter ??= new Adapter({ client: this.client }))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2430,19 +2430,19 @@ export type AppLogResponses = {
|
|||
|
||||
export type AppLogResponse = AppLogResponses[keyof AppLogResponses]
|
||||
|
||||
export type ExperimentalWorkspaceAdaptorListData = {
|
||||
export type ExperimentalWorkspaceAdapterListData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/experimental/workspace/adaptor"
|
||||
url: "/experimental/workspace/adapter"
|
||||
}
|
||||
|
||||
export type ExperimentalWorkspaceAdaptorListResponses = {
|
||||
export type ExperimentalWorkspaceAdapterListResponses = {
|
||||
/**
|
||||
* Workspace adaptors
|
||||
* Workspace adapters
|
||||
*/
|
||||
200: Array<{
|
||||
type: string
|
||||
|
|
@ -2451,8 +2451,8 @@ export type ExperimentalWorkspaceAdaptorListResponses = {
|
|||
}>
|
||||
}
|
||||
|
||||
export type ExperimentalWorkspaceAdaptorListResponse =
|
||||
ExperimentalWorkspaceAdaptorListResponses[keyof ExperimentalWorkspaceAdaptorListResponses]
|
||||
export type ExperimentalWorkspaceAdapterListResponse =
|
||||
ExperimentalWorkspaceAdapterListResponses[keyof ExperimentalWorkspaceAdapterListResponses]
|
||||
|
||||
export type ExperimentalWorkspaceListData = {
|
||||
body?: never
|
||||
|
|
|
|||
|
|
@ -415,9 +415,9 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/experimental/workspace/adaptor": {
|
||||
"/experimental/workspace/adapter": {
|
||||
"get": {
|
||||
"operationId": "experimental.workspace.adaptor.list",
|
||||
"operationId": "experimental.workspace.adapter.list",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
|
|
@ -434,11 +434,11 @@
|
|||
}
|
||||
}
|
||||
],
|
||||
"summary": "List workspace adaptors",
|
||||
"description": "List all available workspace adaptors for the current project.",
|
||||
"summary": "List workspace adapters",
|
||||
"description": "List all available workspace adapters for the current project.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Workspace adaptors",
|
||||
"description": "Workspace adapters",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
|
|
@ -466,7 +466,7 @@
|
|||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.adaptor.list({\n ...\n})"
|
||||
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.adapter.list({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue