@massapi/openclaw-channel
v1.0.10
Published
`openclaw-msghub` 是一个 OpenClaw channel plugin,用来把本地 MsgHub 的 SSE 事件流和响应接口接入 OpenClaw。
Readme
openclaw-msghub
openclaw-msghub 是一个 OpenClaw channel plugin,用来把本地 MsgHub 的 SSE 事件流和响应接口接入 OpenClaw。
当前实现的 channel id 为 msghub,面向一对一会话(chatTypes: ["direct"]),支持流式 block 回复。
1. 接入目标
插件负责两件事:
- 从 MsgHub 的 SSE 事件接口持续接收入站消息。
- 将 OpenClaw 生成的回复通过 MsgHub 的响应接口回写。
对应实现:
- 入口定义:src/index.ts
- channel 定义:src/channel.ts
- 配置解析:src/config.ts
- 网关和入站分发:src/gateway.ts
- HTTP/协议处理:src/http.ts
2. Channel 接入规范
本实现遵循 OpenClaw channel plugin 的基本约定:
- 使用
defineChannelPluginEntry(...)暴露插件入口。 - 使用
createChatChannelPlugin(...)定义 channel 能力。 - 通过
configSchema+config adapter暴露配置面。 - 通过
gateway.startAccount(...)建立长连接接收入站事件。 - 通过
outbound.attachedResults.sendText/sendMedia(...)发送出站回复。 - 通过
runInboundReplyTurn(...)把外部消息转成 OpenClaw 标准 agent turn。
代码里已经包含 OpenClaw 的契约测试:
其中使用了 expectChannelPluginContract(...) 校验基础 channel contract。
3. MsgHub 协议
3.1 入站事件
插件通过下面的 SSE 接口订阅消息:
GET /api/agent/events?access_token=<AGENT_TOKEN>
Accept: text/event-stream默认基地址是:
http://127.0.0.1:8787完整 URL 由 eventsUrl 生成,当前实现把 token 放在 query string:
http://127.0.0.1:8787/api/agent/events?access_token=...SSE data: 中的 JSON 事件结构为:
{
"requestId": "request_xxx",
"message": {
"id": "msg_xxx",
"sender": "alice",
"senderName": "Alice",
"contentType": "text/markdown",
"data": "hello",
"createdAt": "1777892405"
}
}字段语义:
requestId: 本次请求的关联 id;插件回复时原样带回。message.id: 上游消息 id。message.sender: 对端标识;当前实现将它映射为 direct peer id。message.senderName: 展示名,可选。message.contentType: 当前设计使用text/markdown。message.data: 实际消息体。message.createdAt: 可选 Unix 时间戳字符串(秒级,如"1777892405";毫秒级数值"1777892405123"同样接受),插件统一转成毫秒时间。为向后兼容,ISO 8601 字符串仍可解析。
3.2 入站消息文本提取规则
extractTextFromEvent 的处理规则是:
- 若
contentType === "text/markdown",直接把data当消息文本。 - 若
contentType === "application/json",先尝试 JSON.parse。 - 解析成功后,按
text、message、content、prompt的优先顺序提取字符串字段。 - 如果 JSON 解析失败,或没有命中这些字段,则回退为原始
data。
3.3 去重策略
同一账号的 SSE 事件流只保留相邻不重复的消息:
- 对每一条入站事件,计算
message.data的 SHA-256 hash。 - 仅当 hash 与「上一条已处理消息」的 hash 不同才会分发给 agent;否则被判为相邻重复消息。
- 对相邻重复消息,插件直接通过
POST /api/agent/respond回复"重复请求",不再进入 agent turn 流程。 - 比较只针对相邻消息,不再基于时间窗口;
A B A序列中的第二个A仍会被处理。 - 连接断开并重连后,缓存会随账号网关重新初始化,首条消息一定会被处理。
实现见 shouldProcessMsgHubEvent、hashMsgHubData 和 MSGHUB_DUPLICATE_REPLY_TEXT。
3.4 出站响应
插件通过下面的接口回写响应:
POST /api/agent/respond
Authorization: Bearer <AGENT_TOKEN>
Content-Type: application/json请求体结构:
{
"requestId": "request_xxx",
"response": {
"sender": "Local Hub",
"senderName": "Local Hub",
"contentType": "text/markdown",
"data": "done"
}
}对应生成逻辑在:
编码规则:
contentType固定为text/markdown。data直接发送 OpenClaw 生成的回复文本。sender和senderName统一使用账号配置里的name。
4. OpenClaw 内部映射
当前实现把 MsgHub 事件映射成 OpenClaw 的 direct chat turn:
agentId: 固定为defaultchannel:msghubpeer.id:message.senderTo/OriginatingTo:requestIdMessageSid:message.idSenderName:message.senderName ?? message.sender
关键逻辑见 handleMsgHubEvent。
这意味着:
- 同一个
sender会被视为同一个 direct peer。 - 回复线程关联依赖
requestId。 - 当前没有群聊、多线程、多 agent 路由扩展。
5. 配置规范
插件读取 channels.msghub 配置段,默认账号 id 为 default。
支持的字段见 msgHubConfigSchema:
{
"channels": {
"msghub": {
"enabled": true,
"name": "Local Hub",
"baseUrl": "http://127.0.0.1:8787",
"accessToken": "token-1",
"allowFrom": ["alice"],
"dmPolicy": "open"
}
}
}说明:
baseUrl默认值:http://127.0.0.1:8787accessToken也可通过环境变量MSGHUB_ACCESS_TOKEN提供sender/senderName不再单独配置,统一使用namecontentType固定为text/markdowndmPolicy支持:open、allowlist、disabled、pairing
也支持多账号形式:
{
"channels": {
"msghub": {
"accessToken": "token-default",
"accounts": {
"dev": {
"baseUrl": "http://127.0.0.1:8788",
"accessToken": "token-dev"
}
}
}
}
}6. 兼容性结论
截至 2026-05-04,我核对了 npm 最新发布版本,openclaw 当前最新版本是 2026.5.2。本仓库的 package.json 依赖也是:
"openclaw": "^2026.5.2"因此,对“本实现支持最新的 OpenClaw 吗”这个问题,结论是:
是,按当前公开最新版本 [email protected] 看,这个实现就是按该版本 SDK 编写的,并且测试直接依赖这一版的 channel contract helper。
但要区分两个层面:
OpenClaw SDK 兼容性- 当前代码使用的
createChatChannelPlugin、defineChannelPluginEntry、runInboundReplyTurn、runtime/config adapter 等接口,都来自[email protected]。 - 只要构建和测试通过,就说明它和这版 SDK 是对齐的。
- 当前代码使用的
MsgHub 上游协议兼容性- 当前实现假设 SSE 事件结构仍然是
requestId + message{...}。 - 当前实现假设响应接口仍然是
POST /api/agent/respond,并接受response.sender/senderName/contentType/data。 - 如果 MsgHub 后续修改字段名、鉴权方式、JSON payload 结构或线程语义,代码需要同步调整。
- 当前实现假设 SSE 事件结构仍然是
7. 当前实现的边界
已经支持:
- OpenClaw channel 基础契约
- direct chat
- SSE 入站
- bearer token 出站
- JSON/纯文本响应编码
allowFrom和dmPolicy
暂未覆盖:
- 群聊/频道消息
- richer attachment/media 原生协议
- 上游自定义复杂 JSON payload 的专用解析
- 自动重连、退避、心跳监控等更强健的网关策略
8. 验证建议
建议至少验证三件事:
- 构建通过:TypeScript 类型与当前 OpenClaw SDK 一致。
- 测试通过:基础 channel contract 未破坏。
- 联调通过:对真实 MsgHub 的
/api/agent/events和/api/agent/respond完整走通。
8. 测试客户端
仓库内提供了一个独立测试客户端,用来直接验证 MsgHub 服务端的 SSE 长连接和响应接口,不依赖 OpenClaw:
npm run test:client -- --baseUrl http://127.0.0.1:8787 --accessToken <token>可选参数:
--sender <id>: 回写接口里的response.sender,默认msghub-test-client--senderName <name>: 回写接口里的response.senderName,默认与sender相同--reconnectMs <ms>: SSE 断开后的重连间隔,默认3000--compact: 原始收发 JSON 单行打印,默认使用格式化 JSON
行为说明:
- 连接
GET /api/agent/events?access_token=... - 收到消息后,将提取到的文本作为
text/markdown的data原样回写到POST /api/agent/respond - 打印
[recv:raw]原始入站事件和[send:raw]原始响应请求体 - 对
message.data计算 SHA-256 hash,若与上一条相邻消息重复则不再回写原文,而是直接向POST /api/agent/respond回复"重复请求" - 如果 SSE 被服务端关闭,会每隔 3 秒重连一次,并在终端输出连接、断开、响应日志
仓库内已有基础测试,联调时重点关注:
- SSE 是否稳定输出
data:JSON message.contentType是否为text/markdownmessage.data是否为原始消息文本requestId是否可直接用于回写响应
