@yanhaidao/wecom 2.4.120 → 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 (323) hide show
  1. package/README.md +4 -5
  2. package/dist/index.js +68 -0
  3. package/dist/src/accounts.js +20 -0
  4. package/dist/src/agent/handler.js +895 -0
  5. package/dist/src/agent/index.js +5 -0
  6. package/dist/src/app/account-runtime.js +216 -0
  7. package/dist/src/app/bootstrap.js +19 -0
  8. package/dist/src/app/index.js +118 -0
  9. package/dist/src/capability/agent/delivery-service.js +63 -0
  10. package/dist/src/capability/agent/fallback-policy.js +6 -0
  11. package/dist/src/capability/agent/ingress-service.js +33 -0
  12. package/dist/src/capability/agent/upstream-delivery-service.js +71 -0
  13. package/dist/src/capability/bot/dispatch-config.js +45 -0
  14. package/dist/src/capability/bot/fallback-delivery.js +147 -0
  15. package/dist/src/capability/bot/local-path-delivery.js +178 -0
  16. package/dist/src/capability/bot/sandbox-media.js +138 -0
  17. package/dist/src/capability/bot/service.js +49 -0
  18. package/dist/src/capability/bot/stream-delivery.js +321 -0
  19. package/dist/src/capability/bot/stream-finalizer.js +81 -0
  20. package/dist/src/capability/bot/stream-orchestrator.js +318 -0
  21. package/dist/src/capability/bot/types.js +1 -0
  22. package/{src/capability/calendar/client.ts → dist/src/capability/calendar/client.js} +118 -241
  23. package/{src/capability/calendar/schema.ts → dist/src/capability/calendar/schema.js} +0 -38
  24. package/dist/src/capability/calendar/tool.js +365 -0
  25. package/dist/src/capability/calendar/types.js +12 -0
  26. package/{src/capability/doc/client.ts → dist/src/capability/doc/client.js} +370 -605
  27. package/{src/capability/doc/schema.ts → dist/src/capability/doc/schema.js} +345 -394
  28. package/dist/src/capability/doc/tool.js +1556 -0
  29. package/dist/src/capability/doc/types.js +113 -0
  30. package/dist/src/capability/mcp/index.js +3 -0
  31. package/dist/src/capability/mcp/schema.js +102 -0
  32. package/dist/src/capability/mcp/tool.js +146 -0
  33. package/dist/src/capability/mcp/transport.js +293 -0
  34. package/dist/src/channel.js +224 -0
  35. package/dist/src/config/accounts.js +236 -0
  36. package/dist/src/config/derived-paths.js +31 -0
  37. package/dist/src/config/index.js +7 -0
  38. package/dist/src/config/media.js +110 -0
  39. package/dist/src/config/network.js +32 -0
  40. package/dist/src/config/routing.js +20 -0
  41. package/dist/src/config/runtime-config.js +25 -0
  42. package/dist/src/config/schema.js +4 -0
  43. package/{src/config-schema.ts → dist/src/config-schema.js} +1 -1
  44. package/dist/src/context-store.js +219 -0
  45. package/{src/crypto/aes.ts → dist/src/crypto/aes.js} +11 -28
  46. package/dist/src/crypto/index.js +9 -0
  47. package/{src/crypto/signature.ts → dist/src/crypto/signature.js} +3 -18
  48. package/{src/crypto/xml.ts → dist/src/crypto/xml.js} +3 -11
  49. package/dist/src/crypto.js +145 -0
  50. package/dist/src/domain/models.js +1 -0
  51. package/dist/src/domain/policies.js +32 -0
  52. package/{src/dynamic-agent.ts → dist/src/dynamic-agent.js} +36 -73
  53. package/dist/src/gateway-monitor.js +139 -0
  54. package/dist/src/http.js +114 -0
  55. package/{src/media.ts → dist/src/media.js} +21 -40
  56. package/dist/src/monitor/limits.js +7 -0
  57. package/dist/src/monitor/state.js +28 -0
  58. package/dist/src/monitor.js +84 -0
  59. package/dist/src/observability/audit-log.js +30 -0
  60. package/dist/src/observability/legacy-operational-event-store.js +22 -0
  61. package/dist/src/observability/raw-envelope-log.js +24 -0
  62. package/dist/src/observability/status-registry.js +9 -0
  63. package/dist/src/observability/transport-session-view.js +14 -0
  64. package/dist/src/onboarding.js +546 -0
  65. package/dist/src/outbound.js +557 -0
  66. package/dist/src/runtime/dispatcher.js +57 -0
  67. package/{src/runtime/index.ts → dist/src/runtime/index.js} +0 -1
  68. package/dist/src/runtime/outbound-intent.js +1 -0
  69. package/dist/src/runtime/reply-orchestrator.js +38 -0
  70. package/dist/src/runtime/routing-bridge.js +26 -0
  71. package/dist/src/runtime/session-manager.js +112 -0
  72. package/dist/src/runtime/source-registry.js +174 -0
  73. package/dist/src/runtime.js +1 -0
  74. package/dist/src/shared/command-auth.js +57 -0
  75. package/{src/shared/index.ts → dist/src/shared/index.js} +0 -1
  76. package/dist/src/shared/media-asset.js +65 -0
  77. package/dist/src/shared/media-service.js +59 -0
  78. package/dist/src/shared/media-types.js +1 -0
  79. package/{src/shared/xml-parser.ts → dist/src/shared/xml-parser.js} +72 -63
  80. package/dist/src/store/active-reply-store.js +41 -0
  81. package/dist/src/store/interfaces.js +1 -0
  82. package/dist/src/store/memory-store.js +33 -0
  83. package/dist/src/store/stream-batch-store.js +319 -0
  84. package/{src/target.ts → dist/src/target.js} +15 -48
  85. package/dist/src/transport/agent-api/client.js +168 -0
  86. package/dist/src/transport/agent-api/core.js +337 -0
  87. package/dist/src/transport/agent-api/delivery.js +28 -0
  88. package/dist/src/transport/agent-api/media-upload.js +4 -0
  89. package/dist/src/transport/agent-api/reply.js +24 -0
  90. package/dist/src/transport/agent-api/upstream-delivery.js +30 -0
  91. package/dist/src/transport/agent-api/upstream-media-upload.js +46 -0
  92. package/dist/src/transport/agent-api/upstream-reply.js +26 -0
  93. package/dist/src/transport/agent-callback/http-handler.js +30 -0
  94. package/dist/src/transport/agent-callback/inbound.js +4 -0
  95. package/dist/src/transport/agent-callback/reply.js +8 -0
  96. package/dist/src/transport/agent-callback/request-handler.js +189 -0
  97. package/dist/src/transport/agent-callback/session.js +15 -0
  98. package/dist/src/transport/bot-webhook/active-reply.js +27 -0
  99. package/dist/src/transport/bot-webhook/http-handler.js +31 -0
  100. package/dist/src/transport/bot-webhook/inbound-normalizer.js +496 -0
  101. package/dist/src/transport/bot-webhook/inbound.js +4 -0
  102. package/dist/src/transport/bot-webhook/message-shape.js +98 -0
  103. package/dist/src/transport/bot-webhook/protocol.js +124 -0
  104. package/dist/src/transport/bot-webhook/reply.js +9 -0
  105. package/dist/src/transport/bot-webhook/request-handler.js +285 -0
  106. package/dist/src/transport/bot-webhook/session.js +15 -0
  107. package/dist/src/transport/bot-ws/inbound.js +147 -0
  108. package/dist/src/transport/bot-ws/media.js +236 -0
  109. package/dist/src/transport/bot-ws/reply.js +310 -0
  110. package/dist/src/transport/bot-ws/sdk-adapter.js +257 -0
  111. package/dist/src/transport/bot-ws/session.js +15 -0
  112. package/dist/src/transport/http/common.js +78 -0
  113. package/dist/src/transport/http/registry.js +71 -0
  114. package/dist/src/transport/http/request-handler.js +51 -0
  115. package/{src/transport/index.ts → dist/src/transport/index.js} +2 -10
  116. package/dist/src/types/account.js +1 -0
  117. package/dist/src/types/config.js +1 -0
  118. package/dist/src/types/constants.js +28 -0
  119. package/dist/src/types/events.js +1 -0
  120. package/dist/src/types/index.js +1 -0
  121. package/dist/src/types/legacy-stream.js +1 -0
  122. package/dist/src/types/message.js +5 -0
  123. package/dist/src/types/runtime-context.js +1 -0
  124. package/dist/src/types/runtime.js +1 -0
  125. package/dist/src/types.js +1 -0
  126. package/dist/src/upstream/index.js +111 -0
  127. package/dist/src/wecom_msg_adapter/markdown_adapter.js +280 -0
  128. package/openclaw.plugin.json +15 -0
  129. package/package.json +18 -1
  130. package/.github/workflows/release.yml +0 -143
  131. package/GOVERNANCE.md +0 -26
  132. package/MENU_EVENT_CONF.md +0 -500
  133. package/MENU_EVENT_PLAN.md +0 -440
  134. package/SKILLS_CAL.md +0 -895
  135. package/SKILLS_DOC.md +0 -2288
  136. package/UPSTREAM_CONFIG.md +0 -170
  137. package/UPSTREAM_PLAN.md +0 -175
  138. package/assets/01.bot-add.png +0 -0
  139. package/assets/01.bot-setp2.png +0 -0
  140. package/assets/01.image.jpg +0 -0
  141. package/assets/02.agent.add.png +0 -0
  142. package/assets/02.agent.api-set.png +0 -0
  143. package/assets/02.image.jpg +0 -0
  144. package/assets/03.agent.page.png +0 -0
  145. package/assets/03.bot.page.png +0 -0
  146. package/assets/link-me.jpg +0 -0
  147. package/assets/register.png +0 -0
  148. package/changelog/v2.2.28.md +0 -70
  149. package/changelog/v2.3.10.md +0 -17
  150. package/changelog/v2.3.11.md +0 -19
  151. package/changelog/v2.3.12.md +0 -25
  152. package/changelog/v2.3.13.md +0 -19
  153. package/changelog/v2.3.14.md +0 -48
  154. package/changelog/v2.3.15.md +0 -15
  155. package/changelog/v2.3.16.md +0 -11
  156. package/changelog/v2.3.18.md +0 -22
  157. package/changelog/v2.3.19.md +0 -73
  158. package/changelog/v2.3.2.md +0 -28
  159. package/changelog/v2.3.26.md +0 -21
  160. package/changelog/v2.3.27.md +0 -33
  161. package/changelog/v2.3.4.md +0 -20
  162. package/changelog/v2.3.9.md +0 -22
  163. package/changelog/v2.4.12.md +0 -37
  164. package/compat-single-account.md +0 -148
  165. package/index.test.ts +0 -38
  166. package/scripts/test-proxy.ts +0 -70
  167. package/scripts/wecom/README.md +0 -123
  168. package/scripts/wecom/menu-click-help.js +0 -59
  169. package/scripts/wecom/menu-click-help.py +0 -55
  170. package/src/accounts.ts +0 -34
  171. package/src/agent/api-client.upload.test.ts +0 -109
  172. package/src/agent/event-router.test.ts +0 -421
  173. package/src/agent/event-router.ts +0 -272
  174. package/src/agent/handler.event-filter.test.ts +0 -135
  175. package/src/agent/handler.ts +0 -1250
  176. package/src/agent/index.ts +0 -12
  177. package/src/agent/script-runner.ts +0 -186
  178. package/src/agent/test-fixtures/invalid-json-script.mjs +0 -1
  179. package/src/agent/test-fixtures/reply-event-script.mjs +0 -29
  180. package/src/agent/test-fixtures/reply-event-script.py +0 -17
  181. package/src/app/account-runtime.ts +0 -276
  182. package/src/app/bootstrap.ts +0 -29
  183. package/src/app/index.ts +0 -192
  184. package/src/capability/agent/delivery-service.ts +0 -87
  185. package/src/capability/agent/fallback-policy.ts +0 -13
  186. package/src/capability/agent/ingress-service.ts +0 -38
  187. package/src/capability/agent/upstream-delivery-service.ts +0 -96
  188. package/src/capability/bot/dispatch-config.ts +0 -47
  189. package/src/capability/bot/fallback-delivery.ts +0 -178
  190. package/src/capability/bot/local-path-delivery.ts +0 -215
  191. package/src/capability/bot/sandbox-media.test.ts +0 -221
  192. package/src/capability/bot/sandbox-media.ts +0 -176
  193. package/src/capability/bot/service.ts +0 -56
  194. package/src/capability/bot/stream-delivery.ts +0 -379
  195. package/src/capability/bot/stream-finalizer.ts +0 -120
  196. package/src/capability/bot/stream-orchestrator.ts +0 -371
  197. package/src/capability/bot/types.ts +0 -8
  198. package/src/capability/calendar/SKILLS_CHECKLIST.md +0 -251
  199. package/src/capability/calendar/tool.ts +0 -417
  200. package/src/capability/calendar/types.ts +0 -309
  201. package/src/capability/doc/tool.ts +0 -1629
  202. package/src/capability/doc/types.ts +0 -792
  203. package/src/capability/mcp/index.ts +0 -10
  204. package/src/capability/mcp/schema.ts +0 -107
  205. package/src/capability/mcp/tool.ts +0 -174
  206. package/src/capability/mcp/transport.ts +0 -394
  207. package/src/channel.config.test.ts +0 -180
  208. package/src/channel.lifecycle.test.ts +0 -255
  209. package/src/channel.meta.test.ts +0 -26
  210. package/src/channel.ts +0 -256
  211. package/src/config/accounts.resolve.test.ts +0 -75
  212. package/src/config/accounts.ts +0 -312
  213. package/src/config/derived-paths.test.ts +0 -111
  214. package/src/config/derived-paths.ts +0 -41
  215. package/src/config/index.ts +0 -22
  216. package/src/config/media.test.ts +0 -113
  217. package/src/config/media.ts +0 -139
  218. package/src/config/network.ts +0 -20
  219. package/src/config/routing.test.ts +0 -88
  220. package/src/config/routing.ts +0 -26
  221. package/src/config/runtime-config.ts +0 -46
  222. package/src/config/schema.ts +0 -144
  223. package/src/context-store.ts +0 -297
  224. package/src/crypto/index.ts +0 -24
  225. package/src/crypto.test.ts +0 -32
  226. package/src/crypto.ts +0 -176
  227. package/src/domain/models.ts +0 -7
  228. package/src/domain/policies.ts +0 -36
  229. package/src/dynamic-agent.account-scope.test.ts +0 -17
  230. package/src/gateway-monitor.ts +0 -181
  231. package/src/http.ts +0 -137
  232. package/src/media.test.ts +0 -82
  233. package/src/monitor/limits.ts +0 -7
  234. package/src/monitor/state.queue.test.ts +0 -185
  235. package/src/monitor/state.ts +0 -34
  236. package/src/monitor.active.test.ts +0 -245
  237. package/src/monitor.inbound-filter.test.ts +0 -63
  238. package/src/monitor.integration.test.ts +0 -208
  239. package/src/monitor.ts +0 -121
  240. package/src/monitor.webhook.test.ts +0 -774
  241. package/src/observability/audit-log.ts +0 -48
  242. package/src/observability/legacy-operational-event-store.ts +0 -36
  243. package/src/observability/raw-envelope-log.ts +0 -28
  244. package/src/observability/status-registry.ts +0 -13
  245. package/src/observability/transport-session-view.ts +0 -14
  246. package/src/onboarding.test.ts +0 -336
  247. package/src/onboarding.ts +0 -704
  248. package/src/outbound.test.ts +0 -1271
  249. package/src/outbound.ts +0 -746
  250. package/src/runtime/dispatcher.ts +0 -71
  251. package/src/runtime/outbound-intent.ts +0 -4
  252. package/src/runtime/reply-orchestrator.test.ts +0 -71
  253. package/src/runtime/reply-orchestrator.ts +0 -67
  254. package/src/runtime/routing-bridge.test.ts +0 -115
  255. package/src/runtime/routing-bridge.ts +0 -44
  256. package/src/runtime/session-manager.test.ts +0 -174
  257. package/src/runtime/session-manager.ts +0 -139
  258. package/src/runtime/source-registry.ts +0 -249
  259. package/src/runtime.ts +0 -14
  260. package/src/shared/command-auth.ts +0 -87
  261. package/src/shared/media-asset.ts +0 -78
  262. package/src/shared/media-service.test.ts +0 -111
  263. package/src/shared/media-service.ts +0 -84
  264. package/src/shared/media-types.ts +0 -5
  265. package/src/shared/xml-parser.test.ts +0 -50
  266. package/src/store/active-reply-store.ts +0 -42
  267. package/src/store/interfaces.ts +0 -11
  268. package/src/store/memory-store.ts +0 -43
  269. package/src/store/stream-batch-store.ts +0 -350
  270. package/src/transport/agent-api/client.ts +0 -277
  271. package/src/transport/agent-api/core.ts +0 -463
  272. package/src/transport/agent-api/delivery.ts +0 -41
  273. package/src/transport/agent-api/media-upload.ts +0 -11
  274. package/src/transport/agent-api/reply.ts +0 -39
  275. package/src/transport/agent-api/upstream-delivery.ts +0 -45
  276. package/src/transport/agent-api/upstream-media-upload.ts +0 -70
  277. package/src/transport/agent-api/upstream-reply.ts +0 -43
  278. package/src/transport/agent-callback/http-handler.ts +0 -47
  279. package/src/transport/agent-callback/inbound.ts +0 -5
  280. package/src/transport/agent-callback/reply.ts +0 -13
  281. package/src/transport/agent-callback/request-handler.ts +0 -244
  282. package/src/transport/agent-callback/session.ts +0 -23
  283. package/src/transport/bot-webhook/active-reply.ts +0 -39
  284. package/src/transport/bot-webhook/http-handler.ts +0 -48
  285. package/src/transport/bot-webhook/inbound-normalizer.ts +0 -371
  286. package/src/transport/bot-webhook/inbound.ts +0 -5
  287. package/src/transport/bot-webhook/message-shape.ts +0 -89
  288. package/src/transport/bot-webhook/protocol.ts +0 -148
  289. package/src/transport/bot-webhook/reply.ts +0 -15
  290. package/src/transport/bot-webhook/request-handler.ts +0 -394
  291. package/src/transport/bot-webhook/session.ts +0 -23
  292. package/src/transport/bot-ws/inbound.test.ts +0 -96
  293. package/src/transport/bot-ws/inbound.ts +0 -116
  294. package/src/transport/bot-ws/media.test.ts +0 -44
  295. package/src/transport/bot-ws/media.ts +0 -321
  296. package/src/transport/bot-ws/reply.test.ts +0 -450
  297. package/src/transport/bot-ws/reply.ts +0 -365
  298. package/src/transport/bot-ws/sdk-adapter.test.ts +0 -187
  299. package/src/transport/bot-ws/sdk-adapter.ts +0 -314
  300. package/src/transport/bot-ws/session.ts +0 -28
  301. package/src/transport/http/common.ts +0 -109
  302. package/src/transport/http/registry.ts +0 -92
  303. package/src/transport/http/request-handler.ts +0 -84
  304. package/src/types/account.ts +0 -72
  305. package/src/types/config.ts +0 -166
  306. package/src/types/constants.ts +0 -31
  307. package/src/types/events.ts +0 -21
  308. package/src/types/global.d.ts +0 -9
  309. package/src/types/index.ts +0 -17
  310. package/src/types/legacy-stream.ts +0 -50
  311. package/src/types/message.ts +0 -187
  312. package/src/types/runtime-context.ts +0 -28
  313. package/src/types/runtime.ts +0 -165
  314. package/src/types.ts +0 -41
  315. package/src/upstream/index.ts +0 -150
  316. package/src/upstream.test.ts +0 -84
  317. package/src/wecom_msg_adapter/markdown_adapter.ts +0 -331
  318. package/tsconfig.json +0 -22
  319. package/vitest.config.ts +0 -26
  320. /package/{src/capability/agent/index.ts → dist/src/capability/agent/index.js} +0 -0
  321. /package/{src/capability/bot/index.ts → dist/src/capability/bot/index.js} +0 -0
  322. /package/{src/capability/calendar/index.ts → dist/src/capability/calendar/index.js} +0 -0
  323. /package/{src/capability/index.ts → dist/src/capability/index.js} +0 -0
@@ -1,144 +0,0 @@
1
- export interface DmConfig {
2
- policy?: "pairing" | "allowlist" | "open" | "disabled";
3
- allowFrom?: (string | number)[];
4
- }
5
-
6
- export interface AgentEventPolicyConfig {
7
- allowedEventTypes?: string[];
8
- }
9
-
10
- export interface AgentInboundPolicyConfig {
11
- eventEnabled?: boolean;
12
- eventPolicy?: AgentEventPolicyConfig;
13
- }
14
-
15
- export interface AgentEventRouteMatchConfig {
16
- eventType?: string;
17
- changeType?: string;
18
- eventKey?: string;
19
- eventKeyPrefix?: string;
20
- eventKeyPattern?: string;
21
- }
22
-
23
- export interface AgentEventRouteHandlerBuiltinConfig {
24
- type: "builtin";
25
- name?: "echo";
26
- chainToAgent?: boolean;
27
- }
28
-
29
- export interface AgentEventRouteHandlerScriptConfig {
30
- type: "node_script" | "python_script";
31
- entry: string;
32
- timeoutMs?: number;
33
- chainToAgent?: boolean;
34
- }
35
-
36
- export type AgentEventRouteHandlerConfig =
37
- | AgentEventRouteHandlerBuiltinConfig
38
- | AgentEventRouteHandlerScriptConfig;
39
-
40
- export interface AgentEventRouteConfig {
41
- id?: string;
42
- when?: AgentEventRouteMatchConfig;
43
- handler: AgentEventRouteHandlerConfig;
44
- }
45
-
46
- export interface AgentEventRoutingConfig {
47
- unmatchedAction?: "ignore" | "forwardToAgent";
48
- routes?: AgentEventRouteConfig[];
49
- }
50
-
51
- export interface AgentScriptRuntimeConfig {
52
- enabled?: boolean;
53
- allowPaths?: string[];
54
- maxStdoutBytes?: number;
55
- maxStderrBytes?: number;
56
- defaultTimeoutMs?: number;
57
- pythonCommand?: string;
58
- nodeCommand?: string;
59
- }
60
-
61
- export interface MediaConfig {
62
- tempDir?: string;
63
- retentionHours?: number;
64
- cleanupOnStart?: boolean;
65
- maxBytes?: number;
66
- localRoots?: string[];
67
- }
68
-
69
- export interface NetworkConfig {
70
- egressProxyUrl?: string;
71
- }
72
-
73
- export interface RoutingConfig {
74
- failClosedOnDefaultRoute?: boolean;
75
- }
76
-
77
- export interface BotWsConfig {
78
- botId: string;
79
- secret: string;
80
- }
81
-
82
- export interface BotWebhookConfig {
83
- token: string;
84
- encodingAESKey: string;
85
- receiveId?: string;
86
- }
87
-
88
- export interface BotConfig {
89
- primaryTransport?: "ws" | "webhook";
90
- streamPlaceholderContent?: string;
91
- welcomeText?: string;
92
- dm?: DmConfig;
93
- aibotid?: string;
94
- botIds?: string[];
95
- ws?: BotWsConfig;
96
- webhook?: BotWebhookConfig;
97
- }
98
-
99
- export interface AgentConfig {
100
- corpId: string;
101
- agentSecret?: string;
102
- corpSecret?: string;
103
- agentId?: number | string;
104
- token: string;
105
- encodingAESKey: string;
106
- welcomeText?: string;
107
- dm?: DmConfig;
108
- inboundPolicy?: AgentInboundPolicyConfig;
109
- eventRouting?: AgentEventRoutingConfig;
110
- scriptRuntime?: AgentScriptRuntimeConfig;
111
- }
112
-
113
- export interface DynamicAgentsConfig {
114
- enabled?: boolean;
115
- dmCreateAgent?: boolean;
116
- groupEnabled?: boolean;
117
- adminUsers?: string[];
118
- }
119
-
120
- export interface AccountConfig {
121
- enabled?: boolean;
122
- name?: string;
123
- mediaMaxMb?: number;
124
- bot?: BotConfig;
125
- agent?: AgentConfig;
126
- }
127
-
128
- export interface WecomConfigInput {
129
- enabled?: boolean;
130
- mediaMaxMb?: number;
131
- bot?: BotConfig;
132
- agent?: AgentConfig;
133
- accounts?: Record<string, AccountConfig>;
134
- defaultAccount?: string;
135
- media?: MediaConfig;
136
- network?: NetworkConfig;
137
- routing?: RoutingConfig;
138
- dynamicAgents?: DynamicAgentsConfig;
139
- }
140
-
141
- /**
142
- * @deprecated No longer a Zod schema. Kept as a type-only export for backward compatibility.
143
- */
144
- export const WecomConfigSchema = undefined;
@@ -1,297 +0,0 @@
1
- /**
2
- * Context store for WeCom Bot WS proactive push.
3
- *
4
- * Similar to Weixin's contextToken mechanism, we need to track:
5
- * - Which accountId has active sessions with which peerId
6
- * - The contextToken for routing outbound messages
7
- */
8
-
9
- import { randomUUID } from "node:crypto";
10
- import fs from "node:fs";
11
- import path from "node:path";
12
-
13
- // Simple logger
14
- const logger = {
15
- info: (...args: unknown[]) => console.log('[wecom-context]', ...args),
16
- warn: (...args: unknown[]) => console.warn('[wecom-context]', ...args),
17
- debug: (...args: unknown[]) => process.env.DEBUG && console.log('[wecom-context]', ...args),
18
- };
19
-
20
- type PeerKind = "direct" | "group";
21
-
22
- type StoredPeerContext = {
23
- contextToken: string;
24
- peerKind: PeerKind;
25
- lastSeen: number;
26
- upstreamCorpId?: string;
27
- };
28
-
29
- type ResolvedPeerContext = StoredPeerContext & {
30
- accountId: string;
31
- peerId: string;
32
- };
33
-
34
- // In-memory store: accountId -> peerId -> context info
35
- const peerContextStore = new Map<string, Map<string, StoredPeerContext>>();
36
-
37
- // Reverse lookup: peerId -> accountId (for routing outbound)
38
- const peerToAccountMap = new Map<string, string>();
39
- const contextTokenToPeerMap = new Map<string, ResolvedPeerContext>();
40
-
41
- function resolveStateDir(): string {
42
- return process.env.OPENCLAW_STATE_DIR || path.join(process.env.HOME || "/tmp", ".openclaw");
43
- }
44
-
45
- function resolveContextFilePath(accountId: string): string {
46
- return path.join(
47
- resolveStateDir(),
48
- "wecom",
49
- "context",
50
- `${accountId}.json`
51
- );
52
- }
53
-
54
- /** Persist peer contexts for an account to disk */
55
- function persistContexts(accountId: string): void {
56
- const peerMap = peerContextStore.get(accountId);
57
- if (!peerMap) return;
58
-
59
- const data: Record<string, StoredPeerContext> = {};
60
- for (const [peerId, info] of peerMap) {
61
- data[peerId] = info;
62
- }
63
-
64
- const filePath = resolveContextFilePath(accountId);
65
- try {
66
- const dir = path.dirname(filePath);
67
- fs.mkdirSync(dir, { recursive: true });
68
- fs.writeFileSync(filePath, JSON.stringify(data, null, 0), "utf-8");
69
- } catch (err) {
70
- logger.warn?.(`persistContexts: failed to write ${filePath}: ${String(err)}`);
71
- }
72
- }
73
-
74
- function normalizeContextToken(value: unknown): string | undefined {
75
- const token = typeof value === "string" ? value.trim() : "";
76
- return token || undefined;
77
- }
78
-
79
- function normalizePeerKind(value: unknown): PeerKind {
80
- return value === "group" ? "group" : "direct";
81
- }
82
-
83
- function normalizeOptional(value: unknown): string | undefined {
84
- const normalized = typeof value === "string" ? value.trim() : "";
85
- return normalized || undefined;
86
- }
87
-
88
- function findStoredPeerContext(accountId: string, peerId: string): StoredPeerContext | undefined {
89
- const peerMap = peerContextStore.get(accountId);
90
- if (!peerMap) return undefined;
91
-
92
- const exact = peerMap.get(peerId);
93
- if (exact) return exact;
94
-
95
- const normalizedPeerId = peerId.trim().toLowerCase();
96
- for (const [storedPeerId, info] of peerMap) {
97
- if (storedPeerId.trim().toLowerCase() === normalizedPeerId) {
98
- return info;
99
- }
100
- }
101
-
102
- return undefined;
103
- }
104
-
105
- function registerPeerContext(accountId: string, peerId: string, info: StoredPeerContext): void {
106
- let peerMap = peerContextStore.get(accountId);
107
- if (!peerMap) {
108
- peerMap = new Map();
109
- peerContextStore.set(accountId, peerMap);
110
- }
111
-
112
- const previous = peerMap.get(peerId);
113
- if (previous?.contextToken && previous.contextToken !== info.contextToken) {
114
- contextTokenToPeerMap.delete(previous.contextToken);
115
- }
116
-
117
- peerMap.set(peerId, info);
118
- peerToAccountMap.set(peerId, accountId);
119
- contextTokenToPeerMap.set(info.contextToken, {
120
- accountId,
121
- peerId,
122
- ...info,
123
- });
124
- }
125
-
126
- function resolveStoredPeerContext(
127
- accountId: string,
128
- peerId: string,
129
- params: {
130
- contextToken?: string;
131
- peerKind?: PeerKind;
132
- lastSeen?: number;
133
- upstreamCorpId?: string;
134
- },
135
- ): StoredPeerContext {
136
- const existing = findStoredPeerContext(accountId, peerId);
137
- return {
138
- contextToken:
139
- normalizeContextToken(params.contextToken) ??
140
- existing?.contextToken ??
141
- randomUUID(),
142
- peerKind: params.peerKind ?? existing?.peerKind ?? "direct",
143
- lastSeen: params.lastSeen ?? Date.now(),
144
- ...(normalizeOptional(params.upstreamCorpId) || existing?.upstreamCorpId
145
- ? { upstreamCorpId: normalizeOptional(params.upstreamCorpId) ?? existing?.upstreamCorpId }
146
- : {}),
147
- };
148
- }
149
-
150
- /** Restore persisted peer contexts for an account */
151
- export function restorePeerContexts(accountId: string): void {
152
- const filePath = resolveContextFilePath(accountId);
153
- try {
154
- if (!fs.existsSync(filePath)) return;
155
- const raw = fs.readFileSync(filePath, "utf-8");
156
- const data = JSON.parse(raw) as Record<
157
- string,
158
- {
159
- contextToken?: string;
160
- peerKind?: string;
161
- lastSeen?: number;
162
- upstreamCorpId?: string;
163
- }
164
- >;
165
-
166
- const peerMap = new Map<string, StoredPeerContext>();
167
- let count = 0;
168
- let mutated = false;
169
- for (const [peerId, info] of Object.entries(data)) {
170
- const normalized: StoredPeerContext = {
171
- contextToken: normalizeContextToken(info?.contextToken) ?? randomUUID(),
172
- peerKind: normalizePeerKind(info?.peerKind),
173
- lastSeen:
174
- typeof info?.lastSeen === "number" && Number.isFinite(info.lastSeen)
175
- ? info.lastSeen
176
- : Date.now(),
177
- ...(normalizeOptional(info?.upstreamCorpId)
178
- ? { upstreamCorpId: normalizeOptional(info?.upstreamCorpId) }
179
- : {}),
180
- };
181
- peerMap.set(peerId, normalized);
182
- peerToAccountMap.set(peerId, accountId);
183
- contextTokenToPeerMap.set(normalized.contextToken, {
184
- accountId,
185
- peerId,
186
- ...normalized,
187
- });
188
- if (
189
- normalized.contextToken !== info?.contextToken ||
190
- normalized.peerKind !== info?.peerKind ||
191
- normalized.lastSeen !== info?.lastSeen ||
192
- normalized.upstreamCorpId !== normalizeOptional(info?.upstreamCorpId)
193
- ) {
194
- mutated = true;
195
- }
196
- count++;
197
- }
198
- peerContextStore.set(accountId, peerMap);
199
- if (mutated) {
200
- persistContexts(accountId);
201
- }
202
- logger.info?.(`restorePeerContexts: restored ${count} peers for account=${accountId}`);
203
- } catch (err) {
204
- logger.warn?.(`restorePeerContexts: failed to read ${filePath}: ${String(err)}`);
205
- }
206
- }
207
-
208
- /** Store context for a peer (called on inbound message) */
209
- export function setPeerContext(
210
- accountId: string,
211
- peerId: string,
212
- options?: {
213
- contextToken?: string;
214
- peerKind?: PeerKind;
215
- lastSeen?: number;
216
- upstreamCorpId?: string;
217
- },
218
- ): string {
219
- const resolved = resolveStoredPeerContext(accountId, peerId, options ?? {});
220
- registerPeerContext(accountId, peerId, resolved);
221
-
222
- // Persist to disk (debounced would be better, but simple for now)
223
- persistContexts(accountId);
224
-
225
- logger.debug?.(
226
- `setPeerContext: accountId=${accountId} peerId=${peerId} token=${resolved.contextToken} kind=${resolved.peerKind}`,
227
- );
228
- return resolved.contextToken;
229
- }
230
-
231
- /** Get the accountId that has an active session with a peer */
232
- export function getAccountIdByPeer(peerId: string): string | undefined {
233
- return peerToAccountMap.get(peerId);
234
- }
235
-
236
- /** Get the most recent peerId for an account (for proactive push) */
237
- export function getRecentPeerForAccount(accountId: string, maxAgeMs = 30 * 60 * 1000): string | undefined {
238
- const peerMap = peerContextStore.get(accountId);
239
- if (!peerMap) return undefined;
240
-
241
- let mostRecent: { peerId: string; lastSeen: number } | undefined;
242
-
243
- for (const [peerId, info] of peerMap) {
244
- if (Date.now() - info.lastSeen > maxAgeMs) continue;
245
- if (!mostRecent || info.lastSeen > mostRecent.lastSeen) {
246
- mostRecent = { peerId, lastSeen: info.lastSeen };
247
- }
248
- }
249
-
250
- return mostRecent?.peerId;
251
- }
252
-
253
- /** Get context token for a peer */
254
- export function getPeerContextToken(accountId: string, peerId: string): string | undefined {
255
- return findStoredPeerContext(accountId, peerId)?.contextToken;
256
- }
257
-
258
- export function getPeerUpstreamCorpId(accountId: string, peerId: string): string | undefined {
259
- return findStoredPeerContext(accountId, peerId)?.upstreamCorpId;
260
- }
261
-
262
- /** Resolve a peer context from a context token. */
263
- export function getPeerContextByToken(contextToken: string): ResolvedPeerContext | undefined {
264
- return contextTokenToPeerMap.get(contextToken);
265
- }
266
-
267
- /** Resolve accountId from a context token. */
268
- export function getAccountIdByContextToken(contextToken: string): string | undefined {
269
- return contextTokenToPeerMap.get(contextToken)?.accountId;
270
- }
271
-
272
- /** Check if we have an active session for routing */
273
- export function hasActiveSession(accountId: string, peerId: string, maxAgeMs = 30 * 60 * 1000): boolean {
274
- const info = findStoredPeerContext(accountId, peerId);
275
- if (!info) return false;
276
-
277
- return Date.now() - info.lastSeen < maxAgeMs;
278
- }
279
-
280
- /** Clear all contexts for an account */
281
- export function clearPeerContexts(accountId: string): void {
282
- const peerMap = peerContextStore.get(accountId);
283
- if (peerMap) {
284
- for (const [peerId, info] of peerMap) {
285
- peerToAccountMap.delete(peerId);
286
- contextTokenToPeerMap.delete(info.contextToken);
287
- }
288
- }
289
- peerContextStore.delete(accountId);
290
-
291
- const filePath = resolveContextFilePath(accountId);
292
- try {
293
- if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
294
- } catch (err) {
295
- logger.warn?.(`clearPeerContexts: failed to remove ${filePath}`);
296
- }
297
- }
@@ -1,24 +0,0 @@
1
- /**
2
- * WeCom 加解密模块导出
3
- */
4
-
5
- // AES 加解密
6
- export {
7
- decodeEncodingAESKey,
8
- pkcs7Unpad,
9
- decryptWecomEncrypted,
10
- encryptWecomPlaintext,
11
- } from "./aes.js";
12
-
13
- // 签名验证
14
- export {
15
- computeWecomMsgSignature,
16
- verifyWecomSignature,
17
- } from "./signature.js";
18
-
19
- // XML 辅助
20
- export {
21
- extractEncryptFromXml,
22
- extractToUserNameFromXml,
23
- buildEncryptedXmlResponse,
24
- } from "./xml.js";
@@ -1,32 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
-
3
- import { computeWecomMsgSignature, decryptWecomEncrypted, encryptWecomPlaintext } from "./crypto.js";
4
-
5
- describe("wecom crypto", () => {
6
- it("round-trips plaintext", () => {
7
- const encodingAESKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"; // 43 chars base64 (plus '=' padding)
8
- const plaintext = JSON.stringify({ hello: "world" });
9
- const encrypt = encryptWecomPlaintext({ encodingAESKey, receiveId: "", plaintext });
10
- const decrypted = decryptWecomEncrypted({ encodingAESKey, receiveId: "", encrypt });
11
- expect(decrypted).toBe(plaintext);
12
- });
13
-
14
- it("pads correctly when raw length is a multiple of 32", () => {
15
- const encodingAESKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
16
- // raw length = 20 + plaintext.length + receiveId.length; choose plaintext length % 32 === 12
17
- const plaintext = "x".repeat(12);
18
- const encrypt = encryptWecomPlaintext({ encodingAESKey, receiveId: "", plaintext });
19
- const decrypted = decryptWecomEncrypted({ encodingAESKey, receiveId: "", encrypt });
20
- expect(decrypted).toBe(plaintext);
21
- });
22
-
23
- it("computes sha1 msg signature", () => {
24
- const sig = computeWecomMsgSignature({
25
- token: "token",
26
- timestamp: "123",
27
- nonce: "456",
28
- encrypt: "ENCRYPT",
29
- });
30
- expect(sig).toMatch(/^[a-f0-9]{40}$/);
31
- });
32
- });
package/src/crypto.ts DELETED
@@ -1,176 +0,0 @@
1
- import crypto from "node:crypto";
2
-
3
- /**
4
- * **decodeEncodingAESKey (解码 AES Key)**
5
- *
6
- * 将企业微信配置的 Base64 编码的 AES Key 解码为 Buffer。
7
- * 包含补全 Padding 和长度校验 (必须32字节)。
8
- */
9
- export function decodeEncodingAESKey(encodingAESKey: string): Buffer {
10
- const trimmed = encodingAESKey.trim();
11
- if (!trimmed) throw new Error("encodingAESKey missing");
12
- const withPadding = trimmed.endsWith("=") ? trimmed : `${trimmed}=`;
13
- const key = Buffer.from(withPadding, "base64");
14
- if (key.length !== 32) {
15
- throw new Error(`invalid encodingAESKey (expected 32 bytes after base64 decode, got ${key.length})`);
16
- }
17
- return key;
18
- }
19
-
20
- // WeCom uses PKCS#7 padding with a block size of 32 bytes (not AES's 16-byte block).
21
- // This is compatible with AES-CBC as 32 is a multiple of 16, but it requires manual padding/unpadding.
22
- export const WECOM_PKCS7_BLOCK_SIZE = 32;
23
-
24
- function pkcs7Pad(buf: Buffer, blockSize: number): Buffer {
25
- const mod = buf.length % blockSize;
26
- const pad = mod === 0 ? blockSize : blockSize - mod;
27
- const padByte = Buffer.from([pad]);
28
- return Buffer.concat([buf, Buffer.alloc(pad, padByte[0]!)]);
29
- }
30
-
31
- /**
32
- * **pkcs7Unpad (去除 PKCS#7 填充)**
33
- *
34
- * 移除 AES 解密后的 PKCS#7 填充字节。
35
- * 包含填充合法性校验。
36
- */
37
- export function pkcs7Unpad(buf: Buffer, blockSize: number): Buffer {
38
- if (buf.length === 0) throw new Error("invalid pkcs7 payload");
39
- const pad = buf[buf.length - 1]!;
40
- if (pad < 1 || pad > blockSize) {
41
- throw new Error("invalid pkcs7 padding");
42
- }
43
- if (pad > buf.length) {
44
- throw new Error("invalid pkcs7 payload");
45
- }
46
- // Best-effort validation (all padding bytes equal).
47
- for (let i = 0; i < pad; i += 1) {
48
- if (buf[buf.length - 1 - i] !== pad) {
49
- throw new Error("invalid pkcs7 padding");
50
- }
51
- }
52
- return buf.subarray(0, buf.length - pad);
53
- }
54
-
55
- function sha1Hex(input: string): string {
56
- return crypto.createHash("sha1").update(input).digest("hex");
57
- }
58
-
59
- /**
60
- * **computeWecomMsgSignature (计算消息签名)**
61
- *
62
- * 算法:sha1(sort(token, timestamp, nonce, encrypt_msg))
63
- */
64
- export function computeWecomMsgSignature(params: {
65
- token: string;
66
- timestamp: string;
67
- nonce: string;
68
- encrypt: string;
69
- }): string {
70
- const parts = [params.token, params.timestamp, params.nonce, params.encrypt]
71
- .map((v) => String(v ?? ""))
72
- .sort();
73
- return sha1Hex(parts.join(""));
74
- }
75
-
76
- /**
77
- * **verifyWecomSignature (验证消息签名)**
78
- *
79
- * 比较计算出的签名与企业微信传入的签名是否一致。
80
- */
81
- export function verifyWecomSignature(params: {
82
- token: string;
83
- timestamp: string;
84
- nonce: string;
85
- encrypt: string;
86
- signature: string;
87
- }): boolean {
88
- const expected = computeWecomMsgSignature({
89
- token: params.token,
90
- timestamp: params.timestamp,
91
- nonce: params.nonce,
92
- encrypt: params.encrypt,
93
- });
94
- return expected === params.signature;
95
- }
96
-
97
- /**
98
- * **decryptWecomEncrypted (解密企业微信消息)**
99
- *
100
- * 将企业微信的 AES 加密包解密为明文。
101
- * 流程:
102
- * 1. Base64 解码 AESKey 并获取 IV (前16字节)。
103
- * 2. AES-CBC 解密。
104
- * 3. 去除 PKCS#7 填充。
105
- * 4. 拆解协议包结构: [16字节随机串][4字节长度][消息体][接收者ID]。
106
- * 5. 校验接收者ID (ReceiveId)。
107
- */
108
- export function decryptWecomEncrypted(params: {
109
- encodingAESKey: string;
110
- receiveId?: string;
111
- encrypt: string;
112
- }): string {
113
- const aesKey = decodeEncodingAESKey(params.encodingAESKey);
114
- const iv = aesKey.subarray(0, 16);
115
- const decipher = crypto.createDecipheriv("aes-256-cbc", aesKey, iv);
116
- decipher.setAutoPadding(false);
117
- const decryptedPadded = Buffer.concat([
118
- decipher.update(Buffer.from(params.encrypt, "base64")),
119
- decipher.final(),
120
- ]);
121
- const decrypted = pkcs7Unpad(decryptedPadded, WECOM_PKCS7_BLOCK_SIZE);
122
-
123
- if (decrypted.length < 20) {
124
- throw new Error(`invalid decrypted payload (expected at least 20 bytes, got ${decrypted.length})`);
125
- }
126
-
127
- // 16 bytes random + 4 bytes network-order length + msg + receiveId (optional)
128
- const msgLen = decrypted.readUInt32BE(16);
129
- const msgStart = 20;
130
- const msgEnd = msgStart + msgLen;
131
- if (msgEnd > decrypted.length) {
132
- throw new Error(`invalid decrypted msg length (msgEnd=${msgEnd}, payloadLength=${decrypted.length})`);
133
- }
134
- const msg = decrypted.subarray(msgStart, msgEnd).toString("utf8");
135
-
136
- const receiveId = params.receiveId ?? "";
137
- if (receiveId) {
138
- const trailing = decrypted.subarray(msgEnd).toString("utf8");
139
- if (trailing !== receiveId) {
140
- throw new Error(`receiveId mismatch (expected "${receiveId}", got "${trailing}")`);
141
- }
142
- }
143
-
144
- return msg;
145
- }
146
-
147
- /**
148
- * **encryptWecomPlaintext (加密回复消息)**
149
- *
150
- * 将明文消息打包为企业微信的加密格式。
151
- * 流程:
152
- * 1. 构造协议包: [16字节随机串][4字节长度][消息体][接收者ID]。
153
- * 2. PKCS#7 填充。
154
- * 3. AES-CBC 加密。
155
- * 4. 转 Base64。
156
- */
157
- export function encryptWecomPlaintext(params: {
158
- encodingAESKey: string;
159
- receiveId?: string;
160
- plaintext: string;
161
- }): string {
162
- const aesKey = decodeEncodingAESKey(params.encodingAESKey);
163
- const iv = aesKey.subarray(0, 16);
164
- const random16 = crypto.randomBytes(16);
165
- const msg = Buffer.from(params.plaintext ?? "", "utf8");
166
- const msgLen = Buffer.alloc(4);
167
- msgLen.writeUInt32BE(msg.length, 0);
168
- const receiveId = Buffer.from(params.receiveId ?? "", "utf8");
169
-
170
- const raw = Buffer.concat([random16, msgLen, msg, receiveId]);
171
- const padded = pkcs7Pad(raw, WECOM_PKCS7_BLOCK_SIZE);
172
- const cipher = crypto.createCipheriv("aes-256-cbc", aesKey, iv);
173
- cipher.setAutoPadding(false);
174
- const encrypted = Buffer.concat([cipher.update(padded), cipher.final()]);
175
- return encrypted.toString("base64");
176
- }
@@ -1,7 +0,0 @@
1
- import type { DeliveryTask, RawFrameReference, ReplyContext, TransportSessionSnapshot, UnifiedInboundEvent } from "../types/index.js";
2
-
3
- export type WecomConversation = UnifiedInboundEvent["conversation"];
4
- export type WecomRawEnvelope = RawFrameReference;
5
- export type WecomReplyContext = ReplyContext;
6
- export type WecomTransportSession = TransportSessionSnapshot;
7
- export type WecomDeliveryTask = DeliveryTask;