@yanhaidao/wecom 2.4.160 → 2.5.110

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.
Files changed (313) hide show
  1. package/dist/index.js +68 -0
  2. package/dist/src/accounts.js +20 -0
  3. package/dist/src/agent/handler.js +895 -0
  4. package/dist/src/agent/index.js +5 -0
  5. package/dist/src/app/account-runtime.js +216 -0
  6. package/dist/src/app/bootstrap.js +19 -0
  7. package/dist/src/app/index.js +118 -0
  8. package/dist/src/capability/agent/delivery-service.js +63 -0
  9. package/dist/src/capability/agent/fallback-policy.js +6 -0
  10. package/dist/src/capability/agent/ingress-service.js +33 -0
  11. package/dist/src/capability/agent/upstream-delivery-service.js +71 -0
  12. package/dist/src/capability/bot/dispatch-config.js +45 -0
  13. package/dist/src/capability/bot/fallback-delivery.js +147 -0
  14. package/dist/src/capability/bot/local-path-delivery.js +178 -0
  15. package/dist/src/capability/bot/sandbox-media.js +138 -0
  16. package/dist/src/capability/bot/service.js +49 -0
  17. package/dist/src/capability/bot/stream-delivery.js +321 -0
  18. package/dist/src/capability/bot/stream-finalizer.js +81 -0
  19. package/dist/src/capability/bot/stream-orchestrator.js +318 -0
  20. package/dist/src/capability/bot/types.js +1 -0
  21. package/{src/capability/calendar/client.ts → dist/src/capability/calendar/client.js} +118 -241
  22. package/{src/capability/calendar/schema.ts → dist/src/capability/calendar/schema.js} +0 -38
  23. package/dist/src/capability/calendar/tool.js +365 -0
  24. package/dist/src/capability/calendar/types.js +12 -0
  25. package/{src/capability/doc/client.ts → dist/src/capability/doc/client.js} +370 -605
  26. package/{src/capability/doc/schema.ts → dist/src/capability/doc/schema.js} +345 -394
  27. package/dist/src/capability/doc/tool.js +1556 -0
  28. package/dist/src/capability/doc/types.js +113 -0
  29. package/dist/src/capability/mcp/index.js +3 -0
  30. package/dist/src/capability/mcp/schema.js +102 -0
  31. package/dist/src/capability/mcp/tool.js +146 -0
  32. package/dist/src/capability/mcp/transport.js +293 -0
  33. package/dist/src/channel.js +224 -0
  34. package/dist/src/config/accounts.js +236 -0
  35. package/dist/src/config/derived-paths.js +31 -0
  36. package/dist/src/config/index.js +7 -0
  37. package/dist/src/config/media.js +110 -0
  38. package/dist/src/config/network.js +32 -0
  39. package/dist/src/config/routing.js +20 -0
  40. package/dist/src/config/runtime-config.js +25 -0
  41. package/dist/src/config/schema.js +4 -0
  42. package/{src/config-schema.ts → dist/src/config-schema.js} +1 -1
  43. package/dist/src/context-store.js +219 -0
  44. package/{src/crypto/aes.ts → dist/src/crypto/aes.js} +11 -28
  45. package/dist/src/crypto/index.js +9 -0
  46. package/{src/crypto/signature.ts → dist/src/crypto/signature.js} +3 -18
  47. package/{src/crypto/xml.ts → dist/src/crypto/xml.js} +3 -11
  48. package/dist/src/crypto.js +145 -0
  49. package/dist/src/domain/models.js +1 -0
  50. package/dist/src/domain/policies.js +32 -0
  51. package/{src/dynamic-agent.ts → dist/src/dynamic-agent.js} +36 -73
  52. package/dist/src/gateway-monitor.js +139 -0
  53. package/dist/src/http.js +114 -0
  54. package/{src/media.ts → dist/src/media.js} +21 -40
  55. package/dist/src/monitor/limits.js +7 -0
  56. package/dist/src/monitor/state.js +28 -0
  57. package/dist/src/monitor.js +84 -0
  58. package/dist/src/observability/audit-log.js +30 -0
  59. package/dist/src/observability/legacy-operational-event-store.js +22 -0
  60. package/dist/src/observability/raw-envelope-log.js +24 -0
  61. package/dist/src/observability/status-registry.js +9 -0
  62. package/dist/src/observability/transport-session-view.js +14 -0
  63. package/dist/src/onboarding.js +546 -0
  64. package/dist/src/outbound.js +557 -0
  65. package/dist/src/runtime/dispatcher.js +57 -0
  66. package/{src/runtime/index.ts → dist/src/runtime/index.js} +0 -1
  67. package/dist/src/runtime/outbound-intent.js +1 -0
  68. package/dist/src/runtime/reply-orchestrator.js +38 -0
  69. package/dist/src/runtime/routing-bridge.js +26 -0
  70. package/dist/src/runtime/session-manager.js +112 -0
  71. package/dist/src/runtime/source-registry.js +174 -0
  72. package/dist/src/runtime.js +1 -0
  73. package/dist/src/shared/command-auth.js +57 -0
  74. package/{src/shared/index.ts → dist/src/shared/index.js} +0 -1
  75. package/dist/src/shared/media-asset.js +65 -0
  76. package/dist/src/shared/media-service.js +59 -0
  77. package/dist/src/shared/media-types.js +1 -0
  78. package/{src/shared/xml-parser.ts → dist/src/shared/xml-parser.js} +72 -63
  79. package/dist/src/store/active-reply-store.js +41 -0
  80. package/dist/src/store/interfaces.js +1 -0
  81. package/dist/src/store/memory-store.js +33 -0
  82. package/dist/src/store/stream-batch-store.js +319 -0
  83. package/{src/target.ts → dist/src/target.js} +15 -48
  84. package/dist/src/transport/agent-api/client.js +168 -0
  85. package/dist/src/transport/agent-api/core.js +337 -0
  86. package/dist/src/transport/agent-api/delivery.js +28 -0
  87. package/dist/src/transport/agent-api/media-upload.js +4 -0
  88. package/dist/src/transport/agent-api/reply.js +24 -0
  89. package/dist/src/transport/agent-api/upstream-delivery.js +30 -0
  90. package/dist/src/transport/agent-api/upstream-media-upload.js +46 -0
  91. package/dist/src/transport/agent-api/upstream-reply.js +26 -0
  92. package/dist/src/transport/agent-callback/http-handler.js +30 -0
  93. package/dist/src/transport/agent-callback/inbound.js +4 -0
  94. package/dist/src/transport/agent-callback/reply.js +8 -0
  95. package/dist/src/transport/agent-callback/request-handler.js +189 -0
  96. package/dist/src/transport/agent-callback/session.js +15 -0
  97. package/dist/src/transport/bot-webhook/active-reply.js +27 -0
  98. package/dist/src/transport/bot-webhook/http-handler.js +31 -0
  99. package/dist/src/transport/bot-webhook/inbound-normalizer.js +496 -0
  100. package/dist/src/transport/bot-webhook/inbound.js +4 -0
  101. package/dist/src/transport/bot-webhook/message-shape.js +98 -0
  102. package/dist/src/transport/bot-webhook/protocol.js +124 -0
  103. package/dist/src/transport/bot-webhook/reply.js +9 -0
  104. package/dist/src/transport/bot-webhook/request-handler.js +285 -0
  105. package/dist/src/transport/bot-webhook/session.js +15 -0
  106. package/dist/src/transport/bot-ws/inbound.js +147 -0
  107. package/dist/src/transport/bot-ws/media.js +236 -0
  108. package/dist/src/transport/bot-ws/reply.js +310 -0
  109. package/dist/src/transport/bot-ws/sdk-adapter.js +257 -0
  110. package/dist/src/transport/bot-ws/session.js +15 -0
  111. package/dist/src/transport/http/common.js +78 -0
  112. package/dist/src/transport/http/registry.js +71 -0
  113. package/dist/src/transport/http/request-handler.js +51 -0
  114. package/{src/transport/index.ts → dist/src/transport/index.js} +2 -10
  115. package/dist/src/types/account.js +1 -0
  116. package/dist/src/types/config.js +1 -0
  117. package/dist/src/types/constants.js +28 -0
  118. package/dist/src/types/events.js +1 -0
  119. package/dist/src/types/index.js +1 -0
  120. package/dist/src/types/legacy-stream.js +1 -0
  121. package/dist/src/types/message.js +5 -0
  122. package/dist/src/types/runtime-context.js +1 -0
  123. package/dist/src/types/runtime.js +1 -0
  124. package/dist/src/types.js +1 -0
  125. package/dist/src/upstream/index.js +111 -0
  126. package/dist/src/wecom_msg_adapter/markdown_adapter.js +280 -0
  127. package/openclaw.plugin.json +15 -0
  128. package/package.json +18 -1
  129. package/.github/workflows/release.yml +0 -143
  130. package/GOVERNANCE.md +0 -26
  131. package/SKILLS_CAL.md +0 -895
  132. package/SKILLS_DOC.md +0 -2288
  133. package/UPSTREAM_CONFIG.md +0 -170
  134. package/UPSTREAM_PLAN.md +0 -175
  135. package/assets/01.bot-add.png +0 -0
  136. package/assets/01.bot-setp2.png +0 -0
  137. package/assets/01.image.jpg +0 -0
  138. package/assets/02.agent.add.png +0 -0
  139. package/assets/02.agent.api-set.png +0 -0
  140. package/assets/02.image.jpg +0 -0
  141. package/assets/03.agent.page.png +0 -0
  142. package/assets/03.bot.page.png +0 -0
  143. package/assets/link-me.jpg +0 -0
  144. package/assets/register.png +0 -0
  145. package/changelog/v2.2.28.md +0 -70
  146. package/changelog/v2.3.10.md +0 -17
  147. package/changelog/v2.3.11.md +0 -19
  148. package/changelog/v2.3.12.md +0 -25
  149. package/changelog/v2.3.13.md +0 -19
  150. package/changelog/v2.3.14.md +0 -48
  151. package/changelog/v2.3.15.md +0 -15
  152. package/changelog/v2.3.16.md +0 -11
  153. package/changelog/v2.3.18.md +0 -22
  154. package/changelog/v2.3.19.md +0 -73
  155. package/changelog/v2.3.2.md +0 -28
  156. package/changelog/v2.3.26.md +0 -21
  157. package/changelog/v2.3.27.md +0 -33
  158. package/changelog/v2.3.4.md +0 -20
  159. package/changelog/v2.3.9.md +0 -22
  160. package/changelog/v2.4.12.md +0 -37
  161. package/changelog/v2.4.16.md +0 -19
  162. package/compat-single-account.md +0 -148
  163. package/index.test.ts +0 -38
  164. package/scripts/test-proxy.ts +0 -70
  165. package/src/accounts.ts +0 -34
  166. package/src/agent/api-client.upload.test.ts +0 -109
  167. package/src/agent/handler.event-filter.test.ts +0 -100
  168. package/src/agent/handler.ts +0 -1105
  169. package/src/agent/index.ts +0 -12
  170. package/src/app/account-runtime.ts +0 -276
  171. package/src/app/bootstrap.ts +0 -29
  172. package/src/app/index.ts +0 -192
  173. package/src/capability/agent/delivery-service.ts +0 -87
  174. package/src/capability/agent/fallback-policy.ts +0 -13
  175. package/src/capability/agent/ingress-service.ts +0 -38
  176. package/src/capability/agent/upstream-delivery-service.ts +0 -96
  177. package/src/capability/bot/dispatch-config.ts +0 -47
  178. package/src/capability/bot/fallback-delivery.ts +0 -178
  179. package/src/capability/bot/local-path-delivery.ts +0 -215
  180. package/src/capability/bot/sandbox-media.test.ts +0 -221
  181. package/src/capability/bot/sandbox-media.ts +0 -176
  182. package/src/capability/bot/service.ts +0 -56
  183. package/src/capability/bot/stream-delivery.ts +0 -379
  184. package/src/capability/bot/stream-finalizer.ts +0 -120
  185. package/src/capability/bot/stream-orchestrator.ts +0 -371
  186. package/src/capability/bot/types.ts +0 -8
  187. package/src/capability/calendar/SKILLS_CHECKLIST.md +0 -251
  188. package/src/capability/calendar/tool.ts +0 -417
  189. package/src/capability/calendar/types.ts +0 -309
  190. package/src/capability/doc/tool.ts +0 -1629
  191. package/src/capability/doc/types.ts +0 -792
  192. package/src/capability/mcp/index.ts +0 -10
  193. package/src/capability/mcp/schema.ts +0 -107
  194. package/src/capability/mcp/tool.ts +0 -174
  195. package/src/capability/mcp/transport.ts +0 -394
  196. package/src/channel.config.test.ts +0 -147
  197. package/src/channel.lifecycle.test.ts +0 -255
  198. package/src/channel.meta.test.ts +0 -26
  199. package/src/channel.ts +0 -256
  200. package/src/config/accounts.resolve.test.ts +0 -75
  201. package/src/config/accounts.ts +0 -296
  202. package/src/config/derived-paths.test.ts +0 -111
  203. package/src/config/derived-paths.ts +0 -41
  204. package/src/config/index.ts +0 -26
  205. package/src/config/media.test.ts +0 -113
  206. package/src/config/media.ts +0 -139
  207. package/src/config/network.ts +0 -53
  208. package/src/config/routing.test.ts +0 -88
  209. package/src/config/routing.ts +0 -26
  210. package/src/config/runtime-config.ts +0 -46
  211. package/src/config/schema.ts +0 -90
  212. package/src/context-store.ts +0 -297
  213. package/src/crypto/index.ts +0 -24
  214. package/src/crypto.test.ts +0 -32
  215. package/src/crypto.ts +0 -176
  216. package/src/domain/models.ts +0 -7
  217. package/src/domain/policies.ts +0 -36
  218. package/src/dynamic-agent.account-scope.test.ts +0 -17
  219. package/src/gateway-monitor.ts +0 -181
  220. package/src/http.ts +0 -145
  221. package/src/media.test.ts +0 -82
  222. package/src/monitor/limits.ts +0 -7
  223. package/src/monitor/state.queue.test.ts +0 -185
  224. package/src/monitor/state.ts +0 -34
  225. package/src/monitor.active.test.ts +0 -245
  226. package/src/monitor.inbound-filter.test.ts +0 -63
  227. package/src/monitor.integration.test.ts +0 -208
  228. package/src/monitor.ts +0 -121
  229. package/src/monitor.webhook.test.ts +0 -774
  230. package/src/observability/audit-log.ts +0 -48
  231. package/src/observability/legacy-operational-event-store.ts +0 -36
  232. package/src/observability/raw-envelope-log.ts +0 -28
  233. package/src/observability/status-registry.ts +0 -13
  234. package/src/observability/transport-session-view.ts +0 -14
  235. package/src/onboarding.test.ts +0 -336
  236. package/src/onboarding.ts +0 -704
  237. package/src/outbound.test.ts +0 -1271
  238. package/src/outbound.ts +0 -746
  239. package/src/runtime/dispatcher.ts +0 -71
  240. package/src/runtime/outbound-intent.ts +0 -4
  241. package/src/runtime/reply-orchestrator.test.ts +0 -71
  242. package/src/runtime/reply-orchestrator.ts +0 -67
  243. package/src/runtime/routing-bridge.test.ts +0 -115
  244. package/src/runtime/routing-bridge.ts +0 -44
  245. package/src/runtime/session-manager.test.ts +0 -174
  246. package/src/runtime/session-manager.ts +0 -139
  247. package/src/runtime/source-registry.ts +0 -249
  248. package/src/runtime.ts +0 -14
  249. package/src/shared/command-auth.ts +0 -87
  250. package/src/shared/media-asset.ts +0 -78
  251. package/src/shared/media-service.test.ts +0 -111
  252. package/src/shared/media-service.ts +0 -84
  253. package/src/shared/media-types.ts +0 -5
  254. package/src/shared/xml-parser.test.ts +0 -50
  255. package/src/store/active-reply-store.ts +0 -42
  256. package/src/store/interfaces.ts +0 -11
  257. package/src/store/memory-store.ts +0 -43
  258. package/src/store/stream-batch-store.ts +0 -350
  259. package/src/transport/agent-api/client.ts +0 -277
  260. package/src/transport/agent-api/core.ts +0 -463
  261. package/src/transport/agent-api/delivery.ts +0 -41
  262. package/src/transport/agent-api/media-upload.ts +0 -11
  263. package/src/transport/agent-api/reply.ts +0 -39
  264. package/src/transport/agent-api/upstream-delivery.ts +0 -45
  265. package/src/transport/agent-api/upstream-media-upload.ts +0 -70
  266. package/src/transport/agent-api/upstream-reply.ts +0 -43
  267. package/src/transport/agent-callback/http-handler.ts +0 -47
  268. package/src/transport/agent-callback/inbound.ts +0 -5
  269. package/src/transport/agent-callback/reply.ts +0 -13
  270. package/src/transport/agent-callback/request-handler.ts +0 -244
  271. package/src/transport/agent-callback/session.ts +0 -23
  272. package/src/transport/bot-webhook/active-reply.ts +0 -39
  273. package/src/transport/bot-webhook/http-handler.ts +0 -48
  274. package/src/transport/bot-webhook/inbound-normalizer.test.ts +0 -433
  275. package/src/transport/bot-webhook/inbound-normalizer.ts +0 -558
  276. package/src/transport/bot-webhook/inbound.ts +0 -5
  277. package/src/transport/bot-webhook/message-shape.ts +0 -92
  278. package/src/transport/bot-webhook/protocol.ts +0 -148
  279. package/src/transport/bot-webhook/reply.ts +0 -15
  280. package/src/transport/bot-webhook/request-handler.ts +0 -394
  281. package/src/transport/bot-webhook/session.ts +0 -23
  282. package/src/transport/bot-ws/inbound.test.ts +0 -290
  283. package/src/transport/bot-ws/inbound.ts +0 -163
  284. package/src/transport/bot-ws/media.test.ts +0 -44
  285. package/src/transport/bot-ws/media.ts +0 -321
  286. package/src/transport/bot-ws/reply.test.ts +0 -450
  287. package/src/transport/bot-ws/reply.ts +0 -365
  288. package/src/transport/bot-ws/sdk-adapter.test.ts +0 -187
  289. package/src/transport/bot-ws/sdk-adapter.ts +0 -314
  290. package/src/transport/bot-ws/session.ts +0 -28
  291. package/src/transport/http/common.ts +0 -109
  292. package/src/transport/http/registry.ts +0 -92
  293. package/src/transport/http/request-handler.ts +0 -84
  294. package/src/types/account.ts +0 -70
  295. package/src/types/config.ts +0 -114
  296. package/src/types/constants.ts +0 -31
  297. package/src/types/events.ts +0 -21
  298. package/src/types/global.d.ts +0 -9
  299. package/src/types/index.ts +0 -17
  300. package/src/types/legacy-stream.ts +0 -50
  301. package/src/types/message.ts +0 -189
  302. package/src/types/runtime-context.ts +0 -28
  303. package/src/types/runtime.ts +0 -165
  304. package/src/types.ts +0 -41
  305. package/src/upstream/index.ts +0 -150
  306. package/src/upstream.test.ts +0 -84
  307. package/src/wecom_msg_adapter/markdown_adapter.ts +0 -331
  308. package/tsconfig.json +0 -22
  309. package/vitest.config.ts +0 -26
  310. /package/{src/capability/agent/index.ts → dist/src/capability/agent/index.js} +0 -0
  311. /package/{src/capability/bot/index.ts → dist/src/capability/bot/index.js} +0 -0
  312. /package/{src/capability/calendar/index.ts → dist/src/capability/calendar/index.js} +0 -0
  313. /package/{src/capability/index.ts → dist/src/capability/index.js} +0 -0
