TypeBox
TypeBox 是一个以 TypeScript 为先的 schema 库。OpenClaw 使用它来定义 Gateway WebSocket 协议(握手、请求/响应、服务器事件)。这些 schema 驱动 运行时校验(TypeBox Compile)、JSON Schema 导出,以及 macOS 应用的 Swift 代码生成。以单一事实来源为准;其余一切均为生成产物。
有关更上层的协议上下文,请从 Gateway 架构 开始了解。
心智模型(30 秒)¶
每条 Gateway WS 消息都是以下三种帧之一:
- Request(请求):
{ type: "req", id, method, params } - Response(响应):
{ type: "res", id, ok, payload | error } - Event(事件):
{ type: "event", event, payload, seq?, stateVersion? }
第一帧必须是 connect 请求。之后,客户端可以调用方法(例如 health、send、chat.send)并订阅事件(例如 presence、tick、agent)。
连接流程(最小化示例):
Client Gateway
|---- req:connect -------->|
|<---- res:hello-ok --------|
|<---- event:tick ----------|
|---- req:health ---------->|
|<---- res:health ----------|
常见方法与事件:
| 类别 | 示例 | 说明 |
|---|---|---|
| 核心 | connect, health, status |
connect 必须是首个请求 |
| 消息传递 | send, agent, agent.wait, system-event, logs.tail |
有副作用的方法需要 idempotencyKey |
| 聊天 | chat.history, chat.send, chat.abort |
WebChat 使用这些 |
| 会话 | sessions.list, sessions.patch, sessions.delete |
会话管理 |
| 自动化 | wake, cron.list, cron.run, cron.runs |
wake 与 cron 控制 |
| 节点 | node.list, node.invoke, node.pair.* |
Gateway WS 加节点操作 |
| 事件 | tick, presence, agent, chat, health, shutdown |
服务器推送 |
权威的对外公告 discovery(能力发现) 清单位于 src/gateway/server-methods-list.ts(listGatewayMethods、GATEWAY_EVENTS)。
Schema 存放位置¶
- 源码桶(source barrels):
packages/gateway-protocol/src/schema-modules.ts拥有规范的领域模块列表,而公开的schema.ts包装器还暴露了ProtocolSchemas。 - 生成器选择:
packages/gateway-protocol/src/schema/protocol-schema-selection.ts从规范桶的*Schema导出和显式的skill-library.ts导入中派生注册表名称,去掉Schema后缀。EXCLUDED_SCHEMA_EXPORTS将辅助项和拥有补充注册表所有者的 schema 排除在该派生选择之外。 - 生成器注册表:
protocol-schemas.ts组合派生选择、MigrationProtocolSchemas和SessionPlacementProtocolSchemas,拒绝重复键并保留规范的 TypeBox 对象。protocol-schema-types.ts保留每个成员的 schema 类型以及公开的 readonly/writable 修饰符。 - 运行时校验器:
packages/gateway-protocol/src/validator-registry.ts,使用protocol-validator.ts中的惰性 TypeBox Compile 持有者。 - 对外公告的功能/发现注册表:
src/gateway/server-methods-list.ts - 服务器握手与方法分发:
src/gateway/server-core-runtime.ts - Node 客户端:
src/gateway/client.ts - 生成的 JSON Schema:
dist/protocol.schema.json(构建产物,不纳入版本控制) - 生成的 Swift 模型:
apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift
派生选择使用完整源码导出名称的字典序——该顺序是在移除 Schema 后缀之前的排序。迁移和会话放置映射按照各自的插入顺序跟随该选择。这取代了以前手动排序的注册片段;JSON 定义顺序和 Swift 声明位置可以改变,而不会改变 schema 数据或原生声明体。
对于符合条件的源码导出,成员资格现在是默认加入(opt-out)的:规范桶中新增的 *Schema 导出会进入生成器注册表,除非被显式排除。请审查每个新导出是否应属于所生成的协议,并将仅作为辅助用途的导出加入 EXCLUDED_SCHEMA_EXPORTS。注册表守卫会检查选择一致性、规范对象、排除项和组合;但它无法独立判断一个新导出的 schema 是否应属于公开的生成协议。
当前流水线¶
pnpm protocol:gen将 JSON Schema(draft-07)写入dist/protocol.schema.json。pnpm protocol:gen:swift生成 Swift 网关模型。pnpm protocol:check:swift验证已提交的 Swift 模型而不重写它们。pnpm protocol:gen:kotlin生成 Android 协议模型和常量。pnpm protocol:check检查注册表结构、运行全部三个生成器,并验证已提交的 Swift 和 Kotlin 输出。JSON Schema 输出是已加入 gitignore 的构建产物,没有已提交的基线可供 diff,因此pnpm protocol:gen改为断言已发布文档的契约(必需的帧定义、帧顺序、type判别器映射、非空的方法元数据),当生成的 schema 偏离该契约时检查失败。
当网关 schema 影响原生客户端时,运行 pnpm protocol:gen:swift,审查生成的 diff,然后运行 pnpm protocol:check:swift。将 schema 与 GatewayModels.swift 更新一起提交。稳定的解码行为应放在专门的 GatewayModelsCompatibilityTests.swift 回归测试中,而不是手工编写的模型副本中。
Schema 在运行时的使用方式¶
- 服务器端:每个入站帧都使用 TypeBox Compile 进行校验。握手只接受 params 与
ConnectParams匹配的connect请求。 - 客户端:JS 客户端在使用事件帧和响应帧之前会对它们进行校验。
- 功能发现(Feature discovery):Gateway 在
hello-ok中发送一个保守的features.methods和features.events列表,数据来自listGatewayMethods()和GATEWAY_EVENTS。 - 该发现列表并不是
coreGatewayHandlers中每个可调用辅助函数的生成转储;一些辅助 RPC 在src/gateway/server-methods/*.ts中实现,但并未列入对外公告的功能列表中。
示例帧¶
连接(第一条消息):
{
"type": "req",
"id": "c1",
"method": "connect",
"params": {
"minProtocol": 3,
"maxProtocol": 4,
"client": {
"id": "openclaw-macos",
"displayName": "macos",
"version": "1.0.0",
"platform": "macos 15.1",
"mode": "ui",
"instanceId": "A1B2"
}
}
}
Hello-ok 响应:
{
"type": "res",
"id": "c1",
"ok": true,
"payload": {
"type": "hello-ok",
"protocol": 4,
"server": { "version": "dev", "connId": "ws-1" },
"features": { "methods": ["health"], "events": ["tick"] },
"snapshot": {
"presence": [],
"health": {},
"stateVersion": { "presence": 0, "health": 0 },
"uptimeMs": 0
},
"auth": { "role": "operator", "scopes": ["operator.read"] },
"policy": { "maxPayload": 1048576, "maxBufferedBytes": 1048576, "tickIntervalMs": 30000 }
}
}
请求与响应:
事件:
最小客户端(Node.js)¶
最小可用流程:连接 + 健康检查。
import { WebSocket } from "ws";
const ws = new WebSocket("ws://127.0.0.1:18789");
ws.on("open", () => {
ws.send(
JSON.stringify({
type: "req",
id: "c1",
method: "connect",
params: {
minProtocol: 4,
maxProtocol: 4,
client: {
id: "cli",
displayName: "example",
version: "dev",
platform: "node",
mode: "cli",
},
},
}),
);
});
ws.on("message", (data) => {
const msg = JSON.parse(String(data));
if (msg.type === "res" && msg.id === "c1" && msg.ok) {
ws.send(JSON.stringify({ type: "req", id: "h1", method: "health" }));
}
if (msg.type === "res" && msg.id === "h1") {
console.log("health:", msg.payload);
ws.close();
}
});
端到端添加方法的完整示例¶
示例:添加一个新的 system.echo 请求,返回 { ok: true, text }。
- Schema(事实来源)
添加到 packages/gateway-protocol/src/schema/system-info.ts(或最接近的功能模块):
export const SystemEchoParamsSchema = Type.Object(
{ text: NonEmptyString },
{ additionalProperties: false },
);
export const SystemEchoResultSchema = Type.Object(
{ ok: Type.Boolean(), text: NonEmptyString },
{ additionalProperties: false },
);
system-info.ts 已由 packages/gateway-protocol/src/schema-modules.ts 导出,因此这两个 schema 会自动以 SystemEchoParams 和 SystemEchoResult 进入 ProtocolSchemas。如果是新的 owner 模块,则将其导出添加到该规范桶文件(canonical barrel)中。审查它引入的其他 *Schema 导出,并在 protocol-schema-selection.ts 中排除仅用于辅助的 schema。将由迁移或会话放置映射所拥有的 schema 保留在这些映射中,并从衍生的选择中排除。
从 owner 模块导出对应的静态类型:
export type SystemEchoParams = Static<typeof SystemEchoParamsSchema>;
export type SystemEchoResult = Static<typeof SystemEchoResultSchema>;
- 验证
在 packages/gateway-protocol/src/validator-registry.ts 中,使用现有的惰性编译器导出一个验证器:
- 服务器行为
在 src/gateway/server-methods/system.ts 中添加一个处理器:
export const systemHandlers: GatewayRequestHandlers = {
"system.echo": ({ params, respond }) => {
const text = String(params.text ?? "");
respond(true, { ok: true, text });
},
};
在 src/gateway/server-methods.ts 中注册它(该文件已合并 systemHandlers),然后将 "system.echo" 添加到 src/gateway/server-methods-list.ts 的 listGatewayMethods 输入中。
如果该方法可被 operator 或 node 客户端调用,还需要在 src/gateway/method-scopes.ts 中对其进行分类,以确保作用域强制和 hello-ok 功能通告保持一致。
- 重新生成
- 测试与文档
在 src/gateway/server.*.test.ts 中添加一个服务器测试,并在文档中注明该方法。
Swift 代码生成行为¶
Swift 生成器会生成:
- 一个
GatewayFrame枚举,包含req、res、event和unknown分支 - 强类型 payload 结构体/枚举
ErrorCode值、GATEWAY_PROTOCOL_VERSION和GATEWAY_MIN_PROTOCOL_VERSION
未知的帧类型会作为原始 payload 保留,以保持前向兼容。
某些注册表名称会别名指向同一个规范 schema 对象。scripts/protocol-gen-swift.ts 中显式的规范别名偏好设置,可在注册表枚举发生变化时保留现有的公共 Swift 类型名称。请审查生成的声明体,包括字段类型、初始化器以及编码/解码行为;移动声明时不得静默地选择不同的名义类型。
已发布 JSON Schema 的 oneOf 帧顺序是独立的契约:依次为 req、res、event,并带有匹配的 type 判别器映射。scripts/lib/protocol-schema-document.mts 独立于注册表定义顺序来拥有并检查该顺序。
版本控制与兼容性¶
PROTOCOL_VERSION位于packages/gateway-protocol/src/version.ts(当前值:4)。- 客户端发送
minProtocol和maxProtocol;服务器会拒绝其当前协议不在范围内的区间。 - Swift 模型保留未知帧类型,以避免破坏较旧的客户端。
Schema 模式与约定¶
- 大多数对象使用
additionalProperties: false来实现严格 payload。 NonEmptyString(Type.String({ minLength: 1 }))是 ID 和方法/事件名称的默认类型。- 顶层
GatewayFrame在type上使用判别器。 - 具有副作用的方法通常需要在参数中包含
idempotencyKey(示例:send、poll、agent、chat.send)。 agent接受可选的internalEvents,用于运行时生成的编排上下文(例如子代理/定时任务完成交接);请将其视为内部 API 表面。
实时 Schema JSON¶
生成的 JSON Schema 是构建产物,不会提交到仓库中。在软件包发布期间,当前测试版 schema 可通过以下地址获取:
修改 Schema 时¶
- 更新所属
packages/gateway-protocol/src/schema/*.ts模块中的 TypeBox schema。确保新模块已由schema-modules.ts导出,检查其自动生成的*Schema成员资格,并根据需要更新辅助排除项或现有的补充属主映射。 - 在
src/gateway/server-methods-list.ts中注册该方法/事件。 - 当新的 RPC 需要操作员或节点作用域分类时,更新
src/gateway/method-scopes.ts。 - 运行
pnpm protocol:check。 - 提交重新生成的 Swift 模型。
相关文档¶
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw