Compare commits
1 commit
dev
...
adapter-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76e2ff4522 |
22 changed files with 163 additions and 163 deletions
|
|
@ -187,7 +187,7 @@ Use raw Effect HTTP routes where `HttpApi` does not fit. The goal is deleting Ho
|
||||||
| `project` | `bridged` | list, current, git init, update |
|
| `project` | `bridged` | list, current, git init, update |
|
||||||
| `file` | `bridged` partial | find text/file/symbol, list/content/status |
|
| `file` | `bridged` partial | find text/file/symbol, list/content/status |
|
||||||
| `mcp` | `bridged` | status, add, OAuth, connect/disconnect |
|
| `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 |
|
| 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 |
|
| 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 |
|
| `session` | `bridged` | read, lifecycle, prompt, message/part mutations, revert, permission reply |
|
||||||
|
|
@ -279,7 +279,7 @@ This checklist tracks bridge parity only. Checked routes are available through t
|
||||||
|
|
||||||
### Workspace Routes
|
### 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] `POST /experimental/workspace` - create workspace.
|
||||||
- [x] `GET /experimental/workspace` - list workspaces.
|
- [x] `GET /experimental/workspace` - list workspaces.
|
||||||
- [x] `GET /experimental/workspace/status` - workspace status.
|
- [x] `GET /experimental/workspace/status` - workspace status.
|
||||||
|
|
|
||||||
|
|
@ -353,7 +353,7 @@ piecewise.
|
||||||
- [ ] `src/cli/cmd/tui/event.ts`
|
- [ ] `src/cli/cmd/tui/event.ts`
|
||||||
- [ ] `src/cli/ui.ts`
|
- [ ] `src/cli/ui.ts`
|
||||||
- [ ] `src/command/index.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/types.ts`
|
||||||
- [x] `src/control-plane/workspace.ts`
|
- [x] `src/control-plane/workspace.ts`
|
||||||
- [ ] `src/file/index.ts`
|
- [ ] `src/file/index.ts`
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { errorMessage } from "@/util/error"
|
||||||
import { useSDK } from "../context/sdk"
|
import { useSDK } from "../context/sdk"
|
||||||
import { useToast } from "../ui/toast"
|
import { useToast } from "../ui/toast"
|
||||||
|
|
||||||
type Adaptor = {
|
type Adapter = {
|
||||||
type: string
|
type: string
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
|
|
@ -108,26 +108,26 @@ export function DialogWorkspaceCreate(props: { onSelect: (workspaceID: string) =
|
||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const [creating, setCreating] = createSignal<string>()
|
const [creating, setCreating] = createSignal<string>()
|
||||||
const [adaptors, setAdaptors] = createSignal<Adaptor[]>()
|
const [adapters, setAdapters] = createSignal<Adapter[]>()
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
dialog.setSize("medium")
|
dialog.setSize("medium")
|
||||||
void (async () => {
|
void (async () => {
|
||||||
const dir = sync.path.directory || sdk.directory
|
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)
|
if (dir) url.searchParams.set("directory", dir)
|
||||||
const res = await sdk
|
const res = await sdk
|
||||||
.fetch(url)
|
.fetch(url)
|
||||||
.then((x) => x.json() as Promise<Adaptor[]>)
|
.then((x) => x.json() as Promise<Adapter[]>)
|
||||||
.catch(() => undefined)
|
.catch(() => undefined)
|
||||||
if (!res) {
|
if (!res) {
|
||||||
toast.show({
|
toast.show({
|
||||||
message: "Failed to load workspace adaptors",
|
message: "Failed to load workspace adapters",
|
||||||
variant: "error",
|
variant: "error",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setAdaptors(res)
|
setAdapters(res)
|
||||||
})()
|
})()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -142,13 +142,13 @@ export function DialogWorkspaceCreate(props: { onSelect: (workspaceID: string) =
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
const list = adaptors()
|
const list = adapters()
|
||||||
if (!list) {
|
if (!list) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
title: "Loading workspaces...",
|
title: "Loading workspaces...",
|
||||||
value: "loading" as const,
|
value: "loading" as const,
|
||||||
description: "Fetching available workspace adaptors",
|
description: "Fetching available workspace adapters",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
46
packages/opencode/src/control-plane/adapters/index.ts
Normal file
46
packages/opencode/src/control-plane/adapters/index.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { lazy } from "@/util/lazy"
|
||||||
|
import type { ProjectID } from "@/project/schema"
|
||||||
|
import type { WorkspaceAdapter, WorkspaceAdapterEntry } from "../types"
|
||||||
|
|
||||||
|
const BUILTIN: Record<string, () => Promise<WorkspaceAdapter>> = {
|
||||||
|
worktree: lazy(async () => (await import("./worktree")).WorktreeAdapter),
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = new Map<ProjectID, Map<string, WorkspaceAdapter>>()
|
||||||
|
|
||||||
|
export async function getAdapter(projectID: ProjectID, type: string): Promise<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, init]) => {
|
||||||
|
const adapter = await init()
|
||||||
|
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,7 +1,7 @@
|
||||||
import { Schema } from "effect"
|
import { Schema } from "effect"
|
||||||
import { AppRuntime } from "@/effect/app-runtime"
|
import { AppRuntime } from "@/effect/app-runtime"
|
||||||
import { Worktree } from "@/worktree"
|
import { Worktree } from "@/worktree"
|
||||||
import { type WorkspaceAdaptor, WorkspaceInfo } from "../types"
|
import { type WorkspaceAdapter, WorkspaceInfo } from "../types"
|
||||||
|
|
||||||
const WorktreeConfig = Schema.Struct({
|
const WorktreeConfig = Schema.Struct({
|
||||||
name: WorkspaceInfo.fields.name,
|
name: WorkspaceInfo.fields.name,
|
||||||
|
|
@ -10,7 +10,7 @@ const WorktreeConfig = Schema.Struct({
|
||||||
})
|
})
|
||||||
const decodeWorktreeConfig = Schema.decodeUnknownSync(WorktreeConfig)
|
const decodeWorktreeConfig = Schema.decodeUnknownSync(WorktreeConfig)
|
||||||
|
|
||||||
export const WorktreeAdaptor: WorkspaceAdaptor = {
|
export const WorktreeAdapter: WorkspaceAdapter = {
|
||||||
name: "Worktree",
|
name: "Worktree",
|
||||||
description: "Create a git worktree",
|
description: "Create a git worktree",
|
||||||
async configure(info) {
|
async configure(info) {
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
import { lazy } from "@/util/lazy"
|
|
||||||
import type { ProjectID } from "@/project/schema"
|
|
||||||
import type { WorkspaceAdaptor, WorkspaceAdaptorEntry } from "../types"
|
|
||||||
|
|
||||||
const BUILTIN: Record<string, () => Promise<WorkspaceAdaptor>> = {
|
|
||||||
worktree: lazy(async () => (await import("./worktree")).WorktreeAdaptor),
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = new Map<ProjectID, Map<string, WorkspaceAdaptor>>()
|
|
||||||
|
|
||||||
export async function getAdaptor(projectID: ProjectID, type: string): Promise<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, init]) => {
|
|
||||||
const adaptor = await init()
|
|
||||||
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) })))
|
.pipe(withStatics((s) => ({ zod: zod(s) })))
|
||||||
export type WorkspaceInfo = DeepMutable<Schema.Schema.Type<typeof WorkspaceInfo>>
|
export type WorkspaceInfo = DeepMutable<Schema.Schema.Type<typeof WorkspaceInfo>>
|
||||||
|
|
||||||
export const WorkspaceAdaptorEntry = Schema.Struct({
|
export const WorkspaceAdapterEntry = Schema.Struct({
|
||||||
type: Schema.String,
|
type: Schema.String,
|
||||||
name: Schema.String,
|
name: Schema.String,
|
||||||
description: Schema.String,
|
description: Schema.String,
|
||||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
}).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 =
|
export type Target =
|
||||||
| {
|
| {
|
||||||
|
|
@ -35,7 +35,7 @@ export type Target =
|
||||||
headers?: HeadersInit
|
headers?: HeadersInit
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WorkspaceAdaptor = {
|
export type WorkspaceAdapter = {
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
configure(info: WorkspaceInfo): WorkspaceInfo | Promise<WorkspaceInfo>
|
configure(info: WorkspaceInfo): WorkspaceInfo | Promise<WorkspaceInfo>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import { Filesystem } from "@/util/filesystem"
|
||||||
import { ProjectID } from "@/project/schema"
|
import { ProjectID } from "@/project/schema"
|
||||||
import { Slug } from "@opencode-ai/core/util/slug"
|
import { Slug } from "@opencode-ai/core/util/slug"
|
||||||
import { WorkspaceTable } from "./workspace.sql"
|
import { WorkspaceTable } from "./workspace.sql"
|
||||||
import { getAdaptor } from "./adaptors"
|
import { getAdapter } from "./adapters"
|
||||||
import { type WorkspaceInfo, WorkspaceInfo as WorkspaceInfoSchema } from "./types"
|
import { type WorkspaceInfo, WorkspaceInfo as WorkspaceInfoSchema } from "./types"
|
||||||
import { WorkspaceID } from "./schema"
|
import { WorkspaceID } from "./schema"
|
||||||
import { parseSSE } from "./sse"
|
import { parseSSE } from "./sse"
|
||||||
|
|
@ -87,9 +87,9 @@ export type CreateInput = Schema.Schema.Type<typeof CreateInput>
|
||||||
|
|
||||||
export const create = fn(CreateInput.zod, async (input) => {
|
export const create = fn(CreateInput.zod, async (input) => {
|
||||||
const id = WorkspaceID.ascending(input.id)
|
const id = WorkspaceID.ascending(input.id)
|
||||||
const adaptor = await getAdaptor(input.projectID, input.type)
|
const adapter = await getAdapter(input.projectID, input.type)
|
||||||
|
|
||||||
const config = await adaptor.configure({ ...input, id, name: Slug.create(), directory: null })
|
const config = await adapter.configure({ ...input, id, name: Slug.create(), directory: null })
|
||||||
|
|
||||||
const info: Info = {
|
const info: Info = {
|
||||||
id,
|
id,
|
||||||
|
|
@ -123,7 +123,7 @@ export const create = fn(CreateInput.zod, async (input) => {
|
||||||
OTEL_EXPORTER_OTLP_ENDPOINT: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
|
OTEL_EXPORTER_OTLP_ENDPOINT: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
|
||||||
OTEL_RESOURCE_ATTRIBUTES: process.env.OTEL_RESOURCE_ATTRIBUTES,
|
OTEL_RESOURCE_ATTRIBUTES: process.env.OTEL_RESOURCE_ATTRIBUTES,
|
||||||
}
|
}
|
||||||
await adaptor.create(config, env)
|
await adapter.create(config, env)
|
||||||
|
|
||||||
startSync(info)
|
startSync(info)
|
||||||
|
|
||||||
|
|
@ -156,8 +156,8 @@ export const sessionRestore = fn(SessionRestoreInput.zod, async (input) => {
|
||||||
const space = await get(input.workspaceID)
|
const space = await get(input.workspaceID)
|
||||||
if (!space) throw new Error(`Workspace not found: ${input.workspaceID}`)
|
if (!space) throw new Error(`Workspace not found: ${input.workspaceID}`)
|
||||||
|
|
||||||
const adaptor = await getAdaptor(space.projectID, space.type)
|
const adapter = await getAdapter(space.projectID, space.type)
|
||||||
const target = await adaptor.target(space)
|
const target = await adapter.target(space)
|
||||||
|
|
||||||
// Need to switch the workspace of the session
|
// Need to switch the workspace of the session
|
||||||
SyncEvent.run(Session.Event.Updated, {
|
SyncEvent.run(Session.Event.Updated, {
|
||||||
|
|
@ -329,10 +329,10 @@ export const remove = fn(WorkspaceID.zod, async (id) => {
|
||||||
|
|
||||||
const info = fromRow(row)
|
const info = fromRow(row)
|
||||||
try {
|
try {
|
||||||
const adaptor = await getAdaptor(info.projectID, row.type)
|
const adapter = await getAdapter(info.projectID, row.type)
|
||||||
await adaptor.remove(info)
|
await adapter.remove(info)
|
||||||
} catch {
|
} catch {
|
||||||
log.error("adaptor not available when removing workspace", { type: row.type })
|
log.error("adapter not available when removing workspace", { type: row.type })
|
||||||
}
|
}
|
||||||
Database.use((db) => db.delete(WorkspaceTable).where(eq(WorkspaceTable.id, id)).run())
|
Database.use((db) => db.delete(WorkspaceTable).where(eq(WorkspaceTable.id, id)).run())
|
||||||
return info
|
return info
|
||||||
|
|
@ -501,8 +501,8 @@ async function syncHistory(space: Info, url: URL | string, headers: HeadersInit
|
||||||
}
|
}
|
||||||
|
|
||||||
async function syncWorkspaceLoop(space: Info, signal: AbortSignal) {
|
async function syncWorkspaceLoop(space: Info, signal: AbortSignal) {
|
||||||
const adaptor = await getAdaptor(space.projectID, space.type)
|
const adapter = await getAdapter(space.projectID, space.type)
|
||||||
const target = await adaptor.target(space)
|
const target = await adapter.target(space)
|
||||||
|
|
||||||
if (target.type === "local") return null
|
if (target.type === "local") return null
|
||||||
|
|
||||||
|
|
@ -568,8 +568,8 @@ async function syncWorkspaceLoop(space: Info, signal: AbortSignal) {
|
||||||
async function startSync(space: Info) {
|
async function startSync(space: Info) {
|
||||||
if (!Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) return
|
if (!Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) return
|
||||||
|
|
||||||
const adaptor = await getAdaptor(space.projectID, space.type)
|
const adapter = await getAdapter(space.projectID, space.type)
|
||||||
const target = await adaptor.target(space)
|
const target = await adapter.target(space)
|
||||||
|
|
||||||
if (target.type === "local") {
|
if (target.type === "local") {
|
||||||
void Filesystem.exists(target.directory).then((exists) => {
|
void Filesystem.exists(target.directory).then((exists) => {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import type {
|
||||||
PluginInput,
|
PluginInput,
|
||||||
Plugin as PluginInstance,
|
Plugin as PluginInstance,
|
||||||
PluginModule,
|
PluginModule,
|
||||||
WorkspaceAdaptor as PluginWorkspaceAdaptor,
|
WorkspaceAdapter as PluginWorkspaceAdapter,
|
||||||
} from "@opencode-ai/plugin"
|
} from "@opencode-ai/plugin"
|
||||||
import { Config } from "@/config/config"
|
import { Config } from "@/config/config"
|
||||||
import { Bus } from "../bus"
|
import { Bus } from "../bus"
|
||||||
|
|
@ -23,8 +23,8 @@ import { InstanceState } from "@/effect/instance-state"
|
||||||
import { errorMessage } from "@/util/error"
|
import { errorMessage } from "@/util/error"
|
||||||
import { PluginLoader } from "./loader"
|
import { PluginLoader } from "./loader"
|
||||||
import { parsePluginSpecifier, readPluginId, readV1Plugin, resolvePluginId } from "./shared"
|
import { parsePluginSpecifier, readPluginId, readV1Plugin, resolvePluginId } from "./shared"
|
||||||
import { registerAdaptor } from "@/control-plane/adaptors"
|
import { registerAdapter } from "@/control-plane/adapters"
|
||||||
import type { WorkspaceAdaptor } from "@/control-plane/types"
|
import type { WorkspaceAdapter } from "@/control-plane/types"
|
||||||
|
|
||||||
const log = Log.create({ service: "plugin" })
|
const log = Log.create({ service: "plugin" })
|
||||||
|
|
||||||
|
|
@ -136,8 +136,8 @@ export const layer = Layer.effect(
|
||||||
worktree: ctx.worktree,
|
worktree: ctx.worktree,
|
||||||
directory: ctx.directory,
|
directory: ctx.directory,
|
||||||
experimental_workspace: {
|
experimental_workspace: {
|
||||||
register(type: string, adaptor: PluginWorkspaceAdaptor) {
|
register(type: string, adapter: PluginWorkspaceAdapter) {
|
||||||
registerAdaptor(ctx.project.id, type, adaptor as WorkspaceAdaptor)
|
registerAdapter(ctx.project.id, type, adapter as WorkspaceAdapter)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
get serverUrl(): URL {
|
get serverUrl(): URL {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { Hono } from "hono"
|
import { Hono } from "hono"
|
||||||
import { describeRoute, resolver, validator } from "hono-openapi"
|
import { describeRoute, resolver, validator } from "hono-openapi"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { listAdaptors } from "@/control-plane/adaptors"
|
import { listAdapters } from "@/control-plane/adapters"
|
||||||
import { Workspace } from "@/control-plane/workspace"
|
import { Workspace } from "@/control-plane/workspace"
|
||||||
import { WorkspaceAdaptorEntry } from "@/control-plane/types"
|
import { WorkspaceAdapterEntry } from "@/control-plane/types"
|
||||||
import { zodObject } from "@/util/effect-zod"
|
import { zodObject } from "@/util/effect-zod"
|
||||||
import { Instance } from "@/project/instance"
|
import { Instance } from "@/project/instance"
|
||||||
import { errors } from "../../error"
|
import { errors } from "../../error"
|
||||||
|
|
@ -16,24 +16,24 @@ const log = Log.create({ service: "server.workspace" })
|
||||||
export const WorkspaceRoutes = lazy(() =>
|
export const WorkspaceRoutes = lazy(() =>
|
||||||
new Hono()
|
new Hono()
|
||||||
.get(
|
.get(
|
||||||
"/adaptor",
|
"/adapter",
|
||||||
describeRoute({
|
describeRoute({
|
||||||
summary: "List workspace adaptors",
|
summary: "List workspace adapters",
|
||||||
description: "List all available workspace adaptors for the current project.",
|
description: "List all available workspace adapters for the current project.",
|
||||||
operationId: "experimental.workspace.adaptor.list",
|
operationId: "experimental.workspace.adapter.list",
|
||||||
responses: {
|
responses: {
|
||||||
200: {
|
200: {
|
||||||
description: "Workspace adaptors",
|
description: "Workspace adapters",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: resolver(z.array(zodObject(WorkspaceAdaptorEntry))),
|
schema: resolver(z.array(zodObject(WorkspaceAdapterEntry))),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
return c.json(await listAdaptors(Instance.project.id))
|
return c.json(await listAdapters(Instance.project.id))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Workspace } from "@/control-plane/workspace"
|
import { Workspace } from "@/control-plane/workspace"
|
||||||
import { WorkspaceAdaptorEntry } from "@/control-plane/types"
|
import { WorkspaceAdapterEntry } from "@/control-plane/types"
|
||||||
import { NonNegativeInt } from "@/util/schema"
|
import { NonNegativeInt } from "@/util/schema"
|
||||||
import { Schema, Struct } from "effect"
|
import { Schema, Struct } from "effect"
|
||||||
import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||||
|
|
@ -16,7 +16,7 @@ export const SessionRestoreResponse = Schema.Struct({
|
||||||
})
|
})
|
||||||
|
|
||||||
export const WorkspacePaths = {
|
export const WorkspacePaths = {
|
||||||
adaptors: `${root}/adaptor`,
|
adapters: `${root}/adapter`,
|
||||||
list: root,
|
list: root,
|
||||||
status: `${root}/status`,
|
status: `${root}/status`,
|
||||||
remove: `${root}/:id`,
|
remove: `${root}/:id`,
|
||||||
|
|
@ -27,13 +27,13 @@ export const WorkspaceApi = HttpApi.make("workspace")
|
||||||
.add(
|
.add(
|
||||||
HttpApiGroup.make("workspace")
|
HttpApiGroup.make("workspace")
|
||||||
.add(
|
.add(
|
||||||
HttpApiEndpoint.get("adaptors", WorkspacePaths.adaptors, {
|
HttpApiEndpoint.get("adapters", WorkspacePaths.adapters, {
|
||||||
success: described(Schema.Array(WorkspaceAdaptorEntry), "Workspace adaptors"),
|
success: described(Schema.Array(WorkspaceAdapterEntry), "Workspace adapters"),
|
||||||
}).annotateMerge(
|
}).annotateMerge(
|
||||||
OpenApi.annotations({
|
OpenApi.annotations({
|
||||||
identifier: "experimental.workspace.adaptor.list",
|
identifier: "experimental.workspace.adapter.list",
|
||||||
summary: "List workspace adaptors",
|
summary: "List workspace adapters",
|
||||||
description: "List all available workspace adaptors for the current project.",
|
description: "List all available workspace adapters for the current project.",
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
HttpApiEndpoint.get("list", WorkspacePaths.list, {
|
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 { Workspace } from "@/control-plane/workspace"
|
||||||
import * as InstanceState from "@/effect/instance-state"
|
import * as InstanceState from "@/effect/instance-state"
|
||||||
import { Instance } from "@/project/instance"
|
import { Instance } from "@/project/instance"
|
||||||
|
|
@ -9,9 +9,9 @@ import { CreatePayload, SessionRestorePayload } from "../groups/workspace"
|
||||||
|
|
||||||
export const workspaceHandlers = HttpApiBuilder.group(InstanceHttpApi, "workspace", (handlers) =>
|
export const workspaceHandlers = HttpApiBuilder.group(InstanceHttpApi, "workspace", (handlers) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const adaptors = Effect.fn("WorkspaceHttpApi.adaptors")(function* () {
|
const adapters = Effect.fn("WorkspaceHttpApi.adapters")(function* () {
|
||||||
const instance = yield* InstanceState.context
|
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* () {
|
const list = Effect.fn("WorkspaceHttpApi.list")(function* () {
|
||||||
|
|
@ -56,7 +56,7 @@ export const workspaceHandlers = HttpApiBuilder.group(InstanceHttpApi, "workspac
|
||||||
})
|
})
|
||||||
|
|
||||||
return handlers
|
return handlers
|
||||||
.handle("adaptors", adaptors)
|
.handle("adapters", adapters)
|
||||||
.handle("list", list)
|
.handle("list", list)
|
||||||
.handle("create", create)
|
.handle("create", create)
|
||||||
.handle("status", status)
|
.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 { WorkspaceID } from "@/control-plane/schema"
|
||||||
import type { Target } from "@/control-plane/types"
|
import type { Target } from "@/control-plane/types"
|
||||||
import { Workspace } from "@/control-plane/workspace"
|
import { Workspace } from "@/control-plane/workspace"
|
||||||
|
|
@ -89,8 +89,8 @@ function missingWorkspaceResponse(id: WorkspaceID): HttpServerResponse.HttpServe
|
||||||
|
|
||||||
function resolveTarget(workspace: Workspace.Info): Effect.Effect<Target> {
|
function resolveTarget(workspace: Workspace.Info): Effect.Effect<Target> {
|
||||||
return Effect.gen(function* () {
|
return Effect.gen(function* () {
|
||||||
const adaptor = yield* Effect.promise(() => getAdaptor(workspace.projectID, workspace.type))
|
const adapter = yield* Effect.promise(() => getAdapter(workspace.projectID, workspace.type))
|
||||||
return yield* Effect.promise(() => Promise.resolve(adaptor.target(workspace)))
|
return yield* Effect.promise(() => Promise.resolve(adapter.target(workspace)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { MiddlewareHandler } from "hono"
|
import type { MiddlewareHandler } from "hono"
|
||||||
import type { UpgradeWebSocket } from "hono/ws"
|
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 { WorkspaceID } from "@/control-plane/schema"
|
||||||
import { WorkspaceContext } from "@/control-plane/workspace-context"
|
import { WorkspaceContext } from "@/control-plane/workspace-context"
|
||||||
import { Workspace } from "@/control-plane/workspace"
|
import { Workspace } from "@/control-plane/workspace"
|
||||||
|
|
@ -88,8 +88,8 @@ export function WorkspaceRouterMiddleware(upgrade: UpgradeWebSocket): Middleware
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
const adaptor = await getAdaptor(workspace.projectID, workspace.type)
|
const adapter = await getAdapter(workspace.projectID, workspace.type)
|
||||||
const target = await adaptor.target(workspace)
|
const target = await adapter.target(workspace)
|
||||||
|
|
||||||
if (target.type === "local") {
|
if (target.type === "local") {
|
||||||
return WorkspaceContext.provide({
|
return WorkspaceContext.provide({
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, test } from "bun:test"
|
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 { ProjectID } from "../../src/project/schema"
|
||||||
import type { WorkspaceInfo } from "../../src/control-plane/types"
|
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 {
|
return {
|
||||||
name: dir,
|
name: dir,
|
||||||
description: dir,
|
description: dir,
|
||||||
|
|
@ -33,19 +33,19 @@ function adaptor(dir: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("control-plane/adaptors", () => {
|
describe("control-plane/adapters", () => {
|
||||||
test("isolates custom adaptors by project", async () => {
|
test("isolates custom adapters by project", async () => {
|
||||||
const type = `demo-${Math.random().toString(36).slice(2)}`
|
const type = `demo-${Math.random().toString(36).slice(2)}`
|
||||||
const one = ProjectID.make(`project-${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)}`)
|
const two = ProjectID.make(`project-${Math.random().toString(36).slice(2)}`)
|
||||||
registerAdaptor(one, type, adaptor("/one"))
|
registerAdapter(one, type, adapter("/one"))
|
||||||
registerAdaptor(two, type, adaptor("/two"))
|
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",
|
type: "local",
|
||||||
directory: "/one",
|
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",
|
type: "local",
|
||||||
directory: "/two",
|
directory: "/two",
|
||||||
})
|
})
|
||||||
|
|
@ -54,16 +54,16 @@ describe("control-plane/adaptors", () => {
|
||||||
test("latest install wins within a project", async () => {
|
test("latest install wins within a project", async () => {
|
||||||
const type = `demo-${Math.random().toString(36).slice(2)}`
|
const type = `demo-${Math.random().toString(36).slice(2)}`
|
||||||
const id = ProjectID.make(`project-${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",
|
type: "local",
|
||||||
directory: "/one",
|
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",
|
type: "local",
|
||||||
directory: "/two",
|
directory: "/two",
|
||||||
})
|
})
|
||||||
|
|
@ -31,7 +31,7 @@ afterAll(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("plugin.workspace", () => {
|
describe("plugin.workspace", () => {
|
||||||
test("plugin can install a workspace adaptor", async () => {
|
test("plugin can install a workspace adapter", async () => {
|
||||||
await using tmp = await tmpdir({
|
await using tmp = await tmpdir({
|
||||||
init: async (dir) => {
|
init: async (dir) => {
|
||||||
const type = `plug-${Math.random().toString(36).slice(2)}`
|
const type = `plug-${Math.random().toString(36).slice(2)}`
|
||||||
|
|
@ -44,7 +44,7 @@ describe("plugin.workspace", () => {
|
||||||
"export default async ({ experimental_workspace }) => {",
|
"export default async ({ experimental_workspace }) => {",
|
||||||
` experimental_workspace.register(${JSON.stringify(type)}, {`,
|
` experimental_workspace.register(${JSON.stringify(type)}, {`,
|
||||||
' name: "plug",',
|
' name: "plug",',
|
||||||
' description: "plugin workspace adaptor",',
|
' description: "plugin workspace adapter",',
|
||||||
" configure(input) {",
|
" configure(input) {",
|
||||||
` return { ...input, name: "plug", branch: "plug/main", directory: ${JSON.stringify(space)} }`,
|
` return { ...input, name: "plug", branch: "plug/main", directory: ${JSON.stringify(space)} }`,
|
||||||
" },",
|
" },",
|
||||||
|
|
@ -3,8 +3,8 @@ import { mkdir } from "node:fs/promises"
|
||||||
import path from "node:path"
|
import path from "node:path"
|
||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||||
import { registerAdaptor } from "../../src/control-plane/adaptors"
|
import { registerAdapter } from "../../src/control-plane/adapters"
|
||||||
import type { WorkspaceAdaptor } from "../../src/control-plane/types"
|
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||||
import { Workspace } from "../../src/control-plane/workspace"
|
import { Workspace } from "../../src/control-plane/workspace"
|
||||||
import { WorkspacePaths } from "../../src/server/routes/instance/httpapi/groups/workspace"
|
import { WorkspacePaths } from "../../src/server/routes/instance/httpapi/groups/workspace"
|
||||||
import { Session } from "@/session/session"
|
import { Session } from "@/session/session"
|
||||||
|
|
@ -37,7 +37,7 @@ function runSession<A, E>(fx: Effect.Effect<A, E, Session.Service>, workspaceID?
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function localAdaptor(directory: string): WorkspaceAdaptor {
|
function localAdapter(directory: string): WorkspaceAdapter {
|
||||||
return {
|
return {
|
||||||
name: "Local Test",
|
name: "Local Test",
|
||||||
description: "Create a local test workspace",
|
description: "Create a local test workspace",
|
||||||
|
|
@ -61,7 +61,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 {
|
return {
|
||||||
name: "Remote Test",
|
name: "Remote Test",
|
||||||
description: "Create a remote test workspace",
|
description: "Create a remote test workspace",
|
||||||
|
|
@ -139,14 +139,14 @@ describe("workspace HttpApi", () => {
|
||||||
test("serves read endpoints", async () => {
|
test("serves read endpoints", async () => {
|
||||||
await using tmp = await tmpdir({ git: true })
|
await using tmp = await tmpdir({ git: true })
|
||||||
|
|
||||||
const [adaptors, workspaces, status] = await Promise.all([
|
const [adapters, workspaces, status] = await Promise.all([
|
||||||
request(WorkspacePaths.adaptors, tmp.path),
|
request(WorkspacePaths.adapters, tmp.path),
|
||||||
request(WorkspacePaths.list, tmp.path),
|
request(WorkspacePaths.list, tmp.path),
|
||||||
request(WorkspacePaths.status, tmp.path),
|
request(WorkspacePaths.status, tmp.path),
|
||||||
])
|
])
|
||||||
|
|
||||||
expect(adaptors.status).toBe(200)
|
expect(adapters.status).toBe(200)
|
||||||
expect(await adaptors.json()).toEqual([
|
expect(await adapters.json()).toEqual([
|
||||||
{
|
{
|
||||||
type: "worktree",
|
type: "worktree",
|
||||||
name: "Worktree",
|
name: "Worktree",
|
||||||
|
|
@ -167,7 +167,7 @@ describe("workspace HttpApi", () => {
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () =>
|
fn: async () =>
|
||||||
registerAdaptor(Instance.project.id, "local-test", localAdaptor(path.join(tmp.path, ".workspace"))),
|
registerAdapter(Instance.project.id, "local-test", localAdapter(path.join(tmp.path, ".workspace"))),
|
||||||
})
|
})
|
||||||
|
|
||||||
const created = await request(WorkspacePaths.list, tmp.path, {
|
const created = await request(WorkspacePaths.list, tmp.path, {
|
||||||
|
|
@ -207,7 +207,7 @@ describe("workspace HttpApi", () => {
|
||||||
const workspace = await Instance.provide({
|
const workspace = await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
registerAdaptor(Instance.project.id, "local-target", localAdaptor(workspaceDir))
|
registerAdapter(Instance.project.id, "local-target", localAdapter(workspaceDir))
|
||||||
return Workspace.create({
|
return Workspace.create({
|
||||||
type: "local-target",
|
type: "local-target",
|
||||||
branch: null,
|
branch: null,
|
||||||
|
|
@ -261,10 +261,10 @@ describe("workspace HttpApi", () => {
|
||||||
const workspace = await Instance.provide({
|
const workspace = await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
registerAdaptor(
|
registerAdapter(
|
||||||
Instance.project.id,
|
Instance.project.id,
|
||||||
"remote-target",
|
"remote-target",
|
||||||
remoteAdaptor(path.join(tmp.path, ".remote"), `http://127.0.0.1:${remote.port}/base`, {
|
remoteAdapter(path.join(tmp.path, ".remote"), `http://127.0.0.1:${remote.port}/base`, {
|
||||||
"x-target-auth": "secret",
|
"x-target-auth": "secret",
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -332,10 +332,10 @@ describe("workspace HttpApi", () => {
|
||||||
const workspace = await Instance.provide({
|
const workspace = await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
registerAdaptor(
|
registerAdapter(
|
||||||
Instance.project.id,
|
Instance.project.id,
|
||||||
"remote-session-target",
|
"remote-session-target",
|
||||||
remoteAdaptor(path.join(tmp.path, ".remote-session"), `http://127.0.0.1:${remote.port}/base`),
|
remoteAdapter(path.join(tmp.path, ".remote-session"), `http://127.0.0.1:${remote.port}/base`),
|
||||||
)
|
)
|
||||||
return Workspace.create({
|
return Workspace.create({
|
||||||
type: "remote-session-target",
|
type: "remote-session-target",
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:
|
||||||
import fs from "node:fs/promises"
|
import fs from "node:fs/promises"
|
||||||
import path from "node:path"
|
import path from "node:path"
|
||||||
import { GlobalBus } from "../../src/bus/global"
|
import { GlobalBus } from "../../src/bus/global"
|
||||||
import { registerAdaptor } from "../../src/control-plane/adaptors"
|
import { registerAdapter } from "../../src/control-plane/adapters"
|
||||||
import type { WorkspaceAdaptor } from "../../src/control-plane/types"
|
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||||
import { Workspace } from "../../src/control-plane/workspace"
|
import { Workspace } from "../../src/control-plane/workspace"
|
||||||
import { AppRuntime } from "../../src/effect/app-runtime"
|
import { AppRuntime } from "../../src/effect/app-runtime"
|
||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||||
|
|
@ -71,7 +71,7 @@ async function user(sessionID: SessionID, text: string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function remote(dir: string, url: string): WorkspaceAdaptor {
|
function remote(dir: string, url: string): WorkspaceAdapter {
|
||||||
return {
|
return {
|
||||||
name: "remote",
|
name: "remote",
|
||||||
description: "remote",
|
description: "remote",
|
||||||
|
|
@ -94,7 +94,7 @@ function remote(dir: string, url: string): WorkspaceAdaptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function local(dir: string): WorkspaceAdaptor {
|
function local(dir: string): WorkspaceAdapter {
|
||||||
return {
|
return {
|
||||||
name: "local",
|
name: "local",
|
||||||
description: "local",
|
description: "local",
|
||||||
|
|
@ -166,7 +166,7 @@ describe("Workspace.sessionRestore", () => {
|
||||||
const setup = await Instance.provide({
|
const setup = await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
registerAdaptor(Instance.project.id, "worktree", remote(dir, "https://workspace.test/base"))
|
registerAdapter(Instance.project.id, "worktree", remote(dir, "https://workspace.test/base"))
|
||||||
const space = await Workspace.create({
|
const space = await Workspace.create({
|
||||||
type: "worktree",
|
type: "worktree",
|
||||||
branch: null,
|
branch: null,
|
||||||
|
|
@ -247,7 +247,7 @@ describe("Workspace.sessionRestore", () => {
|
||||||
const setup = await Instance.provide({
|
const setup = await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
fn: async () => {
|
fn: async () => {
|
||||||
registerAdaptor(Instance.project.id, "local-restore", local(dir))
|
registerAdapter(Instance.project.id, "local-restore", local(dir))
|
||||||
const space = await Workspace.create({
|
const space = await Workspace.create({
|
||||||
type: "local-restore",
|
type: "local-restore",
|
||||||
branch: null,
|
branch: null,
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ export type WorkspaceTarget =
|
||||||
headers?: HeadersInit
|
headers?: HeadersInit
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WorkspaceAdaptor = {
|
export type WorkspaceAdapter = {
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
configure(config: WorkspaceInfo): WorkspaceInfo | Promise<WorkspaceInfo>
|
configure(config: WorkspaceInfo): WorkspaceInfo | Promise<WorkspaceInfo>
|
||||||
|
|
@ -60,7 +60,7 @@ export type PluginInput = {
|
||||||
directory: string
|
directory: string
|
||||||
worktree: string
|
worktree: string
|
||||||
experimental_workspace: {
|
experimental_workspace: {
|
||||||
register(type: string, adaptor: WorkspaceAdaptor): void
|
register(type: string, adapter: WorkspaceAdapter): void
|
||||||
}
|
}
|
||||||
serverUrl: URL
|
serverUrl: URL
|
||||||
$: BunShell
|
$: BunShell
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import type {
|
||||||
ExperimentalConsoleSwitchOrgResponses,
|
ExperimentalConsoleSwitchOrgResponses,
|
||||||
ExperimentalResourceListResponses,
|
ExperimentalResourceListResponses,
|
||||||
ExperimentalSessionListResponses,
|
ExperimentalSessionListResponses,
|
||||||
ExperimentalWorkspaceAdaptorListResponses,
|
ExperimentalWorkspaceAdapterListResponses,
|
||||||
ExperimentalWorkspaceCreateErrors,
|
ExperimentalWorkspaceCreateErrors,
|
||||||
ExperimentalWorkspaceCreateResponses,
|
ExperimentalWorkspaceCreateResponses,
|
||||||
ExperimentalWorkspaceListResponses,
|
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>(
|
public list<ThrowOnError extends boolean = false>(
|
||||||
parameters?: {
|
parameters?: {
|
||||||
|
|
@ -536,8 +536,8 @@ export class Adaptor extends HeyApiClient {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
return (options?.client ?? this.client).get<ExperimentalWorkspaceAdaptorListResponses, unknown, ThrowOnError>({
|
return (options?.client ?? this.client).get<ExperimentalWorkspaceAdapterListResponses, unknown, ThrowOnError>({
|
||||||
url: "/experimental/workspace/adaptor",
|
url: "/experimental/workspace/adapter",
|
||||||
...options,
|
...options,
|
||||||
...params,
|
...params,
|
||||||
})
|
})
|
||||||
|
|
@ -731,9 +731,9 @@ export class Workspace extends HeyApiClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private _adaptor?: Adaptor
|
private _adapter?: Adapter
|
||||||
get adaptor(): Adaptor {
|
get adapter(): Adapter {
|
||||||
return (this._adaptor ??= new Adaptor({ client: this.client }))
|
return (this._adapter ??= new Adapter({ client: this.client }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2430,19 +2430,19 @@ export type AppLogResponses = {
|
||||||
|
|
||||||
export type AppLogResponse = AppLogResponses[keyof AppLogResponses]
|
export type AppLogResponse = AppLogResponses[keyof AppLogResponses]
|
||||||
|
|
||||||
export type ExperimentalWorkspaceAdaptorListData = {
|
export type ExperimentalWorkspaceAdapterListData = {
|
||||||
body?: never
|
body?: never
|
||||||
path?: never
|
path?: never
|
||||||
query?: {
|
query?: {
|
||||||
directory?: string
|
directory?: string
|
||||||
workspace?: string
|
workspace?: string
|
||||||
}
|
}
|
||||||
url: "/experimental/workspace/adaptor"
|
url: "/experimental/workspace/adapter"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ExperimentalWorkspaceAdaptorListResponses = {
|
export type ExperimentalWorkspaceAdapterListResponses = {
|
||||||
/**
|
/**
|
||||||
* Workspace adaptors
|
* Workspace adapters
|
||||||
*/
|
*/
|
||||||
200: Array<{
|
200: Array<{
|
||||||
type: string
|
type: string
|
||||||
|
|
@ -2451,8 +2451,8 @@ export type ExperimentalWorkspaceAdaptorListResponses = {
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ExperimentalWorkspaceAdaptorListResponse =
|
export type ExperimentalWorkspaceAdapterListResponse =
|
||||||
ExperimentalWorkspaceAdaptorListResponses[keyof ExperimentalWorkspaceAdaptorListResponses]
|
ExperimentalWorkspaceAdapterListResponses[keyof ExperimentalWorkspaceAdapterListResponses]
|
||||||
|
|
||||||
export type ExperimentalWorkspaceListData = {
|
export type ExperimentalWorkspaceListData = {
|
||||||
body?: never
|
body?: never
|
||||||
|
|
|
||||||
|
|
@ -415,9 +415,9 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/experimental/workspace/adaptor": {
|
"/experimental/workspace/adapter": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "experimental.workspace.adaptor.list",
|
"operationId": "experimental.workspace.adapter.list",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"in": "query",
|
"in": "query",
|
||||||
|
|
@ -434,11 +434,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"summary": "List workspace adaptors",
|
"summary": "List workspace adapters",
|
||||||
"description": "List all available workspace adaptors for the current project.",
|
"description": "List all available workspace adapters for the current project.",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Workspace adaptors",
|
"description": "Workspace adapters",
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
|
|
@ -466,7 +466,7 @@
|
||||||
"x-codeSamples": [
|
"x-codeSamples": [
|
||||||
{
|
{
|
||||||
"lang": "js",
|
"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