@workclaw/openclaw-workchat 1.0.0 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,210 +1,210 @@
1
- # openclaw-workchat
2
-
3
- WorkChat 开放平台 OpenClaw 通道插件仓库。插件源码平铺在仓库根目录
4
- (参照官方 `@tencent-weixin/openclaw-weixin` 的包布局),业务协议来自
5
- zhixiaotu_channel。
6
-
7
- - 插件包:`@workclaw/openclaw-workchat`(根目录 index.ts + src/)
8
- - 部署目标:`work-claw-desktop`(openclaw-extensions/workchat → 运行时 third-party-extensions)
9
- - 使用方式:`npm run sync` 一键同步并打包
10
-
11
- ## 快速开始
12
-
13
- ```bash
14
- npm install
15
- npm run sync # 同步插件到 ../work-claw-desktop 并预编译
16
- ```
17
-
18
- ## 仓库结构
19
-
20
- ```
21
- openclaw-workchat/
22
- ├── index.ts # 插件入口(buildChannelConfigSchema 注册)
23
- ├── openclaw.plugin.json # OpenClaw 插件清单
24
- ├── package.json # @workclaw/openclaw-workchat
25
- ├── tsconfig.json
26
- ├── vendor.d.ts # 无类型依赖的声明(qrcode-terminal)
27
- ├── src/
28
- │ ├── channel.ts # ChannelPlugin 契约(gateway/loginWithQr/auth.login/outbound/status)
29
- │ ├── compat.ts # 宿主版本 fail-fast 守卫
30
- │ ├── api/
31
- │ │ ├── api.ts # 开放平台 HTTP 客户端(token 缓存/开连接/收发)
32
- │ │ └── types.ts # 协议类型与常量
33
- │ ├── auth/
34
- │ │ ├── accounts.ts # 账号索引+每账号数据文件+resolveAccount
35
- │ │ ├── login-qr.ts # bind-hardware 二维码本地生成 + INIT_AGENT 等待
36
- │ │ ├── login-qr-terminal.ts # CLI 终端二维码显示
37
- │ │ └── pairing.ts # 框架 allowFrom 配对存储(文件锁)
38
- │ ├── config/config-schema.ts # zod 通道配置 schema
39
- │ ├── messaging/
40
- │ │ ├── inbound.ts # 帧解析 + MsgContext 转换
41
- │ │ ├── process-message.ts # 入站处理链(鉴权→路由→记录→派发)
42
- │ │ ├── send.ts # reply/push 出站发送
43
- │ │ ├── slash-commands.ts # /echo、/toggle-debug 斜杠指令
44
- │ │ ├── debug-mode.ts # 每 bot debug 开关(持久化)
45
- │ │ ├── markdown-filter.ts # 流式 markdown 过滤器
46
- │ │ ├── error-notice.ts # 回复失败的错误提示
47
- │ │ └── outbound-hooks.ts # message_sending/sent 框架 hook
48
- │ ├── monitor/monitor.ts # WebSocket 监听循环(退避重连/ping-pong)
49
- │ ├── storage/state-dir.ts # OPENCLAW_STATE_DIR 解析
50
- │ └── util/ # logger(JSON 行主日志)/redact/random
51
- ├── openclaw-extensions/ # 官方插件源码参考(只读,不参与构建)
52
- ├── scripts/
53
- │ ├── bundle.mjs # esbuild 打包 index.js(免网关 jiti 转译)
54
- │ └── sync-to-desktop.mjs # 同步到 work-claw-desktop 并预编译
55
- └── package-lock.json
56
- ```
57
-
58
- ## 使用流程(对应用户三步)
59
-
60
- 1. **安装插件**:`npm run sync` 把插件源码同步到
61
- `../work-claw-desktop/openclaw-extensions/workchat`,打包出 `index.js`,
62
- 并同步进运行时 `third-party-extensions/workchat`;桌面端在 openclaw.json
63
- 启用 `openclaw-workchat`。
64
- 2. **获取二维码**:桌面端设置页点「扫码绑定 WorkChat」→ 网关 RPC
65
- `web.login.start`(channel=openclaw-workchat)→ 插件**本地生成**
66
- bind-hardware 二维码。CLI 用户也可 `openclaw channels login
67
- --channel openclaw-workchat`(auth.login 适配器,终端渲染二维码)。
68
- 3. **扫码绑定**:插件本地组装 bind-hardware JSON(cmd/type/name 固定,
69
- 其余为设备实时信息 + 当前用户 app_key),用户用 WorkChat App 扫码;
70
- 平台通过 WebSocket 推送 `INIT_AGENT` 事件,插件绑定智能体+创建账户
71
- (**不写 apiKey**——凭据已由网关环境变量注入),`web.login.wait` 由该
72
- 事件唤醒返回。之后即可在 WorkChat 与本地 OpenClaw 对话。
73
-
74
- ## bind-hardware 二维码(本地生成,无平台接口)
75
-
76
- ```json
77
- {
78
- "cmd": "bind-hardware",
79
- "data": {
80
- "type": "99", // 固定
81
- "name": "WorkClaw 桌面端", // 固定
82
- "serial_number": "WC-XXXXXXXXXXXXXXXX", // 主机名+MAC 哈希,稳定设备序列号
83
- "product_model": "WorkClaw Desktop",
84
- "main_processor": "<os.cpus() 型号>",
85
- "memory": "<os.totalmem() 取整>GB",
86
- "emmc": "<fs.statfs 系统磁盘总容量>",
87
- "system": "<os.type() release() arch()>",
88
- "app_key": "<当前用户 WORKCHAT_API_KEY>"
89
- }
90
- }
91
- ```
92
-
93
- 绑定确认不走 HTTP 轮询:用户扫码后平台往 WebSocket 推 `INIT_AGENT` 事件
94
- (data 带 `id`/`agentId` 与 `futureId`/`userId`),插件 `completeWorkChatBinding`
95
- 唤醒挂起的 `web.login.wait`。
96
-
97
- ## 与 openclaw-weixin 的对齐清单
98
-
99
- - **入站处理链**:斜杠命令拦截 → `resolveSenderCommandAuthorizationWithRuntime`
100
- 配对鉴权(allowFrom 文件 + 文件锁)→ `resolveAgentRoute` →
101
- `recordInboundSession` → `createReplyDispatcherWithTyping` →
102
- `dispatchReplyFromConfig`
103
- - **出站**:StreamingMarkdownFilter 过滤 → `message_sending` hook(可取消/改写)
104
- → 发送 → `message_sent` hook;失败走 error-notice
105
- - **channel 契约**:`messaging.targetResolver`、`agentPrompt.messageToolHints`
106
- (cron 投递必须带 to+accountId)、`status` 三件套、`auth.login` CLI 入口、
107
- `gatewayMethods` QR RPC
108
- - **运维**:`/toggle-debug` 开启全链路耗时统计;JSON 行日志写入主 openclaw
109
- 日志文件(与其他渠道同格式);`compat.ts` 宿主版本守卫
110
- - **协议差异**(合理保留):WebSocket 推送代替长轮询(无 sync-buf 游标);
111
- 二维码本地生成(非平台下发);绑定确认靠 WS 事件(非 HTTP 轮询);
112
- 凭据为环境变量注入的 appKey/appSecret(非扫码 token);无 typing API
113
-
114
- ## 凭据链路(与微信插件的关键差异)
115
-
116
- 凭据不是扫码登录获得:桌面端登录后从
117
- `/open-apis/desktop/oauth2/workchat/credentials` 换取 `workchatApiKey`/
118
- `workchatAppSecret`,以 `WORKCHAT_API_KEY` / `WORKCHAT_APP_SECRET` /
119
- `USER_ID` 环境变量注入网关进程;openclaw.json 使用 `${WORKCHAT_API_KEY}` /
120
- `${WORKCHAT_APP_SECRET}` 占位符,密钥不落盘。
121
-
122
- ## 连接协议
123
-
124
- ```
125
- POST {base}/authen/v1/access_token/internal app_key/app_secret -> accessToken
126
- POST {base}/open-apis/v1/connections/open subscriptions -> endpoint + ticket
127
- WS {endpoint}?ticket=... AGENT_MESSAGE/EVENT/SYSTEM
128
- ```
129
-
130
- - 开连接时的订阅声明:`[{type:"CALLBACK",topic:"AGENT_MESSAGE"},{type:"EVENT",topic:"*"},{type:"SYSTEM",topic:"*"}]`。
131
- - `SYSTEM` + topic `ping` → 回 `pong`;topic `disconnect` → 关闭并重连。
132
- - `EVENT` + `AGENT_CREATED|AGENT_UPDATED|AGENT_DELETED|INIT_AGENT` → 账号生命周期。
133
- - `CALLBACK` + topic `AGENT_MESSAGE` → 用户消息。
134
-
135
- ## WebSocket 帧协议详解(信封结构)
136
-
137
- 每帧是一个 JSON 信封:`{ type, metadata: { time, event|topic, contentType }, data }`,
138
- `data` 可能是对象,也可能是 JSON 字符串(解析前需二次 `JSON.parse`),甚至是裸标量
139
- (如 INIT_AGENT 的 data 为字符串化的 agent id,需归一化为 `{id}`)。
140
-
141
- | type | metadata 判定 | data 内容 | 插件处理 |
142
- |------|--------------|-----------|----------|
143
- | `SYSTEM` | topic=`ping` | 任意(回显) | 回 pong:`{code:200,message:"pong",metadata:{contentType:"application/json"},data:JSON.stringify(原data)}`;成功回包即刷新 `lastEventAt`(连通性判据) |
144
- | `SYSTEM` | topic=`disconnect` | — | 主动关闭 WS,交给重连循环 |
145
- | `EVENT` | event=`INIT_AGENT` | `{id, futureId}`(或裸 agentId) | 扫码绑定完成事件:持久化账号、绑定 agentId/userId、唤醒 web.login.wait;重绑时先清理旧 agent 账号 |
146
- | `EVENT` | event=`AGENT_CREATED` | `{id, futureId, ...}` | 平台创建智能体:保存账号并回执状态(notifyAgentCreatedStatus) |
147
- | `EVENT` | event=`AGENT_UPDATED` | agent 对象 | 无本地动作(记录日志) |
148
- | `EVENT` | event=`AGENT_DELETED` | `{id, ...}` | 删除对应本地账号绑定(含状态文件/索引/allowFrom/openclaw.json accounts 条目)并触发渠道重载 |
149
- | `CALLBACK` | topic=`AGENT_MESSAGE` | `{userId, conversationId, msgId, agentId, message: {content}}` | 用户消息:斜杠命令 → 配对鉴权 → 路由 → 记录会话 → 派发回复 |
150
- | — | 其他/未知 | — | 忽略 |
151
-
152
- zhixiaotu 参考实现(`zhixiaotu_channel/packages/plugin`)中还存在但 workchat 插件
153
- 暂未接的 EVENT 类型:`TOOLS_LIST`、`SKILLS_LIST`、`SKILL_DO_REMIND`(skills 事件)、
154
- `CRON_REMIND`(定时任务事件)——协议格式见
155
- `zhixiaotu_channel/packages/plugin/src/gateway/message-context.ts`。
156
-
157
- ### 连通性判据(诊断口径)
158
-
159
- - **连接已建立**:WS `onopen` 成功后插件才上报 `running: true`(`monitor.ts`)。
160
- - **连接保活**:服务端周期性下发 `SYSTEM/ping`(节奏约 1-2 分钟),插件回 pong
161
- 成功后刷新 `lastEventAt`。因此「WS 活着」= `running === true && lastError 为空
162
- && lastEventAt 距今 < 10 分钟`。
163
- - 桌面端连通性测试(`work-claw-desktop/src/main/im/imGatewayManager.ts`
164
- `testWorkchatOpenClawConnectivity`)通过网关 `channels.status` RPC 读取上述快照,
165
- 按此口径给出 pass/fail。
166
-
167
- ## 开发
168
-
169
- ```bash
170
- npm install # 安装 esbuild/typescript
171
- npm run bundle # 打包 index.js
172
- npm run sync # 同步到 ../work-claw-desktop(可传路径参数)
173
- npm run typecheck
174
- ```
175
-
176
- ## 配置项(channels.openclaw-workchat)
177
-
178
- | 配置项 | 类型 | 必填 | 说明 |
179
- |--------|------|------|------|
180
- | enabled | boolean | 否 | 是否启用通道 |
181
- | baseUrl | string | 否 | 默认 `https://workbrain.cn/backend-api` |
182
- | websocketUrl | string | 否 | 默认从 baseUrl 推导 |
183
- | appKey | string | 是 | `${WORKCHAT_API_KEY}` 占位符 |
184
- | appSecret | string | 是 | `${WORKCHAT_APP_SECRET}` 占位符 |
185
- | accounts | object | 否 | 多账号配置,每项可覆盖 agentId/userId |
186
-
187
- ## 断线重连
188
-
189
- - 指数退避(5s 起步,上限 5 分钟),鉴权失败(errCode 2001 /
190
- unauthorized / 缺凭据)停止重连。
191
- - 限频(“访问过于频繁”)时以 1s 最小退避重试。
192
-
193
- ## 测试
194
-
195
- ```bash
196
- npm test # vitest:accounts(索引/清理/prune 边界)+ inbound(帧解析/data 归一化)
197
- ```
198
-
199
- ## TODO(已知留白)
200
-
201
- - **事件接入**:`TOOLS_LIST` / `SKILLS_LIST` / `SKILL_DO_REMIND` / `CRON_REMIND`
202
- 暂不处理(归入 ignored,debug 日志记录 `ignoredKind`)。协议参考
203
- `zhixiaotu_channel/packages/plugin/src/gateway/message-context.ts`,接入时按
204
- zhixiaotu 的 dispatcher 补 handler。
205
- - **账号文件写入加锁**:`handleAgentLifecycleEvent` 里保存/清理账号与
206
- `triggerWorkChatChannelReload` 触发的重载存在并发读写 accounts.json 的窗口;
207
- 可复用 `pairing.ts` 的 `withFileLock`。
208
- - **serial 持久化**:`buildDeviceSerial` 每次扫码按主机名+MAC 现算,网卡变化
209
- (VPN/虚拟网卡)会得到不同 serial,平台可能视为新设备导致重复绑定;
210
- 建议首次生成后持久化到 state 目录。
1
+ # openclaw-workchat
2
+
3
+ WorkChat 开放平台 OpenClaw 通道插件仓库。插件源码平铺在仓库根目录
4
+ (参照官方 `@tencent-weixin/openclaw-weixin` 的包布局),业务协议来自
5
+ zhixiaotu_channel。
6
+
7
+ - 插件包:`@workclaw/openclaw-workchat`(根目录 index.ts + src/)
8
+ - 部署目标:`work-claw-desktop`(openclaw-extensions/workchat → 运行时 third-party-extensions)
9
+ - 使用方式:`npm run sync` 一键同步并打包
10
+
11
+ ## 快速开始
12
+
13
+ ```bash
14
+ npm install
15
+ npm run sync # 同步插件到 ../work-claw-desktop 并预编译
16
+ ```
17
+
18
+ ## 仓库结构
19
+
20
+ ```
21
+ openclaw-workchat/
22
+ ├── index.ts # 插件入口(buildChannelConfigSchema 注册)
23
+ ├── openclaw.plugin.json # OpenClaw 插件清单
24
+ ├── package.json # @workclaw/openclaw-workchat
25
+ ├── tsconfig.json
26
+ ├── vendor.d.ts # 无类型依赖的声明(qrcode-terminal)
27
+ ├── src/
28
+ │ ├── channel.ts # ChannelPlugin 契约(gateway/loginWithQr/auth.login/outbound/status)
29
+ │ ├── compat.ts # 宿主版本 fail-fast 守卫
30
+ │ ├── api/
31
+ │ │ ├── api.ts # 开放平台 HTTP 客户端(token 缓存/开连接/收发)
32
+ │ │ └── types.ts # 协议类型与常量
33
+ │ ├── auth/
34
+ │ │ ├── accounts.ts # 账号索引+每账号数据文件+resolveAccount
35
+ │ │ ├── login-qr.ts # bind-hardware 二维码本地生成 + INIT_AGENT 等待
36
+ │ │ ├── login-qr-terminal.ts # CLI 终端二维码显示
37
+ │ │ └── pairing.ts # 框架 allowFrom 配对存储(文件锁)
38
+ │ ├── config/config-schema.ts # zod 通道配置 schema
39
+ │ ├── messaging/
40
+ │ │ ├── inbound.ts # 帧解析 + MsgContext 转换
41
+ │ │ ├── process-message.ts # 入站处理链(鉴权→路由→记录→派发)
42
+ │ │ ├── send.ts # reply/push 出站发送
43
+ │ │ ├── slash-commands.ts # /echo、/toggle-debug 斜杠指令
44
+ │ │ ├── debug-mode.ts # 每 bot debug 开关(持久化)
45
+ │ │ ├── markdown-filter.ts # 流式 markdown 过滤器
46
+ │ │ ├── error-notice.ts # 回复失败的错误提示
47
+ │ │ └── outbound-hooks.ts # message_sending/sent 框架 hook
48
+ │ ├── monitor/monitor.ts # WebSocket 监听循环(退避重连/ping-pong)
49
+ │ ├── storage/state-dir.ts # OPENCLAW_STATE_DIR 解析
50
+ │ └── util/ # logger(JSON 行主日志)/redact/random
51
+ ├── openclaw-extensions/ # 官方插件源码参考(只读,不参与构建)
52
+ ├── scripts/
53
+ │ ├── bundle.mjs # esbuild 打包 index.js(免网关 jiti 转译)
54
+ │ └── sync-to-desktop.mjs # 同步到 work-claw-desktop 并预编译
55
+ └── package-lock.json
56
+ ```
57
+
58
+ ## 使用流程(对应用户三步)
59
+
60
+ 1. **安装插件**:`npm run sync` 把插件源码同步到
61
+ `../work-claw-desktop/openclaw-extensions/workchat`,打包出 `index.js`,
62
+ 并同步进运行时 `third-party-extensions/workchat`;桌面端在 openclaw.json
63
+ 启用 `openclaw-workchat`。
64
+ 2. **获取二维码**:桌面端设置页点「扫码绑定 WorkChat」→ 网关 RPC
65
+ `web.login.start`(channel=openclaw-workchat)→ 插件**本地生成**
66
+ bind-hardware 二维码。CLI 用户也可 `openclaw channels login
67
+ --channel openclaw-workchat`(auth.login 适配器,终端渲染二维码)。
68
+ 3. **扫码绑定**:插件本地组装 bind-hardware JSON(cmd/type/name 固定,
69
+ 其余为设备实时信息 + 当前用户 app_key),用户用 WorkChat App 扫码;
70
+ 平台通过 WebSocket 推送 `INIT_AGENT` 事件,插件绑定智能体+创建账户
71
+ (**不写 apiKey**——凭据已由网关环境变量注入),`web.login.wait` 由该
72
+ 事件唤醒返回。之后即可在 WorkChat 与本地 OpenClaw 对话。
73
+
74
+ ## bind-hardware 二维码(本地生成,无平台接口)
75
+
76
+ ```json
77
+ {
78
+ "cmd": "bind-hardware",
79
+ "data": {
80
+ "type": "99", // 固定
81
+ "name": "WorkClaw 桌面端", // 固定
82
+ "serial_number": "WC-XXXXXXXXXXXXXXXX", // 主机名+MAC 哈希,稳定设备序列号
83
+ "product_model": "WorkClaw Desktop",
84
+ "main_processor": "<os.cpus() 型号>",
85
+ "memory": "<os.totalmem() 取整>GB",
86
+ "emmc": "<fs.statfs 系统磁盘总容量>",
87
+ "system": "<os.type() release() arch()>",
88
+ "app_key": "<当前用户 WORKCHAT_API_KEY>"
89
+ }
90
+ }
91
+ ```
92
+
93
+ 绑定确认不走 HTTP 轮询:用户扫码后平台往 WebSocket 推 `INIT_AGENT` 事件
94
+ (data 带 `id`/`agentId` 与 `futureId`/`userId`),插件 `completeWorkChatBinding`
95
+ 唤醒挂起的 `web.login.wait`。
96
+
97
+ ## 与 openclaw-weixin 的对齐清单
98
+
99
+ - **入站处理链**:斜杠命令拦截 → `resolveSenderCommandAuthorizationWithRuntime`
100
+ 配对鉴权(allowFrom 文件 + 文件锁)→ `resolveAgentRoute` →
101
+ `recordInboundSession` → `createReplyDispatcherWithTyping` →
102
+ `dispatchReplyFromConfig`
103
+ - **出站**:StreamingMarkdownFilter 过滤 → `message_sending` hook(可取消/改写)
104
+ → 发送 → `message_sent` hook;失败走 error-notice
105
+ - **channel 契约**:`messaging.targetResolver`、`agentPrompt.messageToolHints`
106
+ (cron 投递必须带 to+accountId)、`status` 三件套、`auth.login` CLI 入口、
107
+ `gatewayMethods` QR RPC
108
+ - **运维**:`/toggle-debug` 开启全链路耗时统计;JSON 行日志写入主 openclaw
109
+ 日志文件(与其他渠道同格式);`compat.ts` 宿主版本守卫
110
+ - **协议差异**(合理保留):WebSocket 推送代替长轮询(无 sync-buf 游标);
111
+ 二维码本地生成(非平台下发);绑定确认靠 WS 事件(非 HTTP 轮询);
112
+ 凭据为环境变量注入的 appKey/appSecret(非扫码 token);无 typing API
113
+
114
+ ## 凭据链路(与微信插件的关键差异)
115
+
116
+ 凭据不是扫码登录获得:桌面端登录后从
117
+ `/open-apis/desktop/oauth2/workchat/credentials` 换取 `workchatApiKey`/
118
+ `workchatAppSecret`,以 `WORKCHAT_API_KEY` / `WORKCHAT_APP_SECRET` /
119
+ `USER_ID` 环境变量注入网关进程;openclaw.json 使用 `${WORKCHAT_API_KEY}` /
120
+ `${WORKCHAT_APP_SECRET}` 占位符,密钥不落盘。
121
+
122
+ ## 连接协议
123
+
124
+ ```
125
+ POST {base}/authen/v1/access_token/internal app_key/app_secret -> accessToken
126
+ POST {base}/open-apis/v1/connections/open subscriptions -> endpoint + ticket
127
+ WS {endpoint}?ticket=... AGENT_MESSAGE/EVENT/SYSTEM
128
+ ```
129
+
130
+ - 开连接时的订阅声明:`[{type:"CALLBACK",topic:"AGENT_MESSAGE"},{type:"EVENT",topic:"*"},{type:"SYSTEM",topic:"*"}]`。
131
+ - `SYSTEM` + topic `ping` → 回 `pong`;topic `disconnect` → 关闭并重连。
132
+ - `EVENT` + `AGENT_CREATED|AGENT_UPDATED|AGENT_DELETED|INIT_AGENT` → 账号生命周期。
133
+ - `CALLBACK` + topic `AGENT_MESSAGE` → 用户消息。
134
+
135
+ ## WebSocket 帧协议详解(信封结构)
136
+
137
+ 每帧是一个 JSON 信封:`{ type, metadata: { time, event|topic, contentType }, data }`,
138
+ `data` 可能是对象,也可能是 JSON 字符串(解析前需二次 `JSON.parse`),甚至是裸标量
139
+ (如 INIT_AGENT 的 data 为字符串化的 agent id,需归一化为 `{id}`)。
140
+
141
+ | type | metadata 判定 | data 内容 | 插件处理 |
142
+ |------|--------------|-----------|----------|
143
+ | `SYSTEM` | topic=`ping` | 任意(回显) | 回 pong:`{code:200,message:"pong",metadata:{contentType:"application/json"},data:JSON.stringify(原data)}`;成功回包即刷新 `lastEventAt`(连通性判据) |
144
+ | `SYSTEM` | topic=`disconnect` | — | 主动关闭 WS,交给重连循环 |
145
+ | `EVENT` | event=`INIT_AGENT` | `{id, futureId}`(或裸 agentId) | 扫码绑定完成事件:持久化账号、绑定 agentId/userId、唤醒 web.login.wait;重绑时先清理旧 agent 账号 |
146
+ | `EVENT` | event=`AGENT_CREATED` | `{id, futureId, ...}` | 平台创建智能体:保存账号并回执状态(notifyAgentCreatedStatus) |
147
+ | `EVENT` | event=`AGENT_UPDATED` | agent 对象 | 无本地动作(记录日志) |
148
+ | `EVENT` | event=`AGENT_DELETED` | `{id, ...}` | 删除对应本地账号绑定(含状态文件/索引/allowFrom/openclaw.json accounts 条目)并触发渠道重载 |
149
+ | `CALLBACK` | topic=`AGENT_MESSAGE` | `{userId, conversationId, msgId, agentId, message: {content}}` | 用户消息:斜杠命令 → 配对鉴权 → 路由 → 记录会话 → 派发回复 |
150
+ | — | 其他/未知 | — | 忽略 |
151
+
152
+ zhixiaotu 参考实现(`zhixiaotu_channel/packages/plugin`)中还存在但 workchat 插件
153
+ 暂未接的 EVENT 类型:`TOOLS_LIST`、`SKILLS_LIST`、`SKILL_DO_REMIND`(skills 事件)、
154
+ `CRON_REMIND`(定时任务事件)——协议格式见
155
+ `zhixiaotu_channel/packages/plugin/src/gateway/message-context.ts`。
156
+
157
+ ### 连通性判据(诊断口径)
158
+
159
+ - **连接已建立**:WS `onopen` 成功后插件才上报 `running: true`(`monitor.ts`)。
160
+ - **连接保活**:服务端周期性下发 `SYSTEM/ping`(节奏约 1-2 分钟),插件回 pong
161
+ 成功后刷新 `lastEventAt`。因此「WS 活着」= `running === true && lastError 为空
162
+ && lastEventAt 距今 < 10 分钟`。
163
+ - 桌面端连通性测试(`work-claw-desktop/src/main/im/imGatewayManager.ts`
164
+ `testWorkchatOpenClawConnectivity`)通过网关 `channels.status` RPC 读取上述快照,
165
+ 按此口径给出 pass/fail。
166
+
167
+ ## 开发
168
+
169
+ ```bash
170
+ npm install # 安装 esbuild/typescript
171
+ npm run bundle # 打包 index.js
172
+ npm run sync # 同步到 ../work-claw-desktop(可传路径参数)
173
+ npm run typecheck
174
+ ```
175
+
176
+ ## 配置项(channels.openclaw-workchat)
177
+
178
+ | 配置项 | 类型 | 必填 | 说明 |
179
+ |--------|------|------|------|
180
+ | enabled | boolean | 否 | 是否启用通道 |
181
+ | baseUrl | string | 否 | 默认 `https://workbrain.cn/backend-api` |
182
+ | websocketUrl | string | 否 | 默认从 baseUrl 推导 |
183
+ | appKey | string | 是 | `${WORKCHAT_API_KEY}` 占位符 |
184
+ | appSecret | string | 是 | `${WORKCHAT_APP_SECRET}` 占位符 |
185
+ | accounts | object | 否 | 多账号配置,每项可覆盖 agentId/userId |
186
+
187
+ ## 断线重连
188
+
189
+ - 指数退避(5s 起步,上限 5 分钟),鉴权失败(errCode 2001 /
190
+ unauthorized / 缺凭据)停止重连。
191
+ - 限频(“访问过于频繁”)时以 1s 最小退避重试。
192
+
193
+ ## 测试
194
+
195
+ ```bash
196
+ npm test # vitest:accounts(索引/清理/prune 边界)+ inbound(帧解析/data 归一化)
197
+ ```
198
+
199
+ ## TODO(已知留白)
200
+
201
+ - **事件接入**:`TOOLS_LIST` / `SKILLS_LIST` / `SKILL_DO_REMIND` / `CRON_REMIND`
202
+ 暂不处理(归入 ignored,debug 日志记录 `ignoredKind`)。协议参考
203
+ `zhixiaotu_channel/packages/plugin/src/gateway/message-context.ts`,接入时按
204
+ zhixiaotu 的 dispatcher 补 handler。
205
+ - **账号文件写入加锁**:`handleAgentLifecycleEvent` 里保存/清理账号与
206
+ `triggerWorkChatChannelReload` 触发的重载存在并发读写 accounts.json 的窗口;
207
+ 可复用 `pairing.ts` 的 `withFileLock`。
208
+ - **serial 持久化**:`buildDeviceSerial` 每次扫码按主机名+MAC 现算,网卡变化
209
+ (VPN/虚拟网卡)会得到不同 serial,平台可能视为新设备导致重复绑定;
210
+ 建议首次生成后持久化到 state 目录。