@yanhaidao/wecom 2.3.260 → 2.3.270
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 +10 -5
- package/changelog/v2.3.27.md +33 -0
- package/index.test.ts +5 -1
- package/package.json +17 -17
- package/src/app/index.ts +6 -3
- package/src/capability/mcp/tool.ts +7 -3
- package/src/channel.meta.test.ts +4 -0
- package/src/channel.ts +29 -59
- package/src/onboarding.test.ts +42 -24
- package/src/onboarding.ts +598 -553
- package/src/outbound.ts +17 -11
- package/src/transport/bot-ws/media.test.ts +8 -8
- package/src/transport/bot-ws/media.ts +51 -2
- package/src/transport/bot-ws/sdk-adapter.ts +6 -6
package/README.md
CHANGED
|
@@ -163,6 +163,11 @@
|
|
|
163
163
|
> 项目保持高频迭代,全面对齐甚至超越企业真实业务诉求。
|
|
164
164
|
> **为保持精简,以下仅展示近期 5 次重要更新,完整历史版本(含全部 `v2.2.x`)请前往 [changelog/ 目录](./changelog/) 查阅。**
|
|
165
165
|
|
|
166
|
+
#### 📌 v2.3.27(2026-03-27)
|
|
167
|
+
- **[重要修复] `channel add` 重新支持 WeCom guided setup** 🧭 之前有些环境下,`wecom` 虽然已经安装,却仍会在 OpenClaw 里显示成 “does not support guided setup yet”,导致无法直接通过交互式向导添加。现在插件已经对齐 OpenClaw 当前的 `setupWizard` 接口,`openclaw channels add` 会重新正常识别和进入配置流程。
|
|
168
|
+
- **[重要修复] 修复 `installedCatalogById is not defined`** 🔧 部分用户在渠道添加或选择阶段会直接遇到 `ReferenceError: installedCatalogById is not defined`,表现上像是“选了渠道就报错”或“添加流程突然失效”。这一版已经修复对应的目录访问逻辑,添加流程恢复稳定。
|
|
169
|
+
- **[升级兼容] 清理 OpenClaw 新版下失效的 SDK 旧入口** 📦 这次同步迁移了 `wecom` 插件里几处已经不再建议继续从 `openclaw/plugin-sdk` 根入口直接拿的旧接口,重点覆盖工具上下文、outbound 适配器和 Bot WS 媒体发送链路,升级 OpenClaw 后更不容易再出现“有的地方能跑、有的地方直接炸”的兼容问题。
|
|
170
|
+
|
|
166
171
|
#### 📌 v2.3.26(2026-03-26)
|
|
167
172
|
- **[重要修复] 升级 OpenClaw 后不再乱报错** 🔧 修复了新版 OpenClaw 下 `wecom` 插件容易出现的 `is not a function` 一类启动/运行错误。
|
|
168
173
|
- **[回复更稳] Agent 和 Bot WS 不再乱串** ↔️ 现在是谁收到消息,就尽量由谁来回复,不再容易出现“在 Agent 里说话,结果 Bot WS 回你”的情况。
|
|
@@ -182,10 +187,6 @@
|
|
|
182
187
|
#### 📌 v2.3.16(2026-03-16)
|
|
183
188
|
- **[解析增强] 混合消息媒体正确接管** 🛠 重点修复在 `Bot WS` 通道下,用户如果发了“一张截图 + 一段文字指示”,以前容易丢掉截图或者 AI 只能看到无法查看的腾讯云临时链接。新版底层引擎将自动扫过所有的媒体节点摘取 URL 与解密 AES Key,还大模型一双慧眼。
|
|
184
189
|
|
|
185
|
-
#### 📌 v2.3.15(2026-03-14)
|
|
186
|
-
- **[原生资产稳定写入]** 📄 深度强化大模型创建企微相关原生存根文档/表格时的写入稳定性。执行 `init_content` 有了更强的前置图片上传清洗能力;极大程度杜绝了混发段落/文本/图片触发的企微官方 `Validator` 异常。
|
|
187
|
-
- **[复杂分发目标解包]** 💬 补齐所有关于企业微信“群聊、部门、标签组”作为 `To` 目标的精确指令解析算法,保障向全公司的组织架构层级呼气 AI 报告不再报出 `81013 target invalid`。
|
|
188
|
-
|
|
189
190
|
*(查看更早期关于“超时熔断代投、动态扩容矩阵”等功能的更新日志,请移步 [changelog/ 目录](./changelog/))*
|
|
190
191
|
|
|
191
192
|
---
|
|
@@ -204,7 +205,7 @@ openclaw plugins enable wecom
|
|
|
204
205
|
|
|
205
206
|
### 1.2 互动向导式初配 (适合个人开发者与极客)
|
|
206
207
|
|
|
207
|
-
如果您不想手写繁杂的 JSON 配置文件,可以通过交互式向导快速完成最轻量的 WebSocket
|
|
208
|
+
如果您不想手写繁杂的 JSON 配置文件,可以通过交互式向导快速完成最轻量的 WebSocket 长连接部署。`v2.3.27` 起,`wecom` 已重新对齐 OpenClaw 当前的 guided setup 流程,`openclaw channels add` 可以直接识别并进入配置:
|
|
208
209
|
|
|
209
210
|
1. 确保已启用本插件。
|
|
210
211
|
2. 在终端运行添加渠道指令:
|
|
@@ -214,6 +215,10 @@ openclaw plugins enable wecom
|
|
|
214
215
|
3. 选择下拉列表中第一顺位的:**企业微信 (WeCom)**
|
|
215
216
|
4. 根据终端亮色指引,填入企微机器人对应的 `Bot ID` 及 `Secret`,机器人即可完成握手并进入可用状态。
|
|
216
217
|
|
|
218
|
+
> **如果您最近刚升级 OpenClaw:**
|
|
219
|
+
> - 若之前在添加渠道时看到 `wecom does not support guided setup yet`,请更新到当前版本后重试。
|
|
220
|
+
> - 若之前在渠道添加阶段见过 `ReferenceError: installedCatalogById is not defined`,这一版也已一并修复。
|
|
221
|
+
|
|
217
222
|
### 1.3 生产环境顶配架构示范(Bot WS 流式交互 + Agent 私有通道兜底发送)
|
|
218
223
|
|
|
219
224
|
如果您的目标不是“接进来能聊两句”,而是让团队在企业微信里长期稳定使用 AI,这套组合更接近生产环境的推荐形态:
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# OpenClaw WeCom 插件 v2.3.27 变更简报
|
|
2
|
+
|
|
3
|
+
> [!TIP]
|
|
4
|
+
> `v2.3.27` 是一个以“升级兼容”和“接入恢复”为主的修复版本。重点解决三类真实会挡住使用的问题:第一,`channel add` 里 `wecom` 又能正常被识别和添加了;第二,修复了部分用户在配置或选择渠道时遇到的 `ReferenceError: installedCatalogById is not defined`;第三,把这轮 OpenClaw 升级后 `wecom` 插件里几处已经失效的 SDK 旧入口统一迁到了当前可用的公共接口,避免出现“有的地方能跑、有的地方一升级就炸”的割裂状态。
|
|
5
|
+
|
|
6
|
+
## 2026-03-27(v2.3.27)
|
|
7
|
+
- 【恢复 `channel add` 接入】**[重要修复]** 之前在 OpenClaw 的渠道添加流程里,`wecom` 虽然已经安装,但仍可能被显示成 “does not support guided setup yet”,导致用户无法直接从 `channel add` 走完接入。现在插件已经切到 OpenClaw 当前识别的 `setupWizard` 接口,`wecom` 会重新出现在正常的 guided setup 流程中。
|
|
8
|
+
- 【修复 `installedCatalogById is not defined`】**[重要修复]** 某些用户在渠道选择或安装判断阶段会直接撞到 `ReferenceError: installedCatalogById is not defined`,表现上像是“选了渠道就报错”或“添加流程突然失效”。这次已经把对应的渠道目录访问逻辑改稳,不再依赖容易失效的解构引用。
|
|
9
|
+
- 【迁移失效的插件 SDK 入口】OpenClaw 升级后,`wecom` 插件里原来直接从 `openclaw/plugin-sdk` 根入口拿的一些类型和能力,已经不再按旧方式导出。`v2.3.27` 已经把这几处高风险点迁到当前公开可用的子路径,重点覆盖了工具上下文、outbound 适配器和媒体发送相关接口。
|
|
10
|
+
- 【修复 `wecom_mcp` 工具返回协议】随着 OpenClaw 新版工具结果协议收紧,`wecom_mcp` 这类插件工具如果还按旧格式返回,就会在注册或执行阶段出现类型或运行时不匹配。现在该工具返回结果已经补齐新版要求的 `details` 字段,和当前工具调用协议保持一致。
|
|
11
|
+
- 【修复 Bot WS 媒体发送对旧导出的依赖】之前 Bot WebSocket 媒体发送链路依赖了一个在当前发布版 OpenClaw 中已经不可直接使用的媒体加载导出,升级后容易出现“代码看起来没问题,但媒体链路单独报错”的情况。现在媒体读取改为基于当前仍然公开可用的远程抓取、本地路径校验和 MIME 识别能力自行组装,兼容当前版本的 OpenClaw 发布面。
|
|
12
|
+
- 【补齐严格类型下的 WS 回复头处理】随着依赖和 SDK 类型变严格,Bot WS 主动回复和命令回复链路里对 `req_id` 头部的处理更容易暴露问题。这一版顺手把回复头的兜底生成与返回值收窄一起补齐,减少升级后只在某些路径上报类型错误或运行时异常的概率。
|
|
13
|
+
|
|
14
|
+
## 升级后你会直接感受到
|
|
15
|
+
|
|
16
|
+
- 在 OpenClaw 里重新执行 `channel add` 时,`wecom` 不会再被误判成“不支持 guided setup”。
|
|
17
|
+
- 遇到 `ReferenceError: installedCatalogById is not defined` 的用户,渠道添加和选择流程会恢复正常。
|
|
18
|
+
- OpenClaw 升级后,`wecom` 插件不再那么依赖旧版 SDK 的根入口导出,单独安装或混合运行时更稳。
|
|
19
|
+
- Bot WS 的媒体发送链路不再因为旧导出失效而单独掉链子。
|
|
20
|
+
|
|
21
|
+
## 这次版本背后的最小理解模型
|
|
22
|
+
|
|
23
|
+
把这次更新理解成三句话就够了:
|
|
24
|
+
|
|
25
|
+
1. OpenClaw 的渠道接入面已经变了,`wecom` 不能再继续挂在旧的 onboarding 接口上。
|
|
26
|
+
2. 插件如果直接依赖 `openclaw/plugin-sdk` 根入口里那些历史上“顺手可用”的符号,升级后迟早会踩到导出面收缩的问题。
|
|
27
|
+
3. 真正稳定的兼容修复,不只是把一个 import 改过去,而是把接入流程、工具返回协议、outbound 类型和媒体链路一起对齐到当前公开接口。
|
|
28
|
+
|
|
29
|
+
## 升级提示
|
|
30
|
+
|
|
31
|
+
- 执行 `openclaw plugins update wecom` 即可升级到 `v2.3.27`。
|
|
32
|
+
- 如果你刚升级 OpenClaw 后发现 `wecom` 在 `channel add` 里突然不能配置,或者用户侧开始报 `installedCatalogById is not defined`,这一版就是对应修复。
|
|
33
|
+
- 如果你是“全局安装的 OpenClaw + 本地 `wecom` 源码插件”混合运行,升级后建议完整重启一次 gateway,确保新的 setup 和 outbound 逻辑都已生效。
|
package/index.test.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
1
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
3
|
import plugin from "./index.js";
|
|
4
4
|
|
|
5
5
|
describe("wecom plugin register", () => {
|
|
6
6
|
it("registers both recommended and legacy webhook route prefixes", () => {
|
|
7
7
|
const registerChannel = vi.fn();
|
|
8
8
|
const registerHttpRoute = vi.fn();
|
|
9
|
+
const registerTool = vi.fn();
|
|
10
|
+
const on = vi.fn();
|
|
9
11
|
const api = {
|
|
10
12
|
runtime: {},
|
|
11
13
|
registerChannel,
|
|
12
14
|
registerHttpRoute,
|
|
15
|
+
registerTool,
|
|
16
|
+
on,
|
|
13
17
|
} as unknown as OpenClawPluginApi;
|
|
14
18
|
|
|
15
19
|
plugin.register(api);
|
package/package.json
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yanhaidao/wecom",
|
|
3
|
-
"version": "2.3.
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "2.3.270",
|
|
5
4
|
"description": "OpenClaw 企业微信(WeCom)插件,默认 Bot WebSocket,支持加密媒体解密、Agent 主动发消息与多账号接入",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "YanHaidao (VX: YanHaidao)",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/YanHaidao/wecom.git"
|
|
9
10
|
},
|
|
11
|
+
"type": "module",
|
|
10
12
|
"publishConfig": {
|
|
11
13
|
"access": "public"
|
|
12
14
|
},
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@wecom/aibot-node-sdk": "^1.0.0",
|
|
17
|
+
"fast-xml-parser": "5.3.4",
|
|
18
|
+
"undici": "^7.20.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^25.2.0",
|
|
22
|
+
"typescript": "^5.9.3",
|
|
23
|
+
"vitest": "^2.1.8"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"openclaw": "^2026.3.23-2"
|
|
27
|
+
},
|
|
15
28
|
"openclaw": {
|
|
16
29
|
"extensions": [
|
|
17
30
|
"./index.ts"
|
|
@@ -38,18 +51,5 @@
|
|
|
38
51
|
"localPath": "extensions/wecom",
|
|
39
52
|
"defaultChoice": "npm"
|
|
40
53
|
}
|
|
41
|
-
},
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"@wecom/aibot-node-sdk": "^1.0.0",
|
|
44
|
-
"fast-xml-parser": "5.3.4",
|
|
45
|
-
"undici": "^7.20.0"
|
|
46
|
-
},
|
|
47
|
-
"peerDependencies": {
|
|
48
|
-
"openclaw": "^2026.3.23-2"
|
|
49
|
-
},
|
|
50
|
-
"devDependencies": {
|
|
51
|
-
"@types/node": "^25.2.0",
|
|
52
|
-
"typescript": "^5.9.3",
|
|
53
|
-
"vitest": "^2.1.8"
|
|
54
54
|
}
|
|
55
55
|
}
|
package/src/app/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
2
|
import { clearWecomSourceAccount } from "../runtime/source-registry.js";
|
|
3
|
-
import { WecomAccountRuntime } from "./account-runtime.js";
|
|
4
3
|
import type { ReplyHandle } from "../types/index.js";
|
|
4
|
+
import { WecomAccountRuntime } from "./account-runtime.js";
|
|
5
5
|
|
|
6
6
|
let runtime: PluginRuntime | null = null;
|
|
7
7
|
const runtimes = new Map<string, WecomAccountRuntime>();
|
|
@@ -15,7 +15,7 @@ export type BotWsPushHandle = {
|
|
|
15
15
|
replyCommand: (params: {
|
|
16
16
|
cmd: string;
|
|
17
17
|
body?: Record<string, unknown>;
|
|
18
|
-
headers?: Record<string, string
|
|
18
|
+
headers?: ({ req_id?: string } & Record<string, string>) | undefined;
|
|
19
19
|
}) => Promise<Record<string, unknown>>;
|
|
20
20
|
sendMedia: (params: {
|
|
21
21
|
chatId: string;
|
|
@@ -100,7 +100,10 @@ export function registerActiveBotWsReplyHandle(params: {
|
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
102
|
if (sessionKey) {
|
|
103
|
-
activeBotWsReplyHandlesBySession.set(
|
|
103
|
+
activeBotWsReplyHandlesBySession.set(
|
|
104
|
+
buildSessionHandleKey(accountId, sessionKey),
|
|
105
|
+
params.handle,
|
|
106
|
+
);
|
|
104
107
|
}
|
|
105
108
|
if ((params.peerKind === "direct" || params.peerKind === "group") && peerId) {
|
|
106
109
|
activeBotWsReplyHandlesByPeer.set(
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
OpenClawPluginToolContext,
|
|
3
|
+
OpenClawPluginToolFactory,
|
|
4
|
+
} from "openclaw/plugin-sdk/core";
|
|
2
5
|
import { resolveWecomSourceSnapshot } from "../../runtime/source-registry.js";
|
|
3
6
|
import { cleanSchemaForGemini } from "./schema.js";
|
|
4
7
|
import { clearWecomMcpCategoryCache, sendJsonRpc, type McpToolInfo } from "./transport.js";
|
|
@@ -12,9 +15,10 @@ type WecomMcpParams = {
|
|
|
12
15
|
|
|
13
16
|
const BIZ_CACHE_CLEAR_ERROR_CODES = new Set([850002]);
|
|
14
17
|
|
|
15
|
-
function textResult(data:
|
|
18
|
+
function textResult<TDetails>(data: TDetails) {
|
|
16
19
|
return {
|
|
17
20
|
content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }],
|
|
21
|
+
details: data,
|
|
18
22
|
};
|
|
19
23
|
}
|
|
20
24
|
|
|
@@ -96,7 +100,7 @@ async function handleCall(
|
|
|
96
100
|
return result;
|
|
97
101
|
}
|
|
98
102
|
|
|
99
|
-
export function createWeComMcpToolFactory() {
|
|
103
|
+
export function createWeComMcpToolFactory(): OpenClawPluginToolFactory {
|
|
100
104
|
return (toolContext: OpenClawPluginToolContext) => {
|
|
101
105
|
if (toolContext.messageChannel !== "wecom") {
|
|
102
106
|
return null;
|
package/src/channel.meta.test.ts
CHANGED
|
@@ -9,4 +9,8 @@ describe("wecomPlugin meta", () => {
|
|
|
9
9
|
expect(wecomPlugin.meta.docsLabel).toBe("企业微信");
|
|
10
10
|
expect(wecomPlugin.meta.selectionDocsPrefix).toBe("文档:");
|
|
11
11
|
});
|
|
12
|
+
|
|
13
|
+
it("exposes a setupWizard for guided setup discovery", () => {
|
|
14
|
+
expect(wecomPlugin.setupWizard?.channel).toBe("wecom");
|
|
15
|
+
});
|
|
12
16
|
});
|
package/src/channel.ts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ChannelAccountSnapshot,
|
|
3
|
-
ChannelPlugin,
|
|
4
|
-
OpenClawConfig,
|
|
5
|
-
} from "openclaw/plugin-sdk";
|
|
1
|
+
import type { ChannelAccountSnapshot, ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk";
|
|
6
2
|
import {
|
|
7
3
|
deleteAccountFromConfigSection,
|
|
8
4
|
setAccountEnabledInConfigSection,
|
|
9
5
|
} from "openclaw/plugin-sdk/core";
|
|
10
|
-
|
|
11
6
|
import {
|
|
12
7
|
DEFAULT_ACCOUNT_ID,
|
|
13
8
|
listWecomAccountIds,
|
|
@@ -16,10 +11,10 @@ import {
|
|
|
16
11
|
resolveWecomAccount,
|
|
17
12
|
resolveWecomAccountConflict,
|
|
18
13
|
} from "./config/index.js";
|
|
19
|
-
import type { ResolvedWecomAccount } from "./types/index.js";
|
|
20
14
|
import { monitorWecomProvider } from "./gateway-monitor.js";
|
|
21
|
-
import {
|
|
15
|
+
import { wecomSetupWizard } from "./onboarding.js";
|
|
22
16
|
import { wecomOutbound } from "./outbound.js";
|
|
17
|
+
import type { ResolvedWecomAccount } from "./types/index.js";
|
|
23
18
|
|
|
24
19
|
const meta = {
|
|
25
20
|
id: "wecom",
|
|
@@ -27,22 +22,16 @@ const meta = {
|
|
|
27
22
|
selectionLabel: "WeCom (企业微信)",
|
|
28
23
|
docsPath: "/channels/wecom",
|
|
29
24
|
docsLabel: "企业微信",
|
|
30
|
-
blurb:
|
|
31
|
-
"企业微信官方推荐三方插件,默认 Bot WS 配置简单,支持主动发消息与 Agent 全能力。",
|
|
25
|
+
blurb: "企业微信官方推荐三方插件,默认 Bot WS 配置简单,支持主动发消息与 Agent 全能力。",
|
|
32
26
|
selectionDocsPrefix: "文档:",
|
|
33
27
|
aliases: ["wechatwork", "wework", "qywx", "企微", "企业微信"],
|
|
34
28
|
order: 85,
|
|
35
29
|
quickstartAllowFrom: true,
|
|
36
30
|
};
|
|
37
31
|
|
|
38
|
-
function resolveAccountInboundPath(
|
|
39
|
-
account: ResolvedWecomAccount,
|
|
40
|
-
): string | undefined {
|
|
32
|
+
function resolveAccountInboundPath(account: ResolvedWecomAccount): string | undefined {
|
|
41
33
|
const derivedPaths = resolveDerivedPathSummary(account.accountId);
|
|
42
|
-
if (
|
|
43
|
-
account.bot?.primaryTransport === "webhook" &&
|
|
44
|
-
account.bot.webhookConfigured
|
|
45
|
-
) {
|
|
34
|
+
if (account.bot?.primaryTransport === "webhook" && account.bot.webhookConfigured) {
|
|
46
35
|
return derivedPaths.botWebhook[0];
|
|
47
36
|
}
|
|
48
37
|
if (account.agent?.callbackConfigured) {
|
|
@@ -57,15 +46,13 @@ function normalizeWecomMessagingTarget(raw: string): string | undefined {
|
|
|
57
46
|
if (/^wecom-agent:/i.test(trimmed)) {
|
|
58
47
|
return trimmed;
|
|
59
48
|
}
|
|
60
|
-
return (
|
|
61
|
-
trimmed.replace(/^(wecom|wechatwork|wework|qywx):/i, "").trim() || undefined
|
|
62
|
-
);
|
|
49
|
+
return trimmed.replace(/^(wecom|wechatwork|wework|qywx):/i, "").trim() || undefined;
|
|
63
50
|
}
|
|
64
51
|
|
|
65
52
|
export const wecomPlugin: ChannelPlugin<ResolvedWecomAccount> = {
|
|
66
53
|
id: "wecom",
|
|
67
54
|
meta,
|
|
68
|
-
|
|
55
|
+
setupWizard: wecomSetupWizard,
|
|
69
56
|
capabilities: {
|
|
70
57
|
chatTypes: ["direct", "group"],
|
|
71
58
|
media: true,
|
|
@@ -88,8 +75,7 @@ export const wecomPlugin: ChannelPlugin<ResolvedWecomAccount> = {
|
|
|
88
75
|
listAccountIds: (cfg) => listWecomAccountIds(cfg as OpenClawConfig),
|
|
89
76
|
resolveAccount: (cfg, accountId) =>
|
|
90
77
|
resolveWecomAccount({ cfg: cfg as OpenClawConfig, accountId }),
|
|
91
|
-
defaultAccountId: (cfg) =>
|
|
92
|
-
resolveDefaultWecomAccountId(cfg as OpenClawConfig),
|
|
78
|
+
defaultAccountId: (cfg) => resolveDefaultWecomAccountId(cfg as OpenClawConfig),
|
|
93
79
|
setAccountEnabled: ({ cfg, accountId, enabled }) =>
|
|
94
80
|
setAccountEnabledInConfigSection({
|
|
95
81
|
cfg: cfg as OpenClawConfig,
|
|
@@ -139,9 +125,7 @@ export const wecomPlugin: ChannelPlugin<ResolvedWecomAccount> = {
|
|
|
139
125
|
});
|
|
140
126
|
// 与其他渠道保持一致:直接返回 allowFrom,空则允许所有人
|
|
141
127
|
const allowFrom =
|
|
142
|
-
account.agent?.config.dm?.allowFrom ??
|
|
143
|
-
account.bot?.config.dm?.allowFrom ??
|
|
144
|
-
[];
|
|
128
|
+
account.agent?.config.dm?.allowFrom ?? account.bot?.config.dm?.allowFrom ?? [];
|
|
145
129
|
return allowFrom.map((entry) => String(entry));
|
|
146
130
|
},
|
|
147
131
|
formatAllowFrom: ({ allowFrom }) =>
|
|
@@ -183,31 +167,24 @@ export const wecomPlugin: ChannelPlugin<ResolvedWecomAccount> = {
|
|
|
183
167
|
transport: (snapshot as { transport?: string }).transport ?? null,
|
|
184
168
|
ownerId: (snapshot as { ownerId?: string }).ownerId ?? null,
|
|
185
169
|
health: (snapshot as { health?: string }).health ?? "idle",
|
|
186
|
-
ownerDriftAt:
|
|
187
|
-
(snapshot as { ownerDriftAt?: number | null }).ownerDriftAt ?? null,
|
|
170
|
+
ownerDriftAt: (snapshot as { ownerDriftAt?: number | null }).ownerDriftAt ?? null,
|
|
188
171
|
connected: (snapshot as { connected?: boolean }).connected,
|
|
189
172
|
authenticated: (snapshot as { authenticated?: boolean }).authenticated,
|
|
190
173
|
lastStartAt: snapshot.lastStartAt ?? null,
|
|
191
174
|
lastStopAt: snapshot.lastStopAt ?? null,
|
|
192
175
|
lastError: snapshot.lastError ?? null,
|
|
193
|
-
lastErrorAt:
|
|
194
|
-
(snapshot as { lastErrorAt?: number | null }).lastErrorAt ?? null,
|
|
176
|
+
lastErrorAt: (snapshot as { lastErrorAt?: number | null }).lastErrorAt ?? null,
|
|
195
177
|
lastInboundAt: snapshot.lastInboundAt ?? null,
|
|
196
178
|
lastOutboundAt: snapshot.lastOutboundAt ?? null,
|
|
197
179
|
recentInboundSummary:
|
|
198
|
-
(snapshot as { recentInboundSummary?: string | null })
|
|
199
|
-
.recentInboundSummary ?? null,
|
|
180
|
+
(snapshot as { recentInboundSummary?: string | null }).recentInboundSummary ?? null,
|
|
200
181
|
recentOutboundSummary:
|
|
201
|
-
(snapshot as { recentOutboundSummary?: string | null })
|
|
202
|
-
.recentOutboundSummary ?? null,
|
|
182
|
+
(snapshot as { recentOutboundSummary?: string | null }).recentOutboundSummary ?? null,
|
|
203
183
|
recentIssueCategory:
|
|
204
|
-
(snapshot as { recentIssueCategory?: string | null })
|
|
205
|
-
.recentIssueCategory ?? null,
|
|
184
|
+
(snapshot as { recentIssueCategory?: string | null }).recentIssueCategory ?? null,
|
|
206
185
|
recentIssueSummary:
|
|
207
|
-
(snapshot as { recentIssueSummary?: string | null })
|
|
208
|
-
|
|
209
|
-
transportSessions:
|
|
210
|
-
(snapshot as { transportSessions?: string[] }).transportSessions ?? [],
|
|
186
|
+
(snapshot as { recentIssueSummary?: string | null }).recentIssueSummary ?? null,
|
|
187
|
+
transportSessions: (snapshot as { transportSessions?: string[] }).transportSessions ?? [],
|
|
211
188
|
probe: snapshot.probe,
|
|
212
189
|
lastProbeAt: snapshot.lastProbeAt ?? null,
|
|
213
190
|
}),
|
|
@@ -224,42 +201,35 @@ export const wecomPlugin: ChannelPlugin<ResolvedWecomAccount> = {
|
|
|
224
201
|
configured: account.configured && !conflict,
|
|
225
202
|
webhookPath: resolveAccountInboundPath(account),
|
|
226
203
|
primaryTransport:
|
|
227
|
-
account.bot?.primaryTransport ??
|
|
228
|
-
|
|
229
|
-
transport:
|
|
230
|
-
(runtime as { transport?: string } | undefined)?.transport ?? null,
|
|
204
|
+
account.bot?.primaryTransport ?? (account.agent ? "agent-callback" : null),
|
|
205
|
+
transport: (runtime as { transport?: string } | undefined)?.transport ?? null,
|
|
231
206
|
ownerId: (runtime as { ownerId?: string } | undefined)?.ownerId ?? null,
|
|
232
207
|
health: (runtime as { health?: string } | undefined)?.health ?? "idle",
|
|
233
208
|
ownerDriftAt:
|
|
234
|
-
(runtime as { ownerDriftAt?: number | null } | undefined)
|
|
235
|
-
?.ownerDriftAt ?? null,
|
|
209
|
+
(runtime as { ownerDriftAt?: number | null } | undefined)?.ownerDriftAt ?? null,
|
|
236
210
|
connected: (runtime as { connected?: boolean } | undefined)?.connected,
|
|
237
|
-
authenticated: (runtime as { authenticated?: boolean } | undefined)
|
|
238
|
-
?.authenticated,
|
|
211
|
+
authenticated: (runtime as { authenticated?: boolean } | undefined)?.authenticated,
|
|
239
212
|
running: runtime?.running ?? false,
|
|
240
213
|
lastStartAt: runtime?.lastStartAt ?? null,
|
|
241
214
|
lastStopAt: runtime?.lastStopAt ?? null,
|
|
242
215
|
lastError: runtime?.lastError ?? conflict?.message ?? null,
|
|
243
|
-
lastErrorAt:
|
|
244
|
-
(runtime as { lastErrorAt?: number | null } | undefined)
|
|
245
|
-
?.lastErrorAt ?? null,
|
|
216
|
+
lastErrorAt: (runtime as { lastErrorAt?: number | null } | undefined)?.lastErrorAt ?? null,
|
|
246
217
|
lastInboundAt: runtime?.lastInboundAt ?? null,
|
|
247
218
|
lastOutboundAt: runtime?.lastOutboundAt ?? null,
|
|
248
219
|
recentInboundSummary:
|
|
249
|
-
(runtime as { recentInboundSummary?: string | null } | undefined)
|
|
250
|
-
|
|
220
|
+
(runtime as { recentInboundSummary?: string | null } | undefined)?.recentInboundSummary ??
|
|
221
|
+
null,
|
|
251
222
|
recentOutboundSummary:
|
|
252
223
|
(runtime as { recentOutboundSummary?: string | null } | undefined)
|
|
253
224
|
?.recentOutboundSummary ?? null,
|
|
254
225
|
recentIssueCategory:
|
|
255
|
-
(runtime as { recentIssueCategory?: string | null } | undefined)
|
|
256
|
-
|
|
226
|
+
(runtime as { recentIssueCategory?: string | null } | undefined)?.recentIssueCategory ??
|
|
227
|
+
null,
|
|
257
228
|
recentIssueSummary:
|
|
258
|
-
(runtime as { recentIssueSummary?: string | null } | undefined)
|
|
259
|
-
|
|
229
|
+
(runtime as { recentIssueSummary?: string | null } | undefined)?.recentIssueSummary ??
|
|
230
|
+
null,
|
|
260
231
|
transportSessions:
|
|
261
|
-
(runtime as { transportSessions?: string[] } | undefined)
|
|
262
|
-
?.transportSessions ?? [],
|
|
232
|
+
(runtime as { transportSessions?: string[] } | undefined)?.transportSessions ?? [],
|
|
263
233
|
dmPolicy: account.bot?.config.dm?.policy ?? "pairing",
|
|
264
234
|
};
|
|
265
235
|
},
|
package/src/onboarding.test.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { OpenClawConfig, WizardPrompter } from "openclaw/plugin-sdk";
|
|
2
2
|
import type { RuntimeEnv } from "openclaw/plugin-sdk";
|
|
3
3
|
import { describe, expect, it, vi } from "vitest";
|
|
4
|
-
import {
|
|
4
|
+
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
|
|
5
|
+
import { wecomPlugin } from "./channel.js";
|
|
6
|
+
|
|
7
|
+
const wecomSetupAdapter = buildChannelSetupWizardAdapterFromSetupWizard({
|
|
8
|
+
plugin: wecomPlugin,
|
|
9
|
+
wizard: wecomPlugin.setupWizard!,
|
|
10
|
+
});
|
|
5
11
|
|
|
6
12
|
function createPrompter(overrides: Partial<WizardPrompter>): WizardPrompter {
|
|
7
13
|
return {
|
|
@@ -54,7 +60,7 @@ describe("wecom onboarding", () => {
|
|
|
54
60
|
}) as WizardPrompter["text"],
|
|
55
61
|
});
|
|
56
62
|
|
|
57
|
-
const result = await
|
|
63
|
+
const result = await wecomSetupAdapter.configure({
|
|
58
64
|
cfg: {} as OpenClawConfig,
|
|
59
65
|
runtime: createRuntime(),
|
|
60
66
|
prompter,
|
|
@@ -76,8 +82,12 @@ describe("wecom onboarding", () => {
|
|
|
76
82
|
const noteText = (prompter.note as ReturnType<typeof vi.fn>).mock.calls
|
|
77
83
|
.map(([message]) => String(message))
|
|
78
84
|
.join("\n");
|
|
79
|
-
expect(noteText).toContain(
|
|
80
|
-
|
|
85
|
+
expect(noteText).toContain(
|
|
86
|
+
"YanHaidao/wecom 是企业微信官方推荐三方插件,功能强大,适合直接落生产环境。",
|
|
87
|
+
);
|
|
88
|
+
expect(noteText).toContain(
|
|
89
|
+
"默认就是 Bot WebSocket 模式,配置简单,无需域名,普通用户也能快速接入。",
|
|
90
|
+
);
|
|
81
91
|
expect(noteText).toContain("支持主动发消息,定时任务、异常提醒、工作流通知都可直接落地。");
|
|
82
92
|
});
|
|
83
93
|
|
|
@@ -131,7 +141,7 @@ describe("wecom onboarding", () => {
|
|
|
131
141
|
},
|
|
132
142
|
};
|
|
133
143
|
|
|
134
|
-
const result = await
|
|
144
|
+
const result = await wecomSetupAdapter.configure({
|
|
135
145
|
cfg: initialCfg,
|
|
136
146
|
runtime: createRuntime(),
|
|
137
147
|
prompter,
|
|
@@ -155,7 +165,7 @@ describe("wecom onboarding", () => {
|
|
|
155
165
|
});
|
|
156
166
|
|
|
157
167
|
it("reports chinese status copy for channel selection", async () => {
|
|
158
|
-
const status = await
|
|
168
|
+
const status = await wecomSetupAdapter.getStatus({
|
|
159
169
|
cfg: {} as OpenClawConfig,
|
|
160
170
|
options: {},
|
|
161
171
|
accountOverrides: {},
|
|
@@ -199,7 +209,7 @@ describe("wecom onboarding", () => {
|
|
|
199
209
|
}) as WizardPrompter["text"],
|
|
200
210
|
});
|
|
201
211
|
|
|
202
|
-
const result = await
|
|
212
|
+
const result = await wecomSetupAdapter.configure({
|
|
203
213
|
cfg: {} as OpenClawConfig,
|
|
204
214
|
runtime: createRuntime(),
|
|
205
215
|
prompter,
|
|
@@ -214,25 +224,33 @@ describe("wecom onboarding", () => {
|
|
|
214
224
|
.map(([message]) => String(message))
|
|
215
225
|
.join("\n");
|
|
216
226
|
expect(noteText).toContain("接入标识已规范化为:haidao");
|
|
217
|
-
expect(
|
|
227
|
+
expect(wecomSetupAdapter.dmPolicy).toBeUndefined();
|
|
218
228
|
});
|
|
219
229
|
|
|
220
230
|
it("offers default account selection when config has no accounts", async () => {
|
|
221
231
|
const prompter = createPrompter({
|
|
222
|
-
select: vi.fn(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
select: vi.fn(
|
|
233
|
+
async ({
|
|
234
|
+
message,
|
|
235
|
+
options,
|
|
236
|
+
}: {
|
|
237
|
+
message: string;
|
|
238
|
+
options: Array<{ value: string; label: string }>;
|
|
239
|
+
}) => {
|
|
240
|
+
if (message === "请选择企业微信接入标识(英文):") {
|
|
241
|
+
expect(options.map((option) => option.value)).toEqual(["default", "__new__"]);
|
|
242
|
+
expect(options[0]?.label).toBe("default(默认标识)");
|
|
243
|
+
return "default";
|
|
244
|
+
}
|
|
245
|
+
if (message === "请选择您要配置的接入模式:") {
|
|
246
|
+
return "bot";
|
|
247
|
+
}
|
|
248
|
+
if (message === "请选择私聊 (DM) 访问策略:") {
|
|
249
|
+
return "pairing";
|
|
250
|
+
}
|
|
251
|
+
throw new Error(`Unexpected select prompt: ${message}`);
|
|
252
|
+
},
|
|
253
|
+
) as WizardPrompter["select"],
|
|
236
254
|
text: vi.fn(async ({ message }: { message: string }) => {
|
|
237
255
|
if (message === "请输入 BotId(机器人 ID):") {
|
|
238
256
|
return "bot-id-default";
|
|
@@ -250,7 +268,7 @@ describe("wecom onboarding", () => {
|
|
|
250
268
|
}) as WizardPrompter["text"],
|
|
251
269
|
});
|
|
252
270
|
|
|
253
|
-
const result = await
|
|
271
|
+
const result = await wecomSetupAdapter.configure({
|
|
254
272
|
cfg: {} as OpenClawConfig,
|
|
255
273
|
runtime: createRuntime(),
|
|
256
274
|
prompter,
|
|
@@ -301,7 +319,7 @@ describe("wecom onboarding", () => {
|
|
|
301
319
|
}) as WizardPrompter["text"],
|
|
302
320
|
});
|
|
303
321
|
|
|
304
|
-
const result = await
|
|
322
|
+
const result = await wecomSetupAdapter.configure({
|
|
305
323
|
cfg: {} as OpenClawConfig,
|
|
306
324
|
runtime: createRuntime(),
|
|
307
325
|
prompter,
|