OpenCode 源码解析
第 12 讲:Control Plane 与 Gateway——多工作区编排核心
基于 dev 分支源码 · 2026-07-19
一、Control Plane:OpenCode 的调度中心
前十一讲覆盖了 OpenCode 从 CLI 到配置系统的核心能力。这一讲进入Control Plane(控制平面)——OpenCode 实现多工作区编排、远程会话迁移和事件同步的核心基础设施。Control Plane 横跨 7 个文件、约 1,139 行代码,是整个系统的"调度中心"。
📦 本讲核心文件
packages/opencode/src/control-plane/workspace.ts(964 行)— 工作区服务与同步循环
packages/opencode/src/control-plane/types.ts(59 行)— 工作区类型定义
packages/opencode/src/control-plane/workspace-adapter-runtime.ts(51 行)— 适配器运行时
packages/opencode/src/control-plane/util.ts(39 行)— 事件等待工具
packages/opencode/src/control-plane/adapters/(2 文件)— 工作区适配器注册
二、Workspace Service 架构
workspace.ts(964 行)是 Control Plane 的核心,实现了工作区的 CRUD、会话迁移、远程同步和 SSE 事件推送。
1. Workspace 服务接口
📄 control-plane/workspace.ts (第 131-147 行)
export interface Interface {
readonly create: (input: CreateInput)
=> Effect.Effect<Info, CreateError>
readonly sessionWarp: (input: SessionWarpInput)
=> Effect.Effect<void, SessionWarpError>
readonly list: (project: Project.Info)
=> Effect.Effect<Info[]>
readonly syncList: (project: Project.Info)
=> Effect.Effect<void>
readonly get: (id: WorkspaceV2.ID)
=> Effect.Effect<Info | undefined>
readonly remove: (id: WorkspaceV2.ID)
=> Effect.Effect<Info | undefined>
readonly status: ()
=> Effect.Effect<ConnectionStatus[]>
readonly isSyncing: (workspaceID: WorkspaceV2.ID)
=> Effect.Effect<boolean>
readonly waitForSync: (workspaceID: WorkspaceV2.ID,
state: Record<string, number>,
signal?: AbortSignal, timeout?: number)
=> Effect.Effect<void, WaitForSyncError>
readonly startWorkspaceSyncing:
(projectID: ProjectV2.ID)
=> Effect.Effect<void>
}
设计亮点:11 个公开方法覆盖了工作区生命周期管理、会话迁移、远程同步和状态查询。所有方法返回 Effect.Effect,支持异步组合和错误处理。
2. 工作区类型定义
📄 control-plane/types.ts (第 7-21 行)
export const WorkspaceInfo = Schema.Struct({
id: WorkspaceV2.ID,
type: Schema.String,
name: Schema.String,
branch: Schema.optional(
Schema.NullOr(Schema.String)),
directory: Schema.optional(
Schema.NullOr(Schema.String)),
extra: Schema.optional(
Schema.NullOr(Schema.Unknown)),
projectID: ProjectV2.ID,
}).annotate({ identifier: "Workspace" })
export const WorkspaceListedInfo = Schema.Struct(
Struct.omit(WorkspaceInfo.fields, ["id"])
).annotate({ identifier: "WorkspaceListedInfo" })
类型设计:WorkspaceInfo 包含 ID、类型、名称、分支、目录、额外数据和项目 ID。WorkspaceListedInfo 是列表视图,省略了 ID 字段。
三、工作区适配器系统
工作区适配器定义了本地和远程工作区的行为抽象,支持扩展新的工作区类型。
1. 适配器接口
📄 control-plane/types.ts (第 46-59 行)
export type WorkspaceAdapter = {
name: string
description: string
configure(info: WorkspaceInfo,
context?: WorkspaceAdapterContext)
: WorkspaceInfo | Promise<WorkspaceInfo>
create(info: WorkspaceInfo,
env: Record<string, string | undefined>,
from?: WorkspaceInfo,
context?: WorkspaceAdapterContext)
: Promise<void>
list?(context?: WorkspaceAdapterContext)
: WorkspaceListedInfo[] | Promise<WorkspaceListedInfo[]>
remove(info: WorkspaceInfo,
context?: WorkspaceAdapterContext)
: Promise<void>
target(info: WorkspaceInfo,
context?: WorkspaceAdapterContext)
: Target | Promise<Target>
}
适配器方法:configure 配置工作区、create 创建工作区、list 列出工作区、remove 删除工作区、target 获取目标地址。支持本地和远程两种 Target 类型。
2. 适配器运行时
📄 control-plane/workspace-adapter-runtime.ts (第 14-49 行)
export const target = (info: WorkspaceInfo) =>
Effect.gen(function* () {
const adapter = getAdapter(info.projectID, info.type)
const ctx = yield* context
return yield* EffectBridge.fromPromise(
() => adapter.target(info, ctx)
)
})
export const configure = (adapter: WorkspaceAdapter,
info: WorkspaceInfo) =>
Effect.gen(function* () {
const ctx = yield* context
return yield* EffectBridge.fromPromise(
() => adapter.configure(info, ctx)
)
})
export const create = (adapter: WorkspaceAdapter,
info: WorkspaceInfo,
env: Record<string, string | undefined>,
from?: WorkspaceInfo) =>
Effect.gen(function* () {
const ctx = yield* context
return yield* EffectBridge.fromPromise(
() => adapter.create(info, env, from, ctx)
)
})
运行时设计:所有适配器方法通过 EffectBridge.fromPromise 包装,将异步 Promise 转换为 Effect。上下文通过 InstanceRef 和 WorkspaceRef 注入。
四、SSE 同步与事件推送
Control Plane 通过 SSE(Server-Sent Events)实现远程工作区的实时同步。
1. SSE 连接
📄 control-plane/workspace.ts (第 184-199 行)
const connectSSE = Effect.fn("Workspace.connectSSE")(
function* (url: URL | string,
headers: HeadersInit | undefined) {
const response = yield* http.execute(
HttpClientRequest.get(route(url, "/global/event"), {
headers: new Headers(headers),
accept: "text/event-stream",
})
)
if (response.status < 200 || response.status >= 300) {
return yield* new SyncHttpError({
message: `Workspace sync HTTP failure: ${response.status}`,
status: response.status,
})
}
})
SSE 流程:连接到远程工作区的 /global/event 端点,接收实时事件流。支持状态码检查和错误处理。
2. 事件等待工具
📄 control-plane/util.ts (第 4-38 行)
export function waitEvent(input: {
timeout: number; signal?: AbortSignal;
fn: (event: GlobalEvent) => boolean
}) {
if (input.signal?.aborted)
return Effect.fail(input.signal.reason ??
new Error("Request aborted"))
return Effect.callback<void, unknown>((resume) => {
const handler = (event: GlobalEvent) => {
try {
if (!input.fn(event)) return
cleanup()
resume(Effect.void)
} catch (error) {
cleanup()
resume(Effect.fail(error))
}
}
const cleanup = () => {
clearTimeout(timeout)
GlobalBus.off("event", handler)
input.signal?.removeEventListener("abort", abort)
}
const timeout = setTimeout(() => {
cleanup()
resume(Effect.fail(
new Error("Timed out waiting for global event")
))
}, input.timeout)
GlobalBus.on("event", handler)
return Effect.sync(cleanup)
})
}
工具设计:waitEvent 等待满足条件的全局事件,支持超时和取消信号。使用 Effect.callback 实现异步等待。
五、总结
🔹 11 个公开方法覆盖工作区 CRUD、会话迁移、远程同步
🔹 WorkspaceAdapter 抽象支持本地和远程工作区扩展
🔹 EffectBridge 将异步 Promise 包装为 Effect
🔹 SSE 实现远程工作区实时事件同步
🔹 waitEvent 工具支持超时和取消的全局事件等待
🔹 FiberMap 管理多个工作区的同步 Fiber
← 系列导航 →
← 第 11 讲:Config 配置系统 | 第 13 讲:TUI 终端界面 →
关注公众号获取更多 OpenCode 源码解析干货
源码:https://github.com/opencode-ai/opencode
夜雨聆风