@@ -1,147 +0,0 @@
1
- import type { OpenClawConfig } from "openclaw/plugin-sdk";
2
- import { describe, expect, it } from "vitest";
3
-
4
- import { wecomPlugin } from "./channel.js";
5
-
6
- describe("wecomPlugin config.deleteAccount", () => {
7
- it("removes only the target matrix account", () => {
8
- const cfg: OpenClawConfig = {
9
- channels: {
10
- wecom: {
11
- enabled: true,
12
- accounts: {
13
- "acct-a": {
14
- enabled: true,
15
- bot: {
16
- token: "token-a",
17
- encodingAESKey: "aes-a",
18
- },
19
- },
20
- "acct-b": {
21
- enabled: true,
22
- bot: {
23
- token: "token-b",
24
- encodingAESKey: "aes-b",
25
- },
26
- },
27
- },
28
- },
29
- },
30
- } as OpenClawConfig;
31
-
32
- const next = wecomPlugin.config.deleteAccount!({ cfg, accountId: "acct-a" });
33
- const accounts = (next.channels?.wecom as { accounts?: Record<string, unknown> } | undefined)
34
- ?.accounts;
35
-
36
- expect(accounts?.["acct-a"]).toBeUndefined();
37
- expect(accounts?.["acct-b"]).toBeDefined();
38
- expect(next.channels?.wecom).toBeDefined();
39
- });
40
-
41
- it("removes legacy wecom section when deleting default account", () => {
42
- const cfg: OpenClawConfig = {
43
- channels: {
44
- wecom: {
45
- enabled: true,
46
- bot: {
47
- token: "token",
48
- encodingAESKey: "aes",
49
- },
50
- },
51
- },
52
- } as OpenClawConfig;
53
-
54
- const next = wecomPlugin.config.deleteAccount!({ cfg, accountId: "default" });
55
- expect(next.channels?.wecom).toBeUndefined();
56
- });
57
- });
58
-
59
- describe("wecomPlugin account conflict guards", () => {
60
- it("marks duplicate bot token account as unconfigured", async () => {
61
- const cfg: OpenClawConfig = {
62
- channels: {
63
- wecom: {
64
- enabled: true,
65
- accounts: {
66
- "acct-a": {
67
- enabled: true,
68
- bot: { token: "token-shared", encodingAESKey: "aes-a" },
69
- },
70
- "acct-b": {
71
- enabled: true,
72
- bot: { token: "token-shared", encodingAESKey: "aes-b" },
73
- },
74
- },
75
- },
76
- },
77
- } as OpenClawConfig;
78
-
79
- const accountA = wecomPlugin.config.resolveAccount(cfg, "acct-a");
80
- const accountB = wecomPlugin.config.resolveAccount(cfg, "acct-b");
81
- expect(await wecomPlugin.config.isConfigured!(accountA, cfg)).toBe(true);
82
- expect(await wecomPlugin.config.isConfigured!(accountB, cfg)).toBe(false);
83
- expect(wecomPlugin.config.unconfiguredReason?.(accountB, cfg)).toContain("Duplicate WeCom bot token");
84
- });
85
-
86
- it("marks duplicate bot aibotid account as unconfigured", async () => {
87
- const cfg: OpenClawConfig = {
88
- channels: {
89
- wecom: {
90
- enabled: true,
91
- accounts: {
92
- "acct-a": {
93
- enabled: true,
94
- bot: { token: "token-a", encodingAESKey: "aes-a", aibotid: "BOT_001" },
95
- },
96
- "acct-b": {
97
- enabled: true,
98
- bot: { token: "token-b", encodingAESKey: "aes-b", aibotid: "BOT_001" },
99
- },
100
- },
101
- },
102
- },
103
- } as OpenClawConfig;
104
-
105
- const accountB = wecomPlugin.config.resolveAccount(cfg, "acct-b");
106
- expect(await wecomPlugin.config.isConfigured!(accountB, cfg)).toBe(false);
107
- expect(wecomPlugin.config.unconfiguredReason?.(accountB, cfg)).toContain("Duplicate WeCom bot aibotid");
108
- });
109
-
110
- it("marks duplicate corpId/agentId account as unconfigured", async () => {
111
- const cfg: OpenClawConfig = {
112
- channels: {
113
- wecom: {
114
- enabled: true,
115
- accounts: {
116
- "acct-a": {
117
- enabled: true,
118
- agent: {
119
- corpId: "corp-1",
120
- corpSecret: "secret-a",
121
- agentId: 1001,
122
- token: "token-a",
123
- encodingAESKey: "aes-a",
124
- },
125
- },
126
- "acct-b": {
127
- enabled: true,
128
- agent: {
129
- corpId: "corp-1",
130
- corpSecret: "secret-b",
131
- agentId: 1001,
132
- token: "token-b",
133
- encodingAESKey: "aes-b",
134
- },
135
- },
136
- },
137
- },
138
- },
139
- } as OpenClawConfig;
140
-
141
- const accountB = wecomPlugin.config.resolveAccount(cfg, "acct-b");
142
- expect(await wecomPlugin.config.isConfigured!(accountB, cfg)).toBe(false);
143
- expect(wecomPlugin.config.unconfiguredReason?.(accountB, cfg)).toContain(
144
- "Duplicate WeCom agent identity",
145
- );
146
- });
147
- });
@@ -1,255 +0,0 @@
1
- import { IncomingMessage, ServerResponse } from "node:http";
2
- import { Socket } from "node:net";
3
-
4
- import {
5
- type ChannelAccountSnapshot,
6
- type ChannelGatewayContext,
7
- type OpenClawConfig,
8
- } from "openclaw/plugin-sdk";
9
- import { describe, expect, it, vi } from "vitest";
10
-
11
- import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
12
- import { computeWecomMsgSignature, encryptWecomPlaintext } from "./crypto.js";
13
- import { wecomPlugin } from "./channel.js";
14
- import { handleWecomWebhookRequest } from "./monitor.js";
15
- import type { ResolvedWecomAccount } from "./types/index.js";
16
-
17
- function createMockRequest(params: {
18
- method: "GET" | "POST";
19
- url: string;
20
- body?: unknown;
21
- }): IncomingMessage {
22
- const socket = new Socket();
23
- const req = new IncomingMessage(socket);
24
- req.method = params.method;
25
- req.url = params.url;
26
- if (params.method === "POST") {
27
- req.push(JSON.stringify(params.body ?? {}));
28
- }
29
- req.push(null);
30
- return req;
31
- }
32
-
33
- function createMockResponse(): ServerResponse & {
34
- _getData: () => string;
35
- _getStatusCode: () => number;
36
- } {
37
- type MockResponse = ServerResponse & {
38
- _getData: () => string;
39
- _getStatusCode: () => number;
40
- };
41
- const req = new IncomingMessage(new Socket());
42
- const res = new ServerResponse(req) as MockResponse;
43
- let data = "";
44
- res.write = (chunk: string | Uint8Array) => {
45
- data += String(chunk);
46
- return true;
47
- };
48
- res.end = ((chunk?: string | Uint8Array) => {
49
- if (chunk) data += String(chunk);
50
- return res;
51
- }) as MockResponse["end"];
52
- res._getData = () => data;
53
- res._getStatusCode = () => res.statusCode;
54
- return res;
55
- }
56
-
57
- function createCtx(params: {
58
- cfg: OpenClawConfig;
59
- accountId?: string;
60
- abortController: AbortController;
61
- }): ChannelGatewayContext<ResolvedWecomAccount> & {
62
- statusUpdates: Array<Partial<ChannelAccountSnapshot>>;
63
- } {
64
- const accountId = params.accountId ?? "default";
65
- const account = wecomPlugin.config.resolveAccount(
66
- params.cfg,
67
- accountId,
68
- ) as ResolvedWecomAccount;
69
- const snapshot: ChannelAccountSnapshot = {
70
- accountId,
71
- configured: true,
72
- enabled: true,
73
- running: false,
74
- };
75
- const statusUpdates: Array<Partial<ChannelAccountSnapshot>> = [];
76
- return {
77
- cfg: params.cfg,
78
- accountId,
79
- account,
80
- runtime: createRuntimeEnv(),
81
- abortSignal: params.abortController.signal,
82
- log: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
83
- getStatus: () => snapshot,
84
- setStatus: (next) => {
85
- statusUpdates.push(next);
86
- Object.assign(snapshot, next);
87
- },
88
- statusUpdates,
89
- };
90
- }
91
-
92
- function createWebhookBotConfig(params: {
93
- token: string;
94
- encodingAESKey: string;
95
- receiveId?: string;
96
- }): OpenClawConfig {
97
- return {
98
- channels: {
99
- wecom: {
100
- enabled: true,
101
- bot: {
102
- primaryTransport: "webhook",
103
- webhook: {
104
- token: params.token,
105
- encodingAESKey: params.encodingAESKey,
106
- receiveId: params.receiveId ?? "",
107
- },
108
- },
109
- },
110
- },
111
- } as OpenClawConfig;
112
- }
113
-
114
- async function sendWecomGetVerify(params: {
115
- path: string;
116
- token: string;
117
- encodingAESKey: string;
118
- receiveId: string;
119
- }): Promise<{ handled: boolean; status: number; body: string }> {
120
- const timestamp = "1700000000";
121
- const nonce = "nonce";
122
- const echostr = encryptWecomPlaintext({
123
- encodingAESKey: params.encodingAESKey,
124
- receiveId: params.receiveId,
125
- plaintext: "ping",
126
- });
127
- const msgSignature = computeWecomMsgSignature({
128
- token: params.token,
129
- timestamp,
130
- nonce,
131
- encrypt: echostr,
132
- });
133
- const req = createMockRequest({
134
- method: "GET",
135
- url:
136
- `${params.path}?msg_signature=${encodeURIComponent(msgSignature)}` +
137
- `&timestamp=${encodeURIComponent(timestamp)}` +
138
- `&nonce=${encodeURIComponent(nonce)}` +
139
- `&echostr=${encodeURIComponent(echostr)}`,
140
- });
141
- const res = createMockResponse();
142
- const handled = await handleWecomWebhookRequest(req, res);
143
- return {
144
- handled,
145
- status: res._getStatusCode(),
146
- body: res._getData(),
147
- };
148
- }
149
-
150
- describe("wecomPlugin gateway lifecycle", () => {
151
- it("keeps startAccount pending until abort signal", async () => {
152
- const token = "token";
153
- const encodingAESKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
154
- const cfg = createWebhookBotConfig({ token, encodingAESKey });
155
- const abortController = new AbortController();
156
- const ctx = createCtx({ cfg, abortController });
157
-
158
- const startPromise = wecomPlugin.gateway!.startAccount!(ctx);
159
- let resolved = false;
160
- void startPromise.then(() => {
161
- resolved = true;
162
- });
163
-
164
- await Promise.resolve();
165
- await Promise.resolve();
166
- expect(resolved).toBe(false);
167
-
168
- abortController.abort();
169
- await startPromise;
170
- expect(resolved).toBe(true);
171
- });
172
-
173
- it("unregisters webhook targets after abort", async () => {
174
- const token = "token";
175
- const encodingAESKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
176
- const receiveId = "";
177
- const cfg = createWebhookBotConfig({ token, encodingAESKey, receiveId });
178
- const abortController = new AbortController();
179
- const ctx = createCtx({ cfg, abortController });
180
-
181
- const startPromise = wecomPlugin.gateway!.startAccount!(ctx);
182
- await Promise.resolve();
183
-
184
- const activeLegacyRoute = await sendWecomGetVerify({
185
- path: "/wecom/bot",
186
- token,
187
- encodingAESKey,
188
- receiveId,
189
- });
190
- expect(activeLegacyRoute.handled).toBe(true);
191
- expect(activeLegacyRoute.status).toBe(200);
192
- expect(activeLegacyRoute.body).toBe("ping");
193
-
194
- const activePluginRoute = await sendWecomGetVerify({
195
- path: "/plugins/wecom/bot",
196
- token,
197
- encodingAESKey,
198
- receiveId,
199
- });
200
- expect(activePluginRoute.handled).toBe(true);
201
- expect(activePluginRoute.status).toBe(200);
202
- expect(activePluginRoute.body).toBe("ping");
203
-
204
- abortController.abort();
205
- await startPromise;
206
-
207
- const inactiveLegacyRoute = await sendWecomGetVerify({
208
- path: "/wecom/bot",
209
- token,
210
- encodingAESKey,
211
- receiveId,
212
- });
213
- expect(inactiveLegacyRoute.handled).toBe(false);
214
-
215
- const inactivePluginRoute = await sendWecomGetVerify({
216
- path: "/plugins/wecom/bot",
217
- token,
218
- encodingAESKey,
219
- receiveId,
220
- });
221
- expect(inactivePluginRoute.handled).toBe(false);
222
- });
223
-
224
- it("rejects startup when matrix account credentials conflict", async () => {
225
- const cfg = {
226
- channels: {
227
- wecom: {
228
- enabled: true,
229
- accounts: {
230
- "acct-a": {
231
- enabled: true,
232
- bot: {
233
- token: "token-shared",
234
- encodingAESKey: "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG",
235
- },
236
- },
237
- "acct-b": {
238
- enabled: true,
239
- bot: {
240
- token: "token-shared",
241
- encodingAESKey: "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG",
242
- },
243
- },
244
- },
245
- },
246
- },
247
- } as OpenClawConfig;
248
- const abortController = new AbortController();
249
- const ctx = createCtx({ cfg, accountId: "acct-b", abortController });
250
-
251
- await expect(wecomPlugin.gateway!.startAccount!(ctx)).rejects.toThrow(
252
- /Duplicate WeCom bot token/i,
253
- );
254
- });
255
- });
@@ -1,26 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { wecomPlugin } from "./channel.js";
3
-
4
- describe("wecomPlugin meta", () => {
5
- it("uses chinese-facing labels in channel selection", () => {
6
- expect(wecomPlugin.meta.label).toBe("WeCom (企业微信)");
7
- expect(wecomPlugin.meta.selectionLabel).toBe("WeCom (企业微信)");
8
- expect(wecomPlugin.meta.blurb).toContain("企业微信官方推荐三方插件");
9
- expect(wecomPlugin.meta.docsLabel).toBe("企业微信");
10
- expect(wecomPlugin.meta.selectionDocsPrefix).toBe("文档:");
11
- });
12
-
13
- it("exposes a setupWizard for guided setup discovery", () => {
14
- expect(wecomPlugin.setupWizard?.channel).toBe("wecom");
15
- });
16
-
17
- it("preserves fully qualified WeCom messaging targets during normalization", () => {
18
- expect(wecomPlugin.messaging?.normalizeTarget?.("wecom:user:zhangsan")).toBe(
19
- "wecom:user:zhangsan",
20
- );
21
- expect(wecomPlugin.messaging?.normalizeTarget?.("wecom-agent:blue:user:zhangsan")).toBe(
22
- "wecom-agent:blue:user:zhangsan",
23
- );
24
- expect(wecomPlugin.messaging?.normalizeTarget?.("wecom:zhangsan")).toBe("zhangsan");
25
- });
26
- });
package/src/channel.ts DELETED
@@ -1,256 +0,0 @@
1
- import type { ChannelAccountSnapshot, ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk";
2
- import {
3
- deleteAccountFromConfigSection,
4
- setAccountEnabledInConfigSection,
5
- } from "openclaw/plugin-sdk/core";
6
- import {
7
- DEFAULT_ACCOUNT_ID,
8
- listWecomAccountIds,
9
- resolveDerivedPathSummary,
10
- resolveDefaultWecomAccountId,
11
- resolveWecomAccount,
12
- resolveWecomAccountConflict,
13
- } from "./config/index.js";
14
- import { monitorWecomProvider } from "./gateway-monitor.js";
15
- import { wecomSetupWizard } from "./onboarding.js";
16
- import { wecomOutbound } from "./outbound.js";
17
- import type { ResolvedWecomAccount } from "./types/index.js";
18
-
19
- const meta = {
20
- id: "wecom",
21
- label: "WeCom (企业微信)",
22
- selectionLabel: "WeCom (企业微信)",
23
- docsPath: "/channels/wecom",
24
- docsLabel: "企业微信",
25
- blurb: "企业微信官方推荐三方插件,默认 Bot WS 配置简单,支持主动发消息与 Agent 全能力。",
26
- selectionDocsPrefix: "文档:",
27
- aliases: ["wechatwork", "wework", "qywx", "企微", "企业微信"],
28
- order: 85,
29
- quickstartAllowFrom: true,
30
- };
31
-
32
- function resolveAccountInboundPath(account: ResolvedWecomAccount): string | undefined {
33
- const derivedPaths = resolveDerivedPathSummary(account.accountId);
34
- if (account.bot?.primaryTransport === "webhook" && account.bot.webhookConfigured) {
35
- return derivedPaths.botWebhook[0];
36
- }
37
- if (account.agent?.callbackConfigured) {
38
- return derivedPaths.agentCallback[0];
39
- }
40
- return undefined;
41
- }
42
-
43
- function normalizeWecomMessagingTarget(raw: string): string | undefined {
44
- const trimmed = raw.trim();
45
- if (!trimmed) return undefined;
46
- if (/^wecom-agent(?:-upstream)?:/i.test(trimmed)) {
47
- return trimmed;
48
- }
49
- if (/^(wecom|wechatwork|wework|qywx):(user|group|chat|party|dept|tag|context):/i.test(trimmed)) {
50
- return trimmed;
51
- }
52
- return trimmed.replace(/^(wecom|wechatwork|wework|qywx):/i, "").trim() || undefined;
53
- }
54
-
55
- export const wecomPlugin: ChannelPlugin<ResolvedWecomAccount> = {
56
- id: "wecom",
57
- meta,
58
- setupWizard: wecomSetupWizard,
59
- capabilities: {
60
- chatTypes: ["direct", "group"],
61
- media: true,
62
- reactions: false,
63
- threads: false,
64
- polls: false,
65
- nativeCommands: false,
66
- blockStreaming: false,
67
- },
68
- reload: { configPrefixes: ["channels.wecom"] },
69
- // A permissive schema keeps config UX working while preventing startup failures.
70
- configSchema: {
71
- schema: {
72
- type: "object",
73
- additionalProperties: true,
74
- properties: {},
75
- },
76
- },
77
- config: {
78
- listAccountIds: (cfg) => listWecomAccountIds(cfg as OpenClawConfig),
79
- resolveAccount: (cfg, accountId) =>
80
- resolveWecomAccount({ cfg: cfg as OpenClawConfig, accountId }),
81
- defaultAccountId: (cfg) => resolveDefaultWecomAccountId(cfg as OpenClawConfig),
82
- setAccountEnabled: ({ cfg, accountId, enabled }) =>
83
- setAccountEnabledInConfigSection({
84
- cfg: cfg as OpenClawConfig,
85
- sectionKey: "wecom",
86
- accountId,
87
- enabled,
88
- allowTopLevel: true,
89
- }),
90
- deleteAccount: ({ cfg, accountId }) =>
91
- deleteAccountFromConfigSection({
92
- cfg: cfg as OpenClawConfig,
93
- sectionKey: "wecom",
94
- accountId,
95
- clearBaseFields: ["bot", "agent"],
96
- }),
97
- isConfigured: (account, cfg) => {
98
- if (!account.configured) {
99
- return false;
100
- }
101
- return !resolveWecomAccountConflict({
102
- cfg: cfg as OpenClawConfig,
103
- accountId: account.accountId,
104
- });
105
- },
106
- unconfiguredReason: (account, cfg) =>
107
- resolveWecomAccountConflict({
108
- cfg: cfg as OpenClawConfig,
109
- accountId: account.accountId,
110
- })?.message ?? "not configured",
111
- describeAccount: (account, cfg): ChannelAccountSnapshot => {
112
- const conflict = resolveWecomAccountConflict({
113
- cfg: cfg as OpenClawConfig,
114
- accountId: account.accountId,
115
- });
116
- return {
117
- accountId: account.accountId,
118
- name: account.name,
119
- enabled: account.enabled,
120
- configured: account.configured && !conflict,
121
- webhookPath: resolveAccountInboundPath(account),
122
- };
123
- },
124
- resolveAllowFrom: ({ cfg, accountId }) => {
125
- const account = resolveWecomAccount({
126
- cfg: cfg as OpenClawConfig,
127
- accountId,
128
- });
129
- // 与其他渠道保持一致:直接返回 allowFrom,空则允许所有人
130
- const allowFrom =
131
- account.agent?.config.dm?.allowFrom ?? account.bot?.config.dm?.allowFrom ?? [];
132
- return allowFrom.map((entry) => String(entry));
133
- },
134
- formatAllowFrom: ({ allowFrom }) =>
135
- allowFrom
136
- .map((entry) => String(entry).trim())
137
- .filter(Boolean)
138
- .map((entry) => entry.toLowerCase()),
139
- },
140
- // security 配置在 WeCom 中不需要,框架会通过 resolveAllowFrom 自动判断
141
- groups: {
142
- // WeCom bots are usually mention-gated by the platform in groups already.
143
- resolveRequireMention: () => true,
144
- },
145
- threading: {
146
- resolveReplyToMode: () => "off",
147
- },
148
- messaging: {
149
- normalizeTarget: normalizeWecomMessagingTarget,
150
- targetResolver: {
151
- looksLikeId: (raw) => Boolean(raw.trim()),
152
- hint: "<userid|chatid>",
153
- },
154
- },
155
- outbound: {
156
- ...wecomOutbound,
157
- },
158
- status: {
159
- defaultRuntime: {
160
- accountId: DEFAULT_ACCOUNT_ID,
161
- running: false,
162
- lastStartAt: null,
163
- lastStopAt: null,
164
- lastError: null,
165
- },
166
- buildChannelSummary: ({ snapshot }) => ({
167
- configured: snapshot.configured ?? false,
168
- running: snapshot.running ?? false,
169
- webhookPath: snapshot.webhookPath ?? null,
170
- transport: (snapshot as { transport?: string }).transport ?? null,
171
- ownerId: (snapshot as { ownerId?: string }).ownerId ?? null,
172
- health: (snapshot as { health?: string }).health ?? "idle",
173
- ownerDriftAt: (snapshot as { ownerDriftAt?: number | null }).ownerDriftAt ?? null,
174
- connected: (snapshot as { connected?: boolean }).connected,
175
- authenticated: (snapshot as { authenticated?: boolean }).authenticated,
176
- lastStartAt: snapshot.lastStartAt ?? null,
177
- lastStopAt: snapshot.lastStopAt ?? null,
178
- lastError: snapshot.lastError ?? null,
179
- lastErrorAt: (snapshot as { lastErrorAt?: number | null }).lastErrorAt ?? null,
180
- lastInboundAt: snapshot.lastInboundAt ?? null,
181
- lastOutboundAt: snapshot.lastOutboundAt ?? null,
182
- recentInboundSummary:
183
- (snapshot as { recentInboundSummary?: string | null }).recentInboundSummary ?? null,
184
- recentOutboundSummary:
185
- (snapshot as { recentOutboundSummary?: string | null }).recentOutboundSummary ?? null,
186
- recentIssueCategory:
187
- (snapshot as { recentIssueCategory?: string | null }).recentIssueCategory ?? null,
188
- recentIssueSummary:
189
- (snapshot as { recentIssueSummary?: string | null }).recentIssueSummary ?? null,
190
- transportSessions: (snapshot as { transportSessions?: string[] }).transportSessions ?? [],
191
- probe: snapshot.probe,
192
- lastProbeAt: snapshot.lastProbeAt ?? null,
193
- }),
194
- probeAccount: async () => ({ ok: true }),
195
- buildAccountSnapshot: ({ account, runtime, cfg }) => {
196
- const conflict = resolveWecomAccountConflict({
197
- cfg: cfg as OpenClawConfig,
198
- accountId: account.accountId,
199
- });
200
- return {
201
- accountId: account.accountId,
202
- name: account.name,
203
- enabled: account.enabled,
204
- configured: account.configured && !conflict,
205
- webhookPath: resolveAccountInboundPath(account),
206
- primaryTransport:
207
- account.bot?.primaryTransport ?? (account.agent ? "agent-callback" : null),
208
- transport: (runtime as { transport?: string } | undefined)?.transport ?? null,
209
- ownerId: (runtime as { ownerId?: string } | undefined)?.ownerId ?? null,
210
- health: (runtime as { health?: string } | undefined)?.health ?? "idle",
211
- ownerDriftAt:
212
- (runtime as { ownerDriftAt?: number | null } | undefined)?.ownerDriftAt ?? null,
213
- connected: (runtime as { connected?: boolean } | undefined)?.connected,
214
- authenticated: (runtime as { authenticated?: boolean } | undefined)?.authenticated,
215
- running: runtime?.running ?? false,
216
- lastStartAt: runtime?.lastStartAt ?? null,
217
- lastStopAt: runtime?.lastStopAt ?? null,
218
- lastError: runtime?.lastError ?? conflict?.message ?? null,
219
- lastErrorAt: (runtime as { lastErrorAt?: number | null } | undefined)?.lastErrorAt ?? null,
220
- lastInboundAt: runtime?.lastInboundAt ?? null,
221
- lastOutboundAt: runtime?.lastOutboundAt ?? null,
222
- recentInboundSummary:
223
- (runtime as { recentInboundSummary?: string | null } | undefined)?.recentInboundSummary ??
224
- null,
225
- recentOutboundSummary:
226
- (runtime as { recentOutboundSummary?: string | null } | undefined)
227
- ?.recentOutboundSummary ?? null,
228
- recentIssueCategory:
229
- (runtime as { recentIssueCategory?: string | null } | undefined)?.recentIssueCategory ??
230
- null,
231
- recentIssueSummary:
232
- (runtime as { recentIssueSummary?: string | null } | undefined)?.recentIssueSummary ??
233
- null,
234
- transportSessions:
235
- (runtime as { transportSessions?: string[] } | undefined)?.transportSessions ?? [],
236
- dmPolicy: account.bot?.config.dm?.policy ?? "pairing",
237
- };
238
- },
239
- },
240
- gateway: {
241
- /**
242
- * **startAccount (启动账号)**
243
- *
244
- * WeCom lifecycle is long-running: keep webhook targets active until
245
- * gateway stop/reload aborts the account.
246
- */
247
- startAccount: monitorWecomProvider,
248
- stopAccount: async (ctx) => {
249
- ctx.setStatus({
250
- accountId: ctx.account.accountId,
251
- running: false,
252
- lastStopAt: Date.now(),
253
- });
254
- },
255
- },
256
- };