refactor(core): move server routes around to clarify workspacing (#23031)

This commit is contained in:
James Long
2026-04-17 02:06:20 -04:00
committed by GitHub
parent e7f8f7fa3b
commit 7605acff65
32 changed files with 1196 additions and 1209 deletions

View File

@@ -2,13 +2,13 @@ import { LocalContext } from "../util"
import type { WorkspaceID } from "../control-plane/schema"
export interface WorkspaceContext {
workspaceID: WorkspaceID
workspaceID: WorkspaceID | undefined
}
const context = LocalContext.create<WorkspaceContext>("instance")
export const WorkspaceContext = {
async provide<R>(input: { workspaceID: WorkspaceID; fn: () => R }): Promise<R> {
async provide<R>(input: { workspaceID?: WorkspaceID; fn: () => R }): Promise<R> {
return context.provide({ workspaceID: input.workspaceID }, () => input.fn())
},

View File

@@ -6,13 +6,12 @@ import { ProviderID } from "@/provider/schema"
import { Hono } from "hono"
import { describeRoute, resolver, validator, openAPIRouteHandler } from "hono-openapi"
import z from "zod"
import { errors } from "../error"
import { GlobalRoutes } from "../instance/global"
import { errors } from "../../error"
import { WorkspaceRoutes } from "./workspace"
export function ControlPlaneRoutes(): Hono {
const app = new Hono()
return app
.route("/global", GlobalRoutes())
.put(
"/auth/:providerID",
describeRoute({
@@ -159,4 +158,5 @@ export function ControlPlaneRoutes(): Hono {
return c.json(true)
},
)
.route("/experimental/workspace", WorkspaceRoutes())
}

View File

@@ -1,11 +1,11 @@
import { Hono } from "hono"
import { describeRoute, resolver, validator } from "hono-openapi"
import z from "zod"
import { listAdaptors } from "../../control-plane/adaptors"
import { Workspace } from "../../control-plane/workspace"
import { Instance } from "../../project/instance"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { listAdaptors } from "@/control-plane/adaptors"
import { Workspace } from "@/control-plane/workspace"
import { Instance } from "@/project/instance"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { Log } from "@/util"
import { errorData } from "@/util/error"

View File

@@ -1,11 +1,11 @@
import { Hono } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
import z from "zod"
import { Config } from "../../config"
import { Provider } from "../../provider"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { AppRuntime } from "../../effect/app-runtime"
import { Config } from "@/config"
import { Provider } from "@/provider"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { AppRuntime } from "@/effect/app-runtime"
import { jsonRequest } from "./trace"
export const ConfigRoutes = lazy(() =>

View File

@@ -5,7 +5,7 @@ import { streamSSE } from "hono/streaming"
import { Log } from "@/util"
import { BusEvent } from "@/bus/bus-event"
import { Bus } from "@/bus"
import { AsyncQueue } from "../../util/queue"
import { AsyncQueue } from "@/util/queue"
const log = Log.create({ service: "server" })

View File

@@ -1,22 +1,21 @@
import { Hono } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
import z from "zod"
import { ProviderID, ModelID } from "../../provider/schema"
import { ToolRegistry } from "../../tool"
import { Worktree } from "../../worktree"
import { Instance } from "../../project/instance"
import { Project } from "../../project"
import { MCP } from "../../mcp"
import { Session } from "../../session"
import { Config } from "../../config"
import { ConsoleState } from "../../config/console-state"
import { Account } from "../../account/account"
import { AccountID, OrgID } from "../../account/schema"
import { AppRuntime } from "../../effect/app-runtime"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { ProviderID, ModelID } from "@/provider/schema"
import { ToolRegistry } from "@/tool"
import { Worktree } from "@/worktree"
import { Instance } from "@/project/instance"
import { Project } from "@/project"
import { MCP } from "@/mcp"
import { Session } from "@/session"
import { Config } from "@/config"
import { ConsoleState } from "@/config/console-state"
import { Account } from "@/account/account"
import { AccountID, OrgID } from "@/account/schema"
import { AppRuntime } from "@/effect/app-runtime"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { Effect, Option } from "effect"
import { WorkspaceRoutes } from "./workspace"
import { Agent } from "@/agent/agent"
const ConsoleOrgOption = z.object({
@@ -231,7 +230,6 @@ export const ExperimentalRoutes = lazy(() =>
)
},
)
.route("/workspace", WorkspaceRoutes())
.post(
"/worktree",
describeRoute({

View File

@@ -2,12 +2,12 @@ import { Hono } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
import { Effect } from "effect"
import z from "zod"
import { AppRuntime } from "../../effect/app-runtime"
import { File } from "../../file"
import { Ripgrep } from "../../file/ripgrep"
import { LSP } from "../../lsp"
import { Instance } from "../../project/instance"
import { lazy } from "../../util/lazy"
import { AppRuntime } from "@/effect/app-runtime"
import { File } from "@/file"
import { Ripgrep } from "@/file/ripgrep"
import { LSP } from "@/lsp"
import { Instance } from "@/project/instance"
import { lazy } from "@/util/lazy"
export const FileRoutes = lazy(() =>
new Hono()

View File

@@ -3,15 +3,15 @@ import { Hono } from "hono"
import type { UpgradeWebSocket } from "hono/ws"
import { Context, Effect } from "effect"
import z from "zod"
import { Format } from "../../format"
import { Format } from "@/format"
import { TuiRoutes } from "./tui"
import { Instance } from "../../project/instance"
import { Vcs } from "../../project"
import { Agent } from "../../agent/agent"
import { Skill } from "../../skill"
import { Global } from "../../global"
import { LSP } from "../../lsp"
import { Command } from "../../command"
import { Instance } from "@/project/instance"
import { Vcs } from "@/project"
import { Agent } from "@/agent/agent"
import { Skill } from "@/skill"
import { Global } from "@/global"
import { LSP } from "@/lsp"
import { Command } from "@/command"
import { QuestionRoutes } from "./question"
import { PermissionRoutes } from "./permission"
import { Flag } from "@/flag/flag"
@@ -26,11 +26,10 @@ import { ExperimentalRoutes } from "./experimental"
import { ProviderRoutes } from "./provider"
import { EventRoutes } from "./event"
import { SyncRoutes } from "./sync"
import { WorkspaceRouterMiddleware } from "./middleware"
import { AppRuntime } from "@/effect/app-runtime"
export const InstanceRoutes = (upgrade: UpgradeWebSocket): Hono => {
const app = new Hono().use(WorkspaceRouterMiddleware(upgrade))
const app = new Hono()
if (Flag.OPENCODE_EXPERIMENTAL_HTTPAPI) {
const handler = ExperimentalHttpApiServer.webHandler().handler

View File

@@ -1,12 +1,12 @@
import { Hono } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
import z from "zod"
import { MCP } from "../../mcp"
import { Config } from "../../config"
import { ConfigMCP } from "../../config/mcp"
import { AppRuntime } from "../../effect/app-runtime"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { MCP } from "@/mcp"
import { Config } from "@/config"
import { ConfigMCP } from "@/config/mcp"
import { AppRuntime } from "@/effect/app-runtime"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { Effect } from "effect"
export const McpRoutes = lazy(() =>

View File

@@ -4,8 +4,8 @@ import z from "zod"
import { AppRuntime } from "@/effect/app-runtime"
import { Permission } from "@/permission"
import { PermissionID } from "@/permission/schema"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
export const PermissionRoutes = lazy(() =>
new Hono()

View File

@@ -1,13 +1,13 @@
import { Hono } from "hono"
import { describeRoute, validator } from "hono-openapi"
import { resolver } from "hono-openapi"
import { Instance } from "../../project/instance"
import { Project } from "../../project"
import { Instance } from "@/project/instance"
import { Project } from "@/project"
import z from "zod"
import { ProjectID } from "../../project/schema"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { InstanceBootstrap } from "../../project/bootstrap"
import { ProjectID } from "@/project/schema"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { InstanceBootstrap } from "@/project/bootstrap"
import { AppRuntime } from "@/effect/app-runtime"
export const ProjectRoutes = lazy(() =>

View File

@@ -1,15 +1,15 @@
import { Hono } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
import z from "zod"
import { Config } from "../../config"
import { Provider } from "../../provider"
import { ModelsDev } from "../../provider"
import { ProviderAuth } from "../../provider"
import { ProviderID } from "../../provider/schema"
import { AppRuntime } from "../../effect/app-runtime"
import { Config } from "@/config"
import { Provider } from "@/provider"
import { ModelsDev } from "@/provider"
import { ProviderAuth } from "@/provider"
import { ProviderID } from "@/provider/schema"
import { AppRuntime } from "@/effect/app-runtime"
import { mapValues } from "remeda"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { Effect } from "effect"
export const ProviderRoutes = lazy(() =>

View File

@@ -6,8 +6,8 @@ import z from "zod"
import { AppRuntime } from "@/effect/app-runtime"
import { Pty } from "@/pty"
import { PtyID } from "@/pty/schema"
import { NotFoundError } from "../../storage"
import { errors } from "../error"
import { NotFoundError } from "@/storage"
import { errors } from "../../error"
export function PtyRoutes(upgradeWebSocket: UpgradeWebSocket) {
return new Hono()

View File

@@ -2,11 +2,11 @@ import { Hono } from "hono"
import { describeRoute, validator } from "hono-openapi"
import { resolver } from "hono-openapi"
import { QuestionID } from "@/question/schema"
import { Question } from "../../question"
import { Question } from "@/question"
import { AppRuntime } from "@/effect/app-runtime"
import z from "zod"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
const Reply = z.object({
answers: Question.Answer.zod

View File

@@ -3,28 +3,28 @@ import { stream } from "hono/streaming"
import { describeRoute, validator, resolver } from "hono-openapi"
import { SessionID, MessageID, PartID } from "@/session/schema"
import z from "zod"
import { Session } from "../../session"
import { MessageV2 } from "../../session/message-v2"
import { SessionPrompt } from "../../session/prompt"
import { Session } from "@/session"
import { MessageV2 } from "@/session/message-v2"
import { SessionPrompt } from "@/session/prompt"
import { SessionRunState } from "@/session/run-state"
import { SessionCompaction } from "../../session/compaction"
import { SessionRevert } from "../../session/revert"
import { SessionCompaction } from "@/session/compaction"
import { SessionRevert } from "@/session/revert"
import { SessionShare } from "@/share"
import { SessionStatus } from "@/session/status"
import { SessionSummary } from "@/session/summary"
import { Todo } from "../../session/todo"
import { Todo } from "@/session/todo"
import { Effect } from "effect"
import { AppRuntime } from "../../effect/app-runtime"
import { Agent } from "../../agent/agent"
import { AppRuntime } from "@/effect/app-runtime"
import { Agent } from "@/agent/agent"
import { Snapshot } from "@/snapshot"
import { Command } from "../../command"
import { Log } from "../../util"
import { Command } from "@/command"
import { Log } from "@/util"
import { Permission } from "@/permission"
import { PermissionID } from "@/permission/schema"
import { ModelID, ProviderID } from "@/provider/schema"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { Bus } from "../../bus"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
import { Bus } from "@/bus"
import { NamedError } from "@opencode-ai/shared/util/error"
import { jsonRequest } from "./trace"

View File

@@ -6,7 +6,7 @@ import { Database, asc, and, not, or, lte, eq } from "@/storage"
import { EventTable } from "@/sync/event.sql"
import { lazy } from "@/util/lazy"
import { Log } from "@/util"
import { errors } from "../error"
import { errors } from "../../error"
const ReplayEvent = z.object({
id: z.string(),

View File

@@ -1,6 +1,6 @@
import type { Context } from "hono"
import { Effect } from "effect"
import { AppRuntime } from "../../effect/app-runtime"
import { AppRuntime } from "@/effect/app-runtime"
type AppEnv = Parameters<typeof AppRuntime.runPromise>[0] extends Effect.Effect<any, any, infer R> ? R : never

View File

@@ -1,13 +1,13 @@
import { Hono, type Context } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
import z from "zod"
import { Bus } from "../../bus"
import { Session } from "../../session"
import { Bus } from "@/bus"
import { Session } from "@/session"
import { TuiEvent } from "@/cli/cmd/tui/event"
import { AppRuntime } from "@/effect/app-runtime"
import { AsyncQueue } from "../../util/queue"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { AsyncQueue } from "@/util/queue"
import { errors } from "../../error"
import { lazy } from "@/util/lazy"
const TuiRequest = z.object({
path: z.string(),

View File

@@ -1,16 +1,25 @@
import { generateSpecs } from "hono-openapi"
import { Hono } from "hono"
import type { MiddlewareHandler } from "hono"
import { adapter } from "#hono"
import { MDNS } from "./mdns"
import { lazy } from "@/util/lazy"
import { AuthMiddleware, CompressionMiddleware, CorsMiddleware, ErrorMiddleware, LoggerMiddleware } from "./middleware"
import { FenceMiddleware } from "./fence"
import { InstanceRoutes } from "./instance"
import { initProjectors } from "./projectors"
import { Log } from "@/util"
import { Flag } from "@/flag/flag"
import { ControlPlaneRoutes } from "./control"
import { UIRoutes } from "./ui"
import { Instance } from "@/project/instance"
import { InstanceBootstrap } from "@/project/bootstrap"
import { AppRuntime } from "@/effect/app-runtime"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { WorkspaceID } from "@/control-plane/schema"
import { WorkspaceContext } from "@/control-plane/workspace-context"
import { MDNS } from "./mdns"
import { AuthMiddleware, CompressionMiddleware, CorsMiddleware, ErrorMiddleware, LoggerMiddleware } from "./middleware"
import { FenceMiddleware } from "./fence"
import { initProjectors } from "./projectors"
import { InstanceRoutes } from "./routes/instance"
import { ControlPlaneRoutes } from "./routes/control"
import { UIRoutes } from "./routes/ui"
import { GlobalRoutes } from "./routes/global"
import { WorkspaceRouterMiddleware } from "./workspace"
// @ts-ignore This global is needed to prevent ai-sdk from logging warnings to stdout https://github.com/vercel/ai/blob/2dc67e0ef538307f21368db32d5a12345d98831b/packages/ai/src/logger/log-warnings.ts#L85
globalThis.AI_SDK_LOG_WARNINGS = false
@@ -30,18 +39,48 @@ export const Default = lazy(() => create({}))
function create(opts: { cors?: string[] }) {
const app = new Hono()
.onError(ErrorMiddleware)
.use(AuthMiddleware)
.use(LoggerMiddleware)
.use(CompressionMiddleware)
.use(CorsMiddleware(opts))
.route("/global", GlobalRoutes())
const runtime = adapter.create(app)
function InstanceMiddleware(workspaceID?: WorkspaceID): MiddlewareHandler {
return async (c, next) => {
const raw = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()
const directory = AppFileSystem.resolve(
(() => {
try {
return decodeURIComponent(raw)
} catch {
return raw
}
})(),
)
return WorkspaceContext.provide({
workspaceID,
async fn() {
return Instance.provide({
directory,
init: () => AppRuntime.runPromise(InstanceBootstrap),
async fn() {
return next()
},
})
},
})
}
}
if (Flag.OPENCODE_WORKSPACE_ID) {
return {
app: app
.onError(ErrorMiddleware)
.use(AuthMiddleware)
.use(LoggerMiddleware)
.use(CompressionMiddleware)
.use(CorsMiddleware(opts))
.use(InstanceMiddleware(Flag.OPENCODE_WORKSPACE_ID ? WorkspaceID.make(Flag.OPENCODE_WORKSPACE_ID) : undefined))
.use(FenceMiddleware)
.route("/", ControlPlaneRoutes())
.route("/", InstanceRoutes(runtime.upgradeWebSocket)),
runtime,
}
@@ -49,12 +88,9 @@ function create(opts: { cors?: string[] }) {
return {
app: app
.onError(ErrorMiddleware)
.use(AuthMiddleware)
.use(LoggerMiddleware)
.use(CompressionMiddleware)
.use(CorsMiddleware(opts))
.use(InstanceMiddleware())
.route("/", ControlPlaneRoutes())
.use(WorkspaceRouterMiddleware(runtime.upgradeWebSocket))
.route("/", InstanceRoutes(runtime.upgradeWebSocket))
.route("/", UIRoutes()),
runtime,

View File

@@ -2,17 +2,16 @@ import type { MiddlewareHandler } from "hono"
import type { UpgradeWebSocket } from "hono/ws"
import { getAdaptor } from "@/control-plane/adaptors"
import { WorkspaceID } from "@/control-plane/schema"
import { WorkspaceContext } from "@/control-plane/workspace-context"
import { Workspace } from "@/control-plane/workspace"
import { ServerProxy } from "../proxy"
import { Instance } from "@/project/instance"
import { InstanceBootstrap } from "@/project/bootstrap"
import { Flag } from "@/flag/flag"
import { InstanceBootstrap } from "@/project/bootstrap"
import { Instance } from "@/project/instance"
import { Session } from "@/session"
import { SessionID } from "@/session/schema"
import { WorkspaceContext } from "@/control-plane/workspace-context"
import { AppRuntime } from "@/effect/app-runtime"
import { Log } from "@/util"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
import { ServerProxy } from "./proxy"
type Rule = { method?: string; path: string; exact?: boolean; action: "local" | "forward" }
@@ -51,45 +50,13 @@ export function WorkspaceRouterMiddleware(upgrade: UpgradeWebSocket): Middleware
const log = Log.create({ service: "workspace-router" })
return async (c, next) => {
const raw = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()
const directory = AppFileSystem.resolve(
(() => {
try {
return decodeURIComponent(raw)
} catch {
return raw
}
})(),
)
const url = new URL(c.req.url)
const sessionWorkspaceID = await getSessionWorkspace(url)
const workspaceID = sessionWorkspaceID || url.searchParams.get("workspace")
if (!workspaceID || url.pathname.startsWith("/console") || Flag.OPENCODE_WORKSPACE_ID) {
if (Flag.OPENCODE_WORKSPACE_ID) {
return WorkspaceContext.provide({
workspaceID: WorkspaceID.make(Flag.OPENCODE_WORKSPACE_ID),
async fn() {
return Instance.provide({
directory,
init: () => AppRuntime.runPromise(InstanceBootstrap),
async fn() {
return next()
},
})
},
})
}
return Instance.provide({
directory,
init: () => AppRuntime.runPromise(InstanceBootstrap),
async fn() {
return next()
},
})
return next()
}
const workspace = await Workspace.get(WorkspaceID.make(workspaceID))

View File

@@ -165,16 +165,3 @@ describe("session messages endpoint", () => {
)
})
})
describe("session.prompt_async error handling", () => {
test("prompt_async route has error handler for detached prompt call", async () => {
const src = await Bun.file(new URL("../../src/server/instance/session.ts", import.meta.url)).text()
const start = src.indexOf('"/:sessionID/prompt_async"')
const end = src.indexOf('"/:sessionID/command"', start)
expect(start).toBeGreaterThan(-1)
expect(end).toBeGreaterThan(start)
const route = src.slice(start, end)
expect(route).toContain(".catch(")
expect(route).toContain("Bus.publish(Session.Event.Error")
})
})

View File

@@ -510,6 +510,430 @@ export class App extends HeyApiClient {
}
}
export class Adaptor extends HeyApiClient {
/**
* List workspace adaptors
*
* List all available workspace adaptors for the current project.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalWorkspaceAdaptorListResponses, unknown, ThrowOnError>({
url: "/experimental/workspace/adaptor",
...options,
...params,
})
}
}
export class Workspace extends HeyApiClient {
/**
* List workspaces
*
* List all workspaces.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalWorkspaceListResponses, unknown, ThrowOnError>({
url: "/experimental/workspace",
...options,
...params,
})
}
/**
* Create workspace
*
* Create a workspace for the current project.
*/
public create<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
id?: string
type?: string
branch?: string | null
extra?: unknown | null
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "body", key: "id" },
{ in: "body", key: "type" },
{ in: "body", key: "branch" },
{ in: "body", key: "extra" },
],
},
],
)
return (options?.client ?? this.client).post<
ExperimentalWorkspaceCreateResponses,
ExperimentalWorkspaceCreateErrors,
ThrowOnError
>({
url: "/experimental/workspace",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
/**
* Workspace status
*
* Get connection status for workspaces in the current project.
*/
public status<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalWorkspaceStatusResponses, unknown, ThrowOnError>({
url: "/experimental/workspace/status",
...options,
...params,
})
}
/**
* Remove workspace
*
* Remove an existing workspace.
*/
public remove<ThrowOnError extends boolean = false>(
parameters: {
id: string
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "path", key: "id" },
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).delete<
ExperimentalWorkspaceRemoveResponses,
ExperimentalWorkspaceRemoveErrors,
ThrowOnError
>({
url: "/experimental/workspace/{id}",
...options,
...params,
})
}
/**
* Restore session into workspace
*
* Replay a session's sync events into the target workspace in batches.
*/
public sessionRestore<ThrowOnError extends boolean = false>(
parameters: {
id: string
directory?: string
workspace?: string
sessionID?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "path", key: "id" },
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "body", key: "sessionID" },
],
},
],
)
return (options?.client ?? this.client).post<
ExperimentalWorkspaceSessionRestoreResponses,
ExperimentalWorkspaceSessionRestoreErrors,
ThrowOnError
>({
url: "/experimental/workspace/{id}/session-restore",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
private _adaptor?: Adaptor
get adaptor(): Adaptor {
return (this._adaptor ??= new Adaptor({ client: this.client }))
}
}
export class Console extends HeyApiClient {
/**
* Get active Console provider metadata
*
* Get the active Console org name and the set of provider IDs managed by that Console org.
*/
public get<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalConsoleGetResponses, unknown, ThrowOnError>({
url: "/experimental/console",
...options,
...params,
})
}
/**
* List switchable Console orgs
*
* Get the available Console orgs across logged-in accounts, including the current active org.
*/
public listOrgs<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalConsoleListOrgsResponses, unknown, ThrowOnError>({
url: "/experimental/console/orgs",
...options,
...params,
})
}
/**
* Switch active Console org
*
* Persist a new active Console account/org selection for the current local OpenCode state.
*/
public switchOrg<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
accountID?: string
orgID?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "body", key: "accountID" },
{ in: "body", key: "orgID" },
],
},
],
)
return (options?.client ?? this.client).post<ExperimentalConsoleSwitchOrgResponses, unknown, ThrowOnError>({
url: "/experimental/console/switch",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
}
export class Session extends HeyApiClient {
/**
* List sessions
*
* Get a list of all OpenCode sessions across projects, sorted by most recently updated. Archived sessions are excluded by default.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
roots?: boolean
start?: number
cursor?: number
search?: string
limit?: number
archived?: boolean
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "query", key: "roots" },
{ in: "query", key: "start" },
{ in: "query", key: "cursor" },
{ in: "query", key: "search" },
{ in: "query", key: "limit" },
{ in: "query", key: "archived" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalSessionListResponses, unknown, ThrowOnError>({
url: "/experimental/session",
...options,
...params,
})
}
}
export class Resource extends HeyApiClient {
/**
* Get MCP resources
*
* Get all available MCP resources from connected servers. Optionally filter by name.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalResourceListResponses, unknown, ThrowOnError>({
url: "/experimental/resource",
...options,
...params,
})
}
}
export class Experimental extends HeyApiClient {
private _workspace?: Workspace
get workspace(): Workspace {
return (this._workspace ??= new Workspace({ client: this.client }))
}
private _console?: Console
get console(): Console {
return (this._console ??= new Console({ client: this.client }))
}
private _session?: Session
get session(): Session {
return (this._session ??= new Session({ client: this.client }))
}
private _resource?: Resource
get resource(): Resource {
return (this._resource ??= new Resource({ client: this.client }))
}
}
export class Project extends HeyApiClient {
/**
* List all projects
@@ -972,430 +1396,6 @@ export class Config2 extends HeyApiClient {
}
}
export class Console extends HeyApiClient {
/**
* Get active Console provider metadata
*
* Get the active Console org name and the set of provider IDs managed by that Console org.
*/
public get<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalConsoleGetResponses, unknown, ThrowOnError>({
url: "/experimental/console",
...options,
...params,
})
}
/**
* List switchable Console orgs
*
* Get the available Console orgs across logged-in accounts, including the current active org.
*/
public listOrgs<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalConsoleListOrgsResponses, unknown, ThrowOnError>({
url: "/experimental/console/orgs",
...options,
...params,
})
}
/**
* Switch active Console org
*
* Persist a new active Console account/org selection for the current local OpenCode state.
*/
public switchOrg<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
accountID?: string
orgID?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "body", key: "accountID" },
{ in: "body", key: "orgID" },
],
},
],
)
return (options?.client ?? this.client).post<ExperimentalConsoleSwitchOrgResponses, unknown, ThrowOnError>({
url: "/experimental/console/switch",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
}
export class Adaptor extends HeyApiClient {
/**
* List workspace adaptors
*
* List all available workspace adaptors for the current project.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalWorkspaceAdaptorListResponses, unknown, ThrowOnError>({
url: "/experimental/workspace/adaptor",
...options,
...params,
})
}
}
export class Workspace extends HeyApiClient {
/**
* List workspaces
*
* List all workspaces.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalWorkspaceListResponses, unknown, ThrowOnError>({
url: "/experimental/workspace",
...options,
...params,
})
}
/**
* Create workspace
*
* Create a workspace for the current project.
*/
public create<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
id?: string
type?: string
branch?: string | null
extra?: unknown | null
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "body", key: "id" },
{ in: "body", key: "type" },
{ in: "body", key: "branch" },
{ in: "body", key: "extra" },
],
},
],
)
return (options?.client ?? this.client).post<
ExperimentalWorkspaceCreateResponses,
ExperimentalWorkspaceCreateErrors,
ThrowOnError
>({
url: "/experimental/workspace",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
/**
* Workspace status
*
* Get connection status for workspaces in the current project.
*/
public status<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalWorkspaceStatusResponses, unknown, ThrowOnError>({
url: "/experimental/workspace/status",
...options,
...params,
})
}
/**
* Remove workspace
*
* Remove an existing workspace.
*/
public remove<ThrowOnError extends boolean = false>(
parameters: {
id: string
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "path", key: "id" },
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).delete<
ExperimentalWorkspaceRemoveResponses,
ExperimentalWorkspaceRemoveErrors,
ThrowOnError
>({
url: "/experimental/workspace/{id}",
...options,
...params,
})
}
/**
* Restore session into workspace
*
* Replay a session's sync events into the target workspace in batches.
*/
public sessionRestore<ThrowOnError extends boolean = false>(
parameters: {
id: string
directory?: string
workspace?: string
sessionID?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "path", key: "id" },
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "body", key: "sessionID" },
],
},
],
)
return (options?.client ?? this.client).post<
ExperimentalWorkspaceSessionRestoreResponses,
ExperimentalWorkspaceSessionRestoreErrors,
ThrowOnError
>({
url: "/experimental/workspace/{id}/session-restore",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
private _adaptor?: Adaptor
get adaptor(): Adaptor {
return (this._adaptor ??= new Adaptor({ client: this.client }))
}
}
export class Session extends HeyApiClient {
/**
* List sessions
*
* Get a list of all OpenCode sessions across projects, sorted by most recently updated. Archived sessions are excluded by default.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
roots?: boolean
start?: number
cursor?: number
search?: string
limit?: number
archived?: boolean
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "query", key: "roots" },
{ in: "query", key: "start" },
{ in: "query", key: "cursor" },
{ in: "query", key: "search" },
{ in: "query", key: "limit" },
{ in: "query", key: "archived" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalSessionListResponses, unknown, ThrowOnError>({
url: "/experimental/session",
...options,
...params,
})
}
}
export class Resource extends HeyApiClient {
/**
* Get MCP resources
*
* Get all available MCP resources from connected servers. Optionally filter by name.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
],
},
],
)
return (options?.client ?? this.client).get<ExperimentalResourceListResponses, unknown, ThrowOnError>({
url: "/experimental/resource",
...options,
...params,
})
}
}
export class Experimental extends HeyApiClient {
private _console?: Console
get console(): Console {
return (this._console ??= new Console({ client: this.client }))
}
private _workspace?: Workspace
get workspace(): Workspace {
return (this._workspace ??= new Workspace({ client: this.client }))
}
private _session?: Session
get session(): Session {
return (this._session ??= new Session({ client: this.client }))
}
private _resource?: Resource
get resource(): Resource {
return (this._resource ??= new Resource({ client: this.client }))
}
}
export class Tool extends HeyApiClient {
/**
* List tool IDs
@@ -4314,6 +4314,11 @@ export class OpencodeClient extends HeyApiClient {
return (this._app ??= new App({ client: this.client }))
}
private _experimental?: Experimental
get experimental(): Experimental {
return (this._experimental ??= new Experimental({ client: this.client }))
}
private _project?: Project
get project(): Project {
return (this._project ??= new Project({ client: this.client }))
@@ -4329,11 +4334,6 @@ export class OpencodeClient extends HeyApiClient {
return (this._config ??= new Config2({ client: this.client }))
}
private _experimental?: Experimental
get experimental(): Experimental {
return (this._experimental ??= new Experimental({ client: this.client }))
}
private _tool?: Tool
get tool(): Tool {
return (this._tool ??= new Tool({ client: this.client }))

View File

@@ -1706,6 +1706,16 @@ export type WellKnownAuth = {
export type Auth = OAuth | ApiAuth | WellKnownAuth
export type Workspace = {
id: string
type: string
name: string
branch: string | null
directory: string | null
extra: unknown | null
projectID: string
}
export type NotFoundError = {
name: "NotFoundError"
data: {
@@ -1808,16 +1818,6 @@ export type ToolListItem = {
export type ToolList = Array<ToolListItem>
export type Workspace = {
id: string
type: string
name: string
branch: string | null
directory: string | null
extra: unknown | null
projectID: string
}
export type Worktree = {
name: string
branch: string
@@ -2394,6 +2394,177 @@ export type AppLogResponses = {
export type AppLogResponse = AppLogResponses[keyof AppLogResponses]
export type ExperimentalWorkspaceAdaptorListData = {
body?: never
path?: never
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace/adaptor"
}
export type ExperimentalWorkspaceAdaptorListResponses = {
/**
* Workspace adaptors
*/
200: Array<{
type: string
name: string
description: string
}>
}
export type ExperimentalWorkspaceAdaptorListResponse =
ExperimentalWorkspaceAdaptorListResponses[keyof ExperimentalWorkspaceAdaptorListResponses]
export type ExperimentalWorkspaceListData = {
body?: never
path?: never
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace"
}
export type ExperimentalWorkspaceListResponses = {
/**
* Workspaces
*/
200: Array<Workspace>
}
export type ExperimentalWorkspaceListResponse =
ExperimentalWorkspaceListResponses[keyof ExperimentalWorkspaceListResponses]
export type ExperimentalWorkspaceCreateData = {
body?: {
id?: string
type: string
branch: string | null
extra: unknown | null
}
path?: never
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace"
}
export type ExperimentalWorkspaceCreateErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type ExperimentalWorkspaceCreateError =
ExperimentalWorkspaceCreateErrors[keyof ExperimentalWorkspaceCreateErrors]
export type ExperimentalWorkspaceCreateResponses = {
/**
* Workspace created
*/
200: Workspace
}
export type ExperimentalWorkspaceCreateResponse =
ExperimentalWorkspaceCreateResponses[keyof ExperimentalWorkspaceCreateResponses]
export type ExperimentalWorkspaceStatusData = {
body?: never
path?: never
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace/status"
}
export type ExperimentalWorkspaceStatusResponses = {
/**
* Workspace status
*/
200: Array<{
workspaceID: string
status: "connected" | "connecting" | "disconnected" | "error"
error?: string
}>
}
export type ExperimentalWorkspaceStatusResponse =
ExperimentalWorkspaceStatusResponses[keyof ExperimentalWorkspaceStatusResponses]
export type ExperimentalWorkspaceRemoveData = {
body?: never
path: {
id: string
}
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace/{id}"
}
export type ExperimentalWorkspaceRemoveErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type ExperimentalWorkspaceRemoveError =
ExperimentalWorkspaceRemoveErrors[keyof ExperimentalWorkspaceRemoveErrors]
export type ExperimentalWorkspaceRemoveResponses = {
/**
* Workspace removed
*/
200: Workspace
}
export type ExperimentalWorkspaceRemoveResponse =
ExperimentalWorkspaceRemoveResponses[keyof ExperimentalWorkspaceRemoveResponses]
export type ExperimentalWorkspaceSessionRestoreData = {
body?: {
sessionID: string
}
path: {
id: string
}
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace/{id}/session-restore"
}
export type ExperimentalWorkspaceSessionRestoreErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type ExperimentalWorkspaceSessionRestoreError =
ExperimentalWorkspaceSessionRestoreErrors[keyof ExperimentalWorkspaceSessionRestoreErrors]
export type ExperimentalWorkspaceSessionRestoreResponses = {
/**
* Session replay started
*/
200: {
total: number
}
}
export type ExperimentalWorkspaceSessionRestoreResponse =
ExperimentalWorkspaceSessionRestoreResponses[keyof ExperimentalWorkspaceSessionRestoreResponses]
export type ProjectListData = {
body?: never
path?: never
@@ -2883,177 +3054,6 @@ export type ToolListResponses = {
export type ToolListResponse = ToolListResponses[keyof ToolListResponses]
export type ExperimentalWorkspaceAdaptorListData = {
body?: never
path?: never
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace/adaptor"
}
export type ExperimentalWorkspaceAdaptorListResponses = {
/**
* Workspace adaptors
*/
200: Array<{
type: string
name: string
description: string
}>
}
export type ExperimentalWorkspaceAdaptorListResponse =
ExperimentalWorkspaceAdaptorListResponses[keyof ExperimentalWorkspaceAdaptorListResponses]
export type ExperimentalWorkspaceListData = {
body?: never
path?: never
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace"
}
export type ExperimentalWorkspaceListResponses = {
/**
* Workspaces
*/
200: Array<Workspace>
}
export type ExperimentalWorkspaceListResponse =
ExperimentalWorkspaceListResponses[keyof ExperimentalWorkspaceListResponses]
export type ExperimentalWorkspaceCreateData = {
body?: {
id?: string
type: string
branch: string | null
extra: unknown | null
}
path?: never
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace"
}
export type ExperimentalWorkspaceCreateErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type ExperimentalWorkspaceCreateError =
ExperimentalWorkspaceCreateErrors[keyof ExperimentalWorkspaceCreateErrors]
export type ExperimentalWorkspaceCreateResponses = {
/**
* Workspace created
*/
200: Workspace
}
export type ExperimentalWorkspaceCreateResponse =
ExperimentalWorkspaceCreateResponses[keyof ExperimentalWorkspaceCreateResponses]
export type ExperimentalWorkspaceStatusData = {
body?: never
path?: never
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace/status"
}
export type ExperimentalWorkspaceStatusResponses = {
/**
* Workspace status
*/
200: Array<{
workspaceID: string
status: "connected" | "connecting" | "disconnected" | "error"
error?: string
}>
}
export type ExperimentalWorkspaceStatusResponse =
ExperimentalWorkspaceStatusResponses[keyof ExperimentalWorkspaceStatusResponses]
export type ExperimentalWorkspaceRemoveData = {
body?: never
path: {
id: string
}
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace/{id}"
}
export type ExperimentalWorkspaceRemoveErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type ExperimentalWorkspaceRemoveError =
ExperimentalWorkspaceRemoveErrors[keyof ExperimentalWorkspaceRemoveErrors]
export type ExperimentalWorkspaceRemoveResponses = {
/**
* Workspace removed
*/
200: Workspace
}
export type ExperimentalWorkspaceRemoveResponse =
ExperimentalWorkspaceRemoveResponses[keyof ExperimentalWorkspaceRemoveResponses]
export type ExperimentalWorkspaceSessionRestoreData = {
body?: {
sessionID: string
}
path: {
id: string
}
query?: {
directory?: string
workspace?: string
}
url: "/experimental/workspace/{id}/session-restore"
}
export type ExperimentalWorkspaceSessionRestoreErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type ExperimentalWorkspaceSessionRestoreError =
ExperimentalWorkspaceSessionRestoreErrors[keyof ExperimentalWorkspaceSessionRestoreErrors]
export type ExperimentalWorkspaceSessionRestoreResponses = {
/**
* Session replay started
*/
200: {
total: number
}
}
export type ExperimentalWorkspaceSessionRestoreResponse =
ExperimentalWorkspaceSessionRestoreResponses[keyof ExperimentalWorkspaceSessionRestoreResponses]
export type WorktreeRemoveData = {
body?: WorktreeRemoveInput
path?: never

View File

@@ -415,6 +415,394 @@
]
}
},
"/experimental/workspace/adaptor": {
"get": {
"operationId": "experimental.workspace.adaptor.list",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
}
],
"summary": "List workspace adaptors",
"description": "List all available workspace adaptors for the current project.",
"responses": {
"200": {
"description": "Workspace adaptors",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
}
},
"required": ["type", "name", "description"]
}
}
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.adaptor.list({\n ...\n})"
}
]
}
},
"/experimental/workspace": {
"post": {
"operationId": "experimental.workspace.create",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
}
],
"summary": "Create workspace",
"description": "Create a workspace for the current project.",
"responses": {
"200": {
"description": "Workspace created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Workspace"
}
}
}
},
"400": {
"description": "Bad request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BadRequestError"
}
}
}
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^wrk.*"
},
"type": {
"type": "string"
},
"branch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"extra": {
"anyOf": [
{},
{
"type": "null"
}
]
}
},
"required": ["type", "branch", "extra"]
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.create({\n ...\n})"
}
]
},
"get": {
"operationId": "experimental.workspace.list",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
}
],
"summary": "List workspaces",
"description": "List all workspaces.",
"responses": {
"200": {
"description": "Workspaces",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Workspace"
}
}
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.list({\n ...\n})"
}
]
}
},
"/experimental/workspace/status": {
"get": {
"operationId": "experimental.workspace.status",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
}
],
"summary": "Workspace status",
"description": "Get connection status for workspaces in the current project.",
"responses": {
"200": {
"description": "Workspace status",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"workspaceID": {
"type": "string",
"pattern": "^wrk.*"
},
"status": {
"type": "string",
"enum": ["connected", "connecting", "disconnected", "error"]
},
"error": {
"type": "string"
}
},
"required": ["workspaceID", "status"]
}
}
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.status({\n ...\n})"
}
]
}
},
"/experimental/workspace/{id}": {
"delete": {
"operationId": "experimental.workspace.remove",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
},
{
"in": "path",
"name": "id",
"schema": {
"type": "string",
"pattern": "^wrk.*"
},
"required": true
}
],
"summary": "Remove workspace",
"description": "Remove an existing workspace.",
"responses": {
"200": {
"description": "Workspace removed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Workspace"
}
}
}
},
"400": {
"description": "Bad request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BadRequestError"
}
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.remove({\n ...\n})"
}
]
}
},
"/experimental/workspace/{id}/session-restore": {
"post": {
"operationId": "experimental.workspace.sessionRestore",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
},
{
"in": "path",
"name": "id",
"schema": {
"type": "string",
"pattern": "^wrk.*"
},
"required": true
}
],
"summary": "Restore session into workspace",
"description": "Replay a session's sync events into the target workspace in batches.",
"responses": {
"200": {
"description": "Session replay started",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"total": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": ["total"]
}
}
}
},
"400": {
"description": "Bad request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BadRequestError"
}
}
}
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"pattern": "^ses.*"
}
},
"required": ["sessionID"]
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.sessionRestore({\n ...\n})"
}
]
}
},
"/project": {
"get": {
"operationId": "project.list",
@@ -1501,394 +1889,6 @@
]
}
},
"/experimental/workspace/adaptor": {
"get": {
"operationId": "experimental.workspace.adaptor.list",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
}
],
"summary": "List workspace adaptors",
"description": "List all available workspace adaptors for the current project.",
"responses": {
"200": {
"description": "Workspace adaptors",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
}
},
"required": ["type", "name", "description"]
}
}
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.adaptor.list({\n ...\n})"
}
]
}
},
"/experimental/workspace": {
"post": {
"operationId": "experimental.workspace.create",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
}
],
"summary": "Create workspace",
"description": "Create a workspace for the current project.",
"responses": {
"200": {
"description": "Workspace created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Workspace"
}
}
}
},
"400": {
"description": "Bad request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BadRequestError"
}
}
}
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^wrk.*"
},
"type": {
"type": "string"
},
"branch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"extra": {
"anyOf": [
{},
{
"type": "null"
}
]
}
},
"required": ["type", "branch", "extra"]
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.create({\n ...\n})"
}
]
},
"get": {
"operationId": "experimental.workspace.list",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
}
],
"summary": "List workspaces",
"description": "List all workspaces.",
"responses": {
"200": {
"description": "Workspaces",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Workspace"
}
}
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.list({\n ...\n})"
}
]
}
},
"/experimental/workspace/status": {
"get": {
"operationId": "experimental.workspace.status",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
}
],
"summary": "Workspace status",
"description": "Get connection status for workspaces in the current project.",
"responses": {
"200": {
"description": "Workspace status",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"workspaceID": {
"type": "string",
"pattern": "^wrk.*"
},
"status": {
"type": "string",
"enum": ["connected", "connecting", "disconnected", "error"]
},
"error": {
"type": "string"
}
},
"required": ["workspaceID", "status"]
}
}
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.status({\n ...\n})"
}
]
}
},
"/experimental/workspace/{id}": {
"delete": {
"operationId": "experimental.workspace.remove",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
},
{
"in": "path",
"name": "id",
"schema": {
"type": "string",
"pattern": "^wrk.*"
},
"required": true
}
],
"summary": "Remove workspace",
"description": "Remove an existing workspace.",
"responses": {
"200": {
"description": "Workspace removed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Workspace"
}
}
}
},
"400": {
"description": "Bad request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BadRequestError"
}
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.remove({\n ...\n})"
}
]
}
},
"/experimental/workspace/{id}/session-restore": {
"post": {
"operationId": "experimental.workspace.sessionRestore",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
},
{
"in": "path",
"name": "id",
"schema": {
"type": "string",
"pattern": "^wrk.*"
},
"required": true
}
],
"summary": "Restore session into workspace",
"description": "Replay a session's sync events into the target workspace in batches.",
"responses": {
"200": {
"description": "Session replay started",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"total": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": ["total"]
}
}
}
},
"400": {
"description": "Bad request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BadRequestError"
}
}
}
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"pattern": "^ses.*"
}
},
"required": ["sessionID"]
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.experimental.workspace.sessionRestore({\n ...\n})"
}
]
}
},
"/experimental/worktree": {
"post": {
"operationId": "worktree.create",
@@ -12003,6 +12003,53 @@
}
]
},
"Workspace": {
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^wrk.*"
},
"type": {
"type": "string"
},
"name": {
"type": "string"
},
"branch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"extra": {
"anyOf": [
{},
{
"type": "null"
}
]
},
"projectID": {
"type": "string"
}
},
"required": ["id", "type", "name", "branch", "directory", "extra", "projectID"]
},
"NotFoundError": {
"type": "object",
"properties": {
@@ -12309,53 +12356,6 @@
"$ref": "#/components/schemas/ToolListItem"
}
},
"Workspace": {
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^wrk.*"
},
"type": {
"type": "string"
},
"name": {
"type": "string"
},
"branch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"directory": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"extra": {
"anyOf": [
{},
{
"type": "null"
}
]
},
"projectID": {
"type": "string"
}
},
"required": ["id", "type", "name", "branch", "directory", "extra", "projectID"]
},
"Worktree": {
"type": "object",
"properties": {