167 lines
6.1 KiB
TypeScript
167 lines
6.1 KiB
TypeScript
import { listAdaptors } from "@/control-plane/adaptors"
|
|
import { Workspace } from "@/control-plane/workspace"
|
|
import { WorkspaceAdaptorEntry } from "@/control-plane/types"
|
|
import * as InstanceState from "@/effect/instance-state"
|
|
import { Instance } from "@/project/instance"
|
|
import { Effect, Schema, Struct } from "effect"
|
|
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
|
import { Authorization } from "./auth"
|
|
|
|
const root = "/experimental/workspace"
|
|
const CreatePayload = Schema.Struct(Struct.omit(Workspace.CreateInput.fields, ["projectID"])).annotate({
|
|
identifier: "WorkspaceCreateInput",
|
|
})
|
|
const SessionRestorePayload = Schema.Struct(
|
|
Struct.omit(Workspace.SessionRestoreInput.fields, ["workspaceID"]),
|
|
).annotate({
|
|
identifier: "WorkspaceSessionRestoreInput",
|
|
})
|
|
const SessionRestoreResponse = Schema.Struct({
|
|
total: Schema.Number,
|
|
}).annotate({ identifier: "WorkspaceSessionRestoreResponse" })
|
|
|
|
export const WorkspacePaths = {
|
|
adaptors: `${root}/adaptor`,
|
|
list: root,
|
|
status: `${root}/status`,
|
|
remove: `${root}/:id`,
|
|
sessionRestore: `${root}/:id/session-restore`,
|
|
} as const
|
|
|
|
export const WorkspaceApi = HttpApi.make("workspace")
|
|
.add(
|
|
HttpApiGroup.make("workspace")
|
|
.add(
|
|
HttpApiEndpoint.get("adaptors", WorkspacePaths.adaptors, {
|
|
success: Schema.Array(WorkspaceAdaptorEntry),
|
|
}).annotateMerge(
|
|
OpenApi.annotations({
|
|
identifier: "experimental.workspace.adaptor.list",
|
|
summary: "List workspace adaptors",
|
|
description: "List all available workspace adaptors for the current project.",
|
|
}),
|
|
),
|
|
HttpApiEndpoint.get("list", WorkspacePaths.list, {
|
|
success: Schema.Array(Workspace.Info),
|
|
}).annotateMerge(
|
|
OpenApi.annotations({
|
|
identifier: "experimental.workspace.list",
|
|
summary: "List workspaces",
|
|
description: "List all workspaces.",
|
|
}),
|
|
),
|
|
HttpApiEndpoint.post("create", WorkspacePaths.list, {
|
|
payload: CreatePayload,
|
|
success: Workspace.Info,
|
|
}).annotateMerge(
|
|
OpenApi.annotations({
|
|
identifier: "experimental.workspace.create",
|
|
summary: "Create workspace",
|
|
description: "Create a workspace for the current project.",
|
|
}),
|
|
),
|
|
HttpApiEndpoint.get("status", WorkspacePaths.status, {
|
|
success: Schema.Array(Workspace.ConnectionStatus),
|
|
}).annotateMerge(
|
|
OpenApi.annotations({
|
|
identifier: "experimental.workspace.status",
|
|
summary: "Workspace status",
|
|
description: "Get connection status for workspaces in the current project.",
|
|
}),
|
|
),
|
|
HttpApiEndpoint.delete("remove", WorkspacePaths.remove, {
|
|
params: { id: Workspace.Info.fields.id },
|
|
success: Schema.UndefinedOr(Workspace.Info),
|
|
}).annotateMerge(
|
|
OpenApi.annotations({
|
|
identifier: "experimental.workspace.remove",
|
|
summary: "Remove workspace",
|
|
description: "Remove an existing workspace.",
|
|
}),
|
|
),
|
|
HttpApiEndpoint.post("sessionRestore", WorkspacePaths.sessionRestore, {
|
|
params: { id: Workspace.Info.fields.id },
|
|
payload: SessionRestorePayload,
|
|
success: SessionRestoreResponse,
|
|
}).annotateMerge(
|
|
OpenApi.annotations({
|
|
identifier: "experimental.workspace.sessionRestore",
|
|
summary: "Restore session into workspace",
|
|
description: "Replay a session's sync events into the target workspace in batches.",
|
|
}),
|
|
),
|
|
)
|
|
.annotateMerge(
|
|
OpenApi.annotations({
|
|
title: "workspace",
|
|
description: "Experimental HttpApi workspace routes.",
|
|
}),
|
|
)
|
|
.middleware(Authorization),
|
|
)
|
|
.annotateMerge(
|
|
OpenApi.annotations({
|
|
title: "opencode experimental HttpApi",
|
|
version: "0.0.1",
|
|
description: "Experimental HttpApi surface for selected instance routes.",
|
|
}),
|
|
)
|
|
|
|
export const workspaceHandlers = HttpApiBuilder.group(WorkspaceApi, "workspace", (handlers) =>
|
|
Effect.gen(function* () {
|
|
const adaptors = Effect.fn("WorkspaceHttpApi.adaptors")(function* () {
|
|
const ctx = yield* InstanceState.context
|
|
return yield* Effect.promise(() => listAdaptors(ctx.project.id))
|
|
})
|
|
|
|
const list = Effect.fn("WorkspaceHttpApi.list")(function* () {
|
|
return Workspace.list((yield* InstanceState.context).project)
|
|
})
|
|
|
|
const create = Effect.fn("WorkspaceHttpApi.create")(function* (ctx: { payload: typeof CreatePayload.Type }) {
|
|
const instance = yield* InstanceState.context
|
|
return yield* Effect.promise(() =>
|
|
Instance.restore(instance, () =>
|
|
Workspace.create({
|
|
...ctx.payload,
|
|
projectID: instance.project.id,
|
|
}),
|
|
),
|
|
)
|
|
})
|
|
|
|
const status = Effect.fn("WorkspaceHttpApi.status")(function* () {
|
|
const ids = new Set(Workspace.list((yield* InstanceState.context).project).map((item) => item.id))
|
|
return Workspace.status().filter((item) => ids.has(item.workspaceID))
|
|
})
|
|
|
|
const remove = Effect.fn("WorkspaceHttpApi.remove")(function* (ctx: { params: { id: Workspace.Info["id"] } }) {
|
|
const instance = yield* InstanceState.context
|
|
return yield* Effect.promise(() => Instance.restore(instance, () => Workspace.remove(ctx.params.id)))
|
|
})
|
|
|
|
const sessionRestore = Effect.fn("WorkspaceHttpApi.sessionRestore")(function* (ctx: {
|
|
params: { id: Workspace.Info["id"] }
|
|
payload: typeof SessionRestorePayload.Type
|
|
}) {
|
|
const instance = yield* InstanceState.context
|
|
return yield* Effect.promise(() =>
|
|
Instance.restore(instance, () =>
|
|
Workspace.sessionRestore({
|
|
workspaceID: ctx.params.id,
|
|
sessionID: ctx.payload.sessionID,
|
|
}),
|
|
),
|
|
)
|
|
})
|
|
|
|
return handlers
|
|
.handle("adaptors", adaptors)
|
|
.handle("list", list)
|
|
.handle("create", create)
|
|
.handle("status", status)
|
|
.handle("remove", remove)
|
|
.handle("sessionRestore", sessionRestore)
|
|
}),
|
|
)
|