@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,249 +0,0 @@
1
- export type WecomSourcePlane = "bot-ws" | "agent-callback";
2
-
3
- export type WecomSourceSnapshot = {
4
- accountId: string;
5
- source: WecomSourcePlane;
6
- recordedAt: number;
7
- messageId?: string;
8
- sessionKey?: string;
9
- sessionId?: string;
10
- peerKind?: "direct" | "group";
11
- peerId?: string;
12
- upstreamCorpId?: string;
13
- };
14
-
15
- const MAX_MESSAGE_FACTS = 2048;
16
- const MAX_SESSION_SNAPSHOTS = 1024;
17
- const MAX_CONVERSATION_SNAPSHOTS = 1024;
18
-
19
- const messageFacts = new Map<string, WecomSourceSnapshot>();
20
- const sessionSnapshotsByAccountKey = new Map<string, WecomSourceSnapshot>();
21
- const sessionSnapshotsByLooseKey = new Map<string, WecomSourceSnapshot>();
22
- const conversationSnapshotsByAccountKey = new Map<string, WecomSourceSnapshot>();
23
- const conversationSnapshotsByLooseKey = new Map<string, WecomSourceSnapshot>();
24
-
25
- function normalizeOptional(value: string | null | undefined): string | undefined {
26
- const trimmed = String(value ?? "").trim();
27
- return trimmed || undefined;
28
- }
29
-
30
- function messageFactKey(accountId: string, messageId: string): string {
31
- return `${accountId}::${messageId}`;
32
- }
33
-
34
- function accountScopedSessionKey(
35
- accountId: string,
36
- kind: "sessionKey" | "sessionId",
37
- value: string,
38
- ): string {
39
- return `${accountId}::${kind}::${value}`;
40
- }
41
-
42
- function normalizePeerId(value: string | null | undefined): string | undefined {
43
- const trimmed = String(value ?? "").trim();
44
- return trimmed ? trimmed.toLowerCase() : undefined;
45
- }
46
-
47
- function normalizePeerKind(value: string | null | undefined): "direct" | "group" | undefined {
48
- const trimmed = String(value ?? "").trim().toLowerCase();
49
- return trimmed === "direct" || trimmed === "group" ? trimmed : undefined;
50
- }
51
-
52
- function accountScopedConversationKey(
53
- accountId: string,
54
- peerKind: "direct" | "group",
55
- peerId: string,
56
- ): string {
57
- return `${accountId}::peer::${peerKind}::${peerId}`;
58
- }
59
-
60
- function pruneOldest<T>(map: Map<string, T>, maxSize: number): void {
61
- while (map.size > maxSize) {
62
- const oldestKey = map.keys().next().value;
63
- if (!oldestKey) return;
64
- map.delete(oldestKey);
65
- }
66
- }
67
-
68
- function writeSessionSnapshot(snapshot: WecomSourceSnapshot): void {
69
- const sessionKey = normalizeOptional(snapshot.sessionKey);
70
- const sessionId = normalizeOptional(snapshot.sessionId);
71
- if (sessionKey) {
72
- sessionSnapshotsByAccountKey.set(
73
- accountScopedSessionKey(snapshot.accountId, "sessionKey", sessionKey),
74
- snapshot,
75
- );
76
- sessionSnapshotsByLooseKey.set(`sessionKey::${sessionKey}`, snapshot);
77
- }
78
- if (sessionId) {
79
- sessionSnapshotsByAccountKey.set(
80
- accountScopedSessionKey(snapshot.accountId, "sessionId", sessionId),
81
- snapshot,
82
- );
83
- sessionSnapshotsByLooseKey.set(`sessionId::${sessionId}`, snapshot);
84
- }
85
- pruneOldest(sessionSnapshotsByAccountKey, MAX_SESSION_SNAPSHOTS * 2);
86
- pruneOldest(sessionSnapshotsByLooseKey, MAX_SESSION_SNAPSHOTS * 2);
87
- }
88
-
89
- function writeConversationSnapshot(snapshot: WecomSourceSnapshot): void {
90
- const peerKind = normalizePeerKind(snapshot.peerKind);
91
- const peerId = normalizePeerId(snapshot.peerId);
92
- if (!peerKind || !peerId) {
93
- return;
94
- }
95
- conversationSnapshotsByAccountKey.set(
96
- accountScopedConversationKey(snapshot.accountId, peerKind, peerId),
97
- {
98
- ...snapshot,
99
- peerKind,
100
- peerId,
101
- },
102
- );
103
- conversationSnapshotsByLooseKey.set(`peer::${peerKind}::${peerId}`, {
104
- ...snapshot,
105
- peerKind,
106
- peerId,
107
- });
108
- pruneOldest(conversationSnapshotsByAccountKey, MAX_CONVERSATION_SNAPSHOTS);
109
- pruneOldest(conversationSnapshotsByLooseKey, MAX_CONVERSATION_SNAPSHOTS);
110
- }
111
-
112
- export function registerWecomSourceSnapshot(params: {
113
- accountId: string;
114
- source: WecomSourcePlane;
115
- messageId?: string | null;
116
- sessionKey?: string | null;
117
- sessionId?: string | null;
118
- peerKind?: "direct" | "group" | null;
119
- peerId?: string | null;
120
- upstreamCorpId?: string | null;
121
- }): void {
122
- const accountId = normalizeOptional(params.accountId);
123
- if (!accountId) return;
124
-
125
- const snapshot: WecomSourceSnapshot = {
126
- accountId,
127
- source: params.source,
128
- recordedAt: Date.now(),
129
- ...(normalizeOptional(params.messageId)
130
- ? { messageId: normalizeOptional(params.messageId) }
131
- : {}),
132
- ...(normalizeOptional(params.sessionKey)
133
- ? { sessionKey: normalizeOptional(params.sessionKey) }
134
- : {}),
135
- ...(normalizeOptional(params.sessionId)
136
- ? { sessionId: normalizeOptional(params.sessionId) }
137
- : {}),
138
- ...(normalizePeerKind(params.peerKind) ? { peerKind: normalizePeerKind(params.peerKind) } : {}),
139
- ...(normalizePeerId(params.peerId) ? { peerId: normalizePeerId(params.peerId) } : {}),
140
- ...(normalizeOptional(params.upstreamCorpId)
141
- ? { upstreamCorpId: normalizeOptional(params.upstreamCorpId) }
142
- : {}),
143
- };
144
-
145
- if (snapshot.messageId) {
146
- messageFacts.set(messageFactKey(accountId, snapshot.messageId), snapshot);
147
- pruneOldest(messageFacts, MAX_MESSAGE_FACTS);
148
- }
149
-
150
- writeSessionSnapshot(snapshot);
151
- writeConversationSnapshot(snapshot);
152
- }
153
-
154
- export function resolveWecomSourceSnapshot(params: {
155
- accountId?: string | null;
156
- sessionKey?: string | null;
157
- sessionId?: string | null;
158
- peerKind?: "direct" | "group" | null;
159
- peerId?: string | null;
160
- }): WecomSourceSnapshot | undefined {
161
- const accountId = normalizeOptional(params.accountId);
162
- const sessionKey = normalizeOptional(params.sessionKey);
163
- const sessionId = normalizeOptional(params.sessionId);
164
- const peerKind = normalizePeerKind(params.peerKind);
165
- const peerId = normalizePeerId(params.peerId);
166
-
167
- if (accountId && sessionKey) {
168
- const scoped = sessionSnapshotsByAccountKey.get(
169
- accountScopedSessionKey(accountId, "sessionKey", sessionKey),
170
- );
171
- if (scoped) return scoped;
172
- }
173
- if (accountId && sessionId) {
174
- const scoped = sessionSnapshotsByAccountKey.get(
175
- accountScopedSessionKey(accountId, "sessionId", sessionId),
176
- );
177
- if (scoped) return scoped;
178
- }
179
- if (sessionKey) {
180
- const loose = sessionSnapshotsByLooseKey.get(`sessionKey::${sessionKey}`);
181
- if (loose) return loose;
182
- }
183
- if (sessionId) {
184
- const loose = sessionSnapshotsByLooseKey.get(`sessionId::${sessionId}`);
185
- if (loose) return loose;
186
- }
187
- if (accountId && peerKind && peerId) {
188
- const scoped = conversationSnapshotsByAccountKey.get(
189
- accountScopedConversationKey(accountId, peerKind, peerId),
190
- );
191
- if (scoped) return scoped;
192
- }
193
- if (peerKind && peerId) {
194
- const loose = conversationSnapshotsByLooseKey.get(`peer::${peerKind}::${peerId}`);
195
- if (loose) return loose;
196
- }
197
- return undefined;
198
- }
199
-
200
- export function clearWecomSourceAccount(accountId: string): void {
201
- const normalized = normalizeOptional(accountId);
202
- if (!normalized) return;
203
-
204
- for (const [key, value] of messageFacts) {
205
- if (value.accountId === normalized || key.startsWith(`${normalized}::`)) {
206
- messageFacts.delete(key);
207
- }
208
- }
209
- for (const [key, value] of sessionSnapshotsByAccountKey) {
210
- if (value.accountId === normalized || key.startsWith(`${normalized}::`)) {
211
- sessionSnapshotsByAccountKey.delete(key);
212
- }
213
- }
214
- for (const [key, value] of sessionSnapshotsByLooseKey) {
215
- if (value.accountId === normalized) {
216
- sessionSnapshotsByLooseKey.delete(key);
217
- }
218
- }
219
- for (const [key, value] of conversationSnapshotsByAccountKey) {
220
- if (value.accountId === normalized || key.startsWith(`${normalized}::`)) {
221
- conversationSnapshotsByAccountKey.delete(key);
222
- }
223
- }
224
- for (const [key, value] of conversationSnapshotsByLooseKey) {
225
- if (value.accountId === normalized) {
226
- conversationSnapshotsByLooseKey.delete(key);
227
- }
228
- }
229
- }
230
-
231
- export function isWecomBotWsSource(params: {
232
- accountId?: string | null;
233
- sessionKey?: string | null;
234
- sessionId?: string | null;
235
- peerKind?: "direct" | "group" | null;
236
- peerId?: string | null;
237
- }): boolean {
238
- return resolveWecomSourceSnapshot(params)?.source === "bot-ws";
239
- }
240
-
241
- export function isWecomAgentSource(params: {
242
- accountId?: string | null;
243
- sessionKey?: string | null;
244
- sessionId?: string | null;
245
- peerKind?: "direct" | "group" | null;
246
- peerId?: string | null;
247
- }): boolean {
248
- return resolveWecomSourceSnapshot(params)?.source === "agent-callback";
249
- }
package/src/runtime.ts DELETED
@@ -1,14 +0,0 @@
1
- export {
2
- getActiveBotWsReplyHandle,
3
- getAccountRuntime,
4
- getAccountRuntimeSnapshot,
5
- getBotWsPushHandle,
6
- getWecomRuntime,
7
- registerActiveBotWsReplyHandle,
8
- registerAccountRuntime,
9
- registerBotWsPushHandle,
10
- setWecomRuntime,
11
- unregisterActiveBotWsReplyHandle,
12
- unregisterBotWsPushHandle,
13
- unregisterAccountRuntime,
14
- } from "./app/index.js";
@@ -1,87 +0,0 @@
1
- import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
2
-
3
- import type { WecomAgentConfig, WecomBotConfig } from "../types/index.js";
4
- import { isWecomSenderAllowed, normalizeWecomAllowFromEntry } from "../domain/policies.js";
5
-
6
- type WecomCommandAuthAccountConfig = Pick<WecomBotConfig, "dm"> | Pick<WecomAgentConfig, "dm">;
7
-
8
- export async function resolveWecomCommandAuthorization(params: {
9
- core: PluginRuntime;
10
- cfg: OpenClawConfig;
11
- accountConfig: WecomCommandAuthAccountConfig;
12
- rawBody: string;
13
- senderUserId: string;
14
- }): Promise<{
15
- shouldComputeAuth: boolean;
16
- dmPolicy: "pairing" | "allowlist" | "open" | "disabled";
17
- senderAllowed: boolean;
18
- authorizerConfigured: boolean;
19
- commandAuthorized: boolean | undefined;
20
- effectiveAllowFrom: string[];
21
- }> {
22
- const { core, cfg, accountConfig, rawBody, senderUserId } = params;
23
-
24
- const dmPolicy = (accountConfig.dm?.policy ?? "pairing") as "pairing" | "allowlist" | "open" | "disabled";
25
- const configAllowFrom = (accountConfig.dm?.allowFrom ?? []).map((v) => String(v));
26
-
27
- const shouldComputeAuth = core.channel.commands.shouldComputeCommandAuthorized(rawBody, cfg);
28
- // WeCom channel currently does NOT support the `openclaw pairing` CLI workflow
29
- // ("Channel wecom does not support pairing"). So we must not rely on pairing
30
- // store approvals for command authorization here.
31
- //
32
- // Policy semantics:
33
- // - open: commands are allowed for everyone by default (unless higher-level access-groups deny).
34
- // - allowlist: commands require allowFrom entries.
35
- // - pairing: treated the same as allowlist for WeCom (since pairing CLI is unsupported).
36
- const effectiveAllowFrom = dmPolicy === "open" ? ["*"] : configAllowFrom;
37
-
38
- const senderAllowed = isWecomSenderAllowed(senderUserId, effectiveAllowFrom);
39
- const allowAllConfigured = effectiveAllowFrom.some((entry) => normalizeWecomAllowFromEntry(entry) === "*");
40
- const authorizerConfigured = allowAllConfigured || effectiveAllowFrom.length > 0;
41
- const useAccessGroups = cfg.commands?.useAccessGroups !== false;
42
-
43
- const commandAuthorized = shouldComputeAuth
44
- ? core.channel.commands.resolveCommandAuthorizedFromAuthorizers({
45
- useAccessGroups,
46
- authorizers: [{ configured: authorizerConfigured, allowed: senderAllowed }],
47
- })
48
- : undefined;
49
-
50
- return {
51
- shouldComputeAuth,
52
- dmPolicy,
53
- senderAllowed,
54
- authorizerConfigured,
55
- commandAuthorized,
56
- effectiveAllowFrom,
57
- };
58
- }
59
-
60
- export function buildWecomUnauthorizedCommandPrompt(params: {
61
- senderUserId: string;
62
- dmPolicy: "pairing" | "allowlist" | "open" | "disabled";
63
- scope: "bot" | "agent";
64
- }): string {
65
- const user = params.senderUserId || "unknown";
66
- const policy = params.dmPolicy;
67
- const scopeLabel = params.scope === "bot" ? "Bot(智能机器人)" : "Agent(自建应用)";
68
- const dmPrefix = params.scope === "bot" ? "channels.wecom.bot.dm" : "channels.wecom.agent.dm";
69
- const allowCmd = (value: string) => `openclaw config set ${dmPrefix}.allowFrom '${value}'`;
70
- const policyCmd = (value: string) => `openclaw config set ${dmPrefix}.policy "${value}"`;
71
-
72
- if (policy === "disabled") {
73
- return [
74
- `无权限执行命令(${scopeLabel} 已禁用:dm.policy=disabled)`,
75
- `触发者:${user}`,
76
- `管理员:${policyCmd("open")}(全放开)或 ${policyCmd("allowlist")}(白名单)`,
77
- ].join("\n");
78
- }
79
- // WeCom 不支持 pairing CLI,因此这里统一给出“open / allowlist”两种明确的配置指令
80
- return [
81
- `无权限执行命令(入口:${scopeLabel},userid:${user})`,
82
- `管理员全放开:${policyCmd("open")}`,
83
- `管理员放行该用户:${policyCmd("allowlist")}`,
84
- `然后设置白名单:${allowCmd(JSON.stringify([user]))}`,
85
- `如果仍被拦截:检查 commands.useAccessGroups/访问组`,
86
- ].join("\n");
87
- }
@@ -1,78 +0,0 @@
1
- import path from "node:path";
2
-
3
- import { resolveWecomEgressProxyUrlFromNetwork } from "../config/index.js";
4
- import { wecomFetch } from "../http.js";
5
- import type { WecomNetworkConfig } from "../types/index.js";
6
-
7
- function inferContentTypeFromFilePath(filePath: string): string {
8
- const ext = path.extname(filePath).slice(1).toLowerCase();
9
- const mimeTypes: Record<string, string> = {
10
- jpg: "image/jpeg",
11
- jpeg: "image/jpeg",
12
- png: "image/png",
13
- gif: "image/gif",
14
- webp: "image/webp",
15
- bmp: "image/bmp",
16
- mp3: "audio/mpeg",
17
- wav: "audio/wav",
18
- amr: "audio/amr",
19
- mp4: "video/mp4",
20
- pdf: "application/pdf",
21
- doc: "application/msword",
22
- docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
23
- xls: "application/vnd.ms-excel",
24
- xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
25
- ppt: "application/vnd.ms-powerpoint",
26
- pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
27
- txt: "text/plain",
28
- csv: "text/csv",
29
- tsv: "text/tab-separated-values",
30
- md: "text/markdown",
31
- json: "application/json",
32
- xml: "application/xml",
33
- yaml: "application/yaml",
34
- yml: "application/yaml",
35
- zip: "application/zip",
36
- rar: "application/vnd.rar",
37
- "7z": "application/x-7z-compressed",
38
- tar: "application/x-tar",
39
- gz: "application/gzip",
40
- tgz: "application/gzip",
41
- rtf: "application/rtf",
42
- odt: "application/vnd.oasis.opendocument.text",
43
- };
44
- return mimeTypes[ext] || "application/octet-stream";
45
- }
46
-
47
- export async function resolveOutboundMediaAsset(params: {
48
- mediaUrl: string;
49
- network?: WecomNetworkConfig;
50
- timeoutMs?: number;
51
- }): Promise<{ buffer: Buffer; filename: string; contentType: string }> {
52
- const { mediaUrl, network, timeoutMs = 30000 } = params;
53
- if (/^https?:\/\//i.test(mediaUrl)) {
54
- const response = await wecomFetch(
55
- mediaUrl,
56
- { method: "GET" },
57
- {
58
- proxyUrl: resolveWecomEgressProxyUrlFromNetwork(network),
59
- timeoutMs,
60
- },
61
- );
62
- if (!response.ok) {
63
- throw new Error(`Failed to download media: ${response.status}`);
64
- }
65
- const buffer = Buffer.from(await response.arrayBuffer());
66
- const contentType = response.headers.get("content-type") || "application/octet-stream";
67
- const filename = path.basename(new URL(mediaUrl).pathname) || "media";
68
- return { buffer, filename, contentType };
69
- }
70
-
71
- const fs = await import("node:fs/promises");
72
- const buffer = await fs.readFile(mediaUrl);
73
- return {
74
- buffer,
75
- filename: path.basename(mediaUrl),
76
- contentType: inferContentTypeFromFilePath(mediaUrl),
77
- };
78
- }
@@ -1,111 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import { WecomMediaService } from "./media-service.js";
3
-
4
- describe("WecomMediaService", () => {
5
- const fetchRemoteMedia = vi.fn();
6
- const saveMediaBuffer = vi.fn();
7
-
8
- beforeEach(() => {
9
- fetchRemoteMedia.mockReset();
10
- saveMediaBuffer.mockReset();
11
- });
12
-
13
- it("passes configured wecom mediaMaxMb to remote attachment fetches and saves", async () => {
14
- const service = new WecomMediaService(
15
- {
16
- channel: {
17
- media: {
18
- fetchRemoteMedia,
19
- saveMediaBuffer,
20
- },
21
- },
22
- } as never,
23
- {
24
- channels: {
25
- wecom: {
26
- mediaMaxMb: 24,
27
- },
28
- },
29
- } as never,
30
- );
31
-
32
- fetchRemoteMedia.mockResolvedValue({
33
- buffer: Buffer.from("file"),
34
- contentType: "application/pdf",
35
- fileName: "sample.pdf",
36
- });
37
- saveMediaBuffer.mockResolvedValue({
38
- path: "/tmp/sample.pdf",
39
- });
40
-
41
- const event = {
42
- accountId: "default",
43
- attachments: [{ remoteUrl: "https://example.com/sample.pdf" }],
44
- } as never;
45
-
46
- const attachment = await service.normalizeFirstAttachment(event);
47
-
48
- expect(fetchRemoteMedia).toHaveBeenCalledWith({
49
- url: "https://example.com/sample.pdf",
50
- maxBytes: 24 * 1024 * 1024,
51
- });
52
-
53
- await service.saveInboundAttachment(event, attachment!);
54
-
55
- expect(saveMediaBuffer).toHaveBeenCalledWith(
56
- expect.any(Buffer),
57
- "application/pdf",
58
- "inbound",
59
- 24 * 1024 * 1024,
60
- "sample.pdf",
61
- );
62
- });
63
-
64
- it("prefers account-specific mediaMaxMb for inbound saves", async () => {
65
- const service = new WecomMediaService(
66
- {
67
- channel: {
68
- media: {
69
- fetchRemoteMedia,
70
- saveMediaBuffer,
71
- },
72
- },
73
- } as never,
74
- {
75
- channels: {
76
- wecom: {
77
- mediaMaxMb: 24,
78
- accounts: {
79
- ops: {
80
- mediaMaxMb: 36,
81
- },
82
- },
83
- },
84
- },
85
- } as never,
86
- );
87
-
88
- saveMediaBuffer.mockResolvedValue({
89
- path: "/tmp/account-specific.pdf",
90
- });
91
-
92
- await service.saveInboundAttachment(
93
- {
94
- accountId: "ops",
95
- } as never,
96
- {
97
- buffer: Buffer.from("file"),
98
- contentType: "application/pdf",
99
- filename: "ops.pdf",
100
- },
101
- );
102
-
103
- expect(saveMediaBuffer).toHaveBeenCalledWith(
104
- expect.any(Buffer),
105
- "application/pdf",
106
- "inbound",
107
- 36 * 1024 * 1024,
108
- "ops.pdf",
109
- );
110
- });
111
- });
@@ -1,84 +0,0 @@
1
- import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
2
- import { resolveWecomMediaMaxBytes } from "../config/index.js";
3
- import { decryptWecomMediaWithMeta } from "../media.js";
4
- import type { UnifiedInboundEvent } from "../types/index.js";
5
- import type { NormalizedMediaAttachment } from "./media-types.js";
6
-
7
- export class WecomMediaService {
8
- constructor(
9
- private readonly core: PluginRuntime,
10
- private readonly cfg: OpenClawConfig,
11
- ) {}
12
-
13
- private resolveInboundMaxBytes(accountId: string): number {
14
- return resolveWecomMediaMaxBytes(this.cfg, accountId);
15
- }
16
-
17
- async downloadRemoteMedia(params: {
18
- url: string;
19
- maxBytes: number;
20
- }): Promise<NormalizedMediaAttachment> {
21
- const loaded = await this.core.channel.media.fetchRemoteMedia({
22
- url: params.url,
23
- maxBytes: params.maxBytes,
24
- });
25
- return {
26
- buffer: loaded.buffer,
27
- contentType: loaded.contentType,
28
- filename: loaded.fileName,
29
- };
30
- }
31
-
32
- /**
33
- * Download and decrypt WeCom AES-encrypted media.
34
- * Bot-ws: each message carries a unique per-URL aeskey in the message body.
35
- * Bot-webhook: uses the account-level EncodingAESKey.
36
- * Both use AES-256-CBC with PKCS#7 padding (32-byte block), IV = key[:16].
37
- */
38
- async downloadEncryptedMedia(params: {
39
- url: string;
40
- aesKey: string;
41
- maxBytes: number;
42
- }): Promise<NormalizedMediaAttachment> {
43
- const decrypted = await decryptWecomMediaWithMeta(params.url, params.aesKey, {
44
- maxBytes: params.maxBytes,
45
- });
46
- return {
47
- buffer: decrypted.buffer,
48
- contentType: decrypted.sourceContentType,
49
- filename: decrypted.sourceFilename,
50
- };
51
- }
52
-
53
- async saveInboundAttachment(
54
- event: UnifiedInboundEvent,
55
- attachment: NormalizedMediaAttachment,
56
- ): Promise<string> {
57
- const maxBytes = this.resolveInboundMaxBytes(event.accountId);
58
- const saved = await this.core.channel.media.saveMediaBuffer(
59
- attachment.buffer,
60
- attachment.contentType,
61
- "inbound",
62
- maxBytes,
63
- attachment.filename,
64
- );
65
- return saved.path;
66
- }
67
-
68
- async normalizeFirstAttachment(
69
- event: UnifiedInboundEvent,
70
- ): Promise<NormalizedMediaAttachment | undefined> {
71
- const first = event.attachments?.[0];
72
- if (!first?.remoteUrl) {
73
- return undefined;
74
- }
75
- // Keep fetch/decrypt/save on the same account-aware limit instead of falling back
76
- // to the core media store default (5MB).
77
- const maxBytes = this.resolveInboundMaxBytes(event.accountId);
78
- // Bot-ws media is AES-encrypted; use decryption when aesKey is present
79
- if (first.aesKey) {
80
- return this.downloadEncryptedMedia({ url: first.remoteUrl, aesKey: first.aesKey, maxBytes });
81
- }
82
- return this.downloadRemoteMedia({ url: first.remoteUrl, maxBytes });
83
- }
84
- }
@@ -1,5 +0,0 @@
1
- export type NormalizedMediaAttachment = {
2
- filename?: string;
3
- contentType?: string;
4
- buffer: Buffer;
5
- };
@@ -1,50 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
-
3
- import { extractContent, extractFromUser, extractMediaId, extractMsgId, parseXml } from "./xml-parser.js";
4
-
5
- describe("wecom xml-parser", () => {
6
- test("extractContent is robust to non-string Content", () => {
7
- const msg: any = { MsgType: "text", Content: { "#text": "hello", "@_foo": "bar" } };
8
- expect(extractContent(msg)).toBe("hello");
9
- });
10
-
11
- test("extractContent handles array content", () => {
12
- const msg: any = { MsgType: "text", Content: ["a", "b"] };
13
- expect(extractContent(msg)).toBe("a\nb");
14
- });
15
-
16
- test("extractContent handles file messages", () => {
17
- const msg: any = { MsgType: "file", MediaId: "MEDIA123" };
18
- expect(extractContent(msg)).toBe("[文件消息]");
19
- });
20
-
21
- test("extractMediaId handles object MediaId", () => {
22
- const msg: any = { MediaId: { "#text": "MEDIA123", "@_foo": "bar" } };
23
- expect(extractMediaId(msg)).toBe("MEDIA123");
24
- });
25
-
26
- test("extractMsgId handles number MsgId", () => {
27
- const msg: any = { MsgId: 123456789 };
28
- expect(extractMsgId(msg)).toBe("123456789");
29
- });
30
-
31
- test("parseXml preserves leading zero userid in FromUserName", () => {
32
- const xml = `
33
- <xml>
34
- <FromUserName><![CDATA[0254571]]></FromUserName>
35
- </xml>
36
- `;
37
- const msg = parseXml(xml);
38
- expect(extractFromUser(msg)).toBe("0254571");
39
- });
40
-
41
- test("parseXml preserves 64-bit MsgId as string", () => {
42
- const xml = `
43
- <xml>
44
- <MsgId>1234567890123456</MsgId>
45
- </xml>
46
- `;
47
- const msg = parseXml(xml);
48
- expect(extractMsgId(msg)).toBe("1234567890123456");
49
- });
50
- });