@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
@@ -0,0 +1,112 @@
1
+ import { getPeerContextToken } from "../context-store.js";
2
+ import { buildWecomContextTarget } from "../target.js";
3
+ import { resolveRuntimeRoute } from "./routing-bridge.js";
4
+ import { registerWecomSourceSnapshot } from "./source-registry.js";
5
+ function readContextSessionId(ctx) {
6
+ const sessionId = "SessionId" in ctx ? ctx.SessionId : undefined;
7
+ return typeof sessionId === "string" && sessionId.trim() ? sessionId.trim() : undefined;
8
+ }
9
+ export async function prepareInboundSession(params) {
10
+ const { core, cfg, event, mediaService } = params;
11
+ const route = resolveRuntimeRoute({ core, cfg, event });
12
+ const source = event.transport === "bot-ws"
13
+ ? "bot-ws"
14
+ : event.transport === "agent-callback"
15
+ ? "agent-callback"
16
+ : undefined;
17
+ if (source) {
18
+ registerWecomSourceSnapshot({
19
+ accountId: event.accountId,
20
+ source,
21
+ messageId: event.messageId,
22
+ sessionKey: route.sessionKey,
23
+ peerKind: event.conversation.peerKind,
24
+ peerId: event.conversation.peerId,
25
+ });
26
+ }
27
+ const storePath = core.channel.session.resolveStorePath(cfg.session?.store, {
28
+ agentId: route.agentId,
29
+ });
30
+ const previousTimestamp = core.channel.session.readSessionUpdatedAt({
31
+ storePath,
32
+ sessionKey: route.sessionKey,
33
+ });
34
+ const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(cfg);
35
+ const body = core.channel.reply.formatAgentEnvelope({
36
+ channel: "WeCom",
37
+ from: `${event.conversation.peerKind}:${event.conversation.peerId}`,
38
+ previousTimestamp,
39
+ envelope: envelopeOptions,
40
+ body: event.text,
41
+ });
42
+ const firstAttachment = await mediaService.normalizeFirstAttachment(event);
43
+ const mediaPath = firstAttachment
44
+ ? await mediaService.saveInboundAttachment(event, firstAttachment)
45
+ : undefined;
46
+ const defaultOriginatingTo = event.conversation.peerKind === "group"
47
+ ? `wecom:group:${event.conversation.peerId}`
48
+ : `wecom:user:${event.conversation.peerId}`;
49
+ const contextToken = event.transport === "bot-ws"
50
+ ? getPeerContextToken(event.accountId, event.conversation.peerId)
51
+ : undefined;
52
+ const originatingTo = contextToken
53
+ ? buildWecomContextTarget(contextToken)
54
+ : defaultOriginatingTo;
55
+ const providerContext = event.transport === "bot-ws"
56
+ ? {
57
+ // Bot WS inbound turns already have a live reply handle bound to the
58
+ // current req_id. Mark the current surface as WeCom so core final text
59
+ // stays on that handle and replaces the placeholder instead of being
60
+ // re-routed as a second active-push message.
61
+ Provider: "wecom",
62
+ Surface: "wecom",
63
+ }
64
+ : {
65
+ Provider: "wecom",
66
+ };
67
+ const ctx = core.channel.reply.finalizeInboundContext({
68
+ Body: body,
69
+ RawBody: event.text,
70
+ CommandBody: event.text,
71
+ From: event.conversation.peerKind === "group"
72
+ ? `wecom:group:${event.conversation.peerId}`
73
+ : `wecom:user:${event.conversation.senderId}`,
74
+ To: event.conversation.peerKind === "group"
75
+ ? `wecom:group:${event.conversation.peerId}`
76
+ : `wecom:user:${event.conversation.peerId}`,
77
+ SessionKey: route.sessionKey,
78
+ AccountId: route.accountId,
79
+ ChatType: event.conversation.peerKind,
80
+ ConversationLabel: `${event.conversation.peerKind}:${event.conversation.peerId}`,
81
+ SenderName: event.senderName ?? event.conversation.senderId,
82
+ SenderId: event.conversation.senderId,
83
+ // Keep Originating* populated so explicit route-to-origin flows and message
84
+ // tools can still resolve the active peer context when needed.
85
+ ...providerContext,
86
+ OriginatingChannel: "wecom",
87
+ OriginatingTo: originatingTo,
88
+ MessageSid: event.messageId,
89
+ CommandAuthorized: true,
90
+ MediaPath: mediaPath,
91
+ MediaUrl: mediaPath,
92
+ MediaType: firstAttachment?.contentType,
93
+ });
94
+ if (source) {
95
+ registerWecomSourceSnapshot({
96
+ accountId: event.accountId,
97
+ source,
98
+ messageId: event.messageId,
99
+ sessionKey: ctx.SessionKey ?? route.sessionKey,
100
+ sessionId: readContextSessionId(ctx),
101
+ peerKind: event.conversation.peerKind,
102
+ peerId: event.conversation.peerId,
103
+ });
104
+ }
105
+ await core.channel.session.recordInboundSession({
106
+ storePath,
107
+ sessionKey: ctx.SessionKey ?? route.sessionKey,
108
+ ctx,
109
+ onRecordError: () => { },
110
+ });
111
+ return { route, ctx, storePath };
112
+ }
@@ -0,0 +1,174 @@
1
+ const MAX_MESSAGE_FACTS = 2048;
2
+ const MAX_SESSION_SNAPSHOTS = 1024;
3
+ const MAX_CONVERSATION_SNAPSHOTS = 1024;
4
+ const messageFacts = new Map();
5
+ const sessionSnapshotsByAccountKey = new Map();
6
+ const sessionSnapshotsByLooseKey = new Map();
7
+ const conversationSnapshotsByAccountKey = new Map();
8
+ const conversationSnapshotsByLooseKey = new Map();
9
+ function normalizeOptional(value) {
10
+ const trimmed = String(value ?? "").trim();
11
+ return trimmed || undefined;
12
+ }
13
+ function messageFactKey(accountId, messageId) {
14
+ return `${accountId}::${messageId}`;
15
+ }
16
+ function accountScopedSessionKey(accountId, kind, value) {
17
+ return `${accountId}::${kind}::${value}`;
18
+ }
19
+ function normalizePeerId(value) {
20
+ const trimmed = String(value ?? "").trim();
21
+ return trimmed ? trimmed.toLowerCase() : undefined;
22
+ }
23
+ function normalizePeerKind(value) {
24
+ const trimmed = String(value ?? "").trim().toLowerCase();
25
+ return trimmed === "direct" || trimmed === "group" ? trimmed : undefined;
26
+ }
27
+ function accountScopedConversationKey(accountId, peerKind, peerId) {
28
+ return `${accountId}::peer::${peerKind}::${peerId}`;
29
+ }
30
+ function pruneOldest(map, maxSize) {
31
+ while (map.size > maxSize) {
32
+ const oldestKey = map.keys().next().value;
33
+ if (!oldestKey)
34
+ return;
35
+ map.delete(oldestKey);
36
+ }
37
+ }
38
+ function writeSessionSnapshot(snapshot) {
39
+ const sessionKey = normalizeOptional(snapshot.sessionKey);
40
+ const sessionId = normalizeOptional(snapshot.sessionId);
41
+ if (sessionKey) {
42
+ sessionSnapshotsByAccountKey.set(accountScopedSessionKey(snapshot.accountId, "sessionKey", sessionKey), snapshot);
43
+ sessionSnapshotsByLooseKey.set(`sessionKey::${sessionKey}`, snapshot);
44
+ }
45
+ if (sessionId) {
46
+ sessionSnapshotsByAccountKey.set(accountScopedSessionKey(snapshot.accountId, "sessionId", sessionId), snapshot);
47
+ sessionSnapshotsByLooseKey.set(`sessionId::${sessionId}`, snapshot);
48
+ }
49
+ pruneOldest(sessionSnapshotsByAccountKey, MAX_SESSION_SNAPSHOTS * 2);
50
+ pruneOldest(sessionSnapshotsByLooseKey, MAX_SESSION_SNAPSHOTS * 2);
51
+ }
52
+ function writeConversationSnapshot(snapshot) {
53
+ const peerKind = normalizePeerKind(snapshot.peerKind);
54
+ const peerId = normalizePeerId(snapshot.peerId);
55
+ if (!peerKind || !peerId) {
56
+ return;
57
+ }
58
+ conversationSnapshotsByAccountKey.set(accountScopedConversationKey(snapshot.accountId, peerKind, peerId), {
59
+ ...snapshot,
60
+ peerKind,
61
+ peerId,
62
+ });
63
+ conversationSnapshotsByLooseKey.set(`peer::${peerKind}::${peerId}`, {
64
+ ...snapshot,
65
+ peerKind,
66
+ peerId,
67
+ });
68
+ pruneOldest(conversationSnapshotsByAccountKey, MAX_CONVERSATION_SNAPSHOTS);
69
+ pruneOldest(conversationSnapshotsByLooseKey, MAX_CONVERSATION_SNAPSHOTS);
70
+ }
71
+ export function registerWecomSourceSnapshot(params) {
72
+ const accountId = normalizeOptional(params.accountId);
73
+ if (!accountId)
74
+ return;
75
+ const snapshot = {
76
+ accountId,
77
+ source: params.source,
78
+ recordedAt: Date.now(),
79
+ ...(normalizeOptional(params.messageId)
80
+ ? { messageId: normalizeOptional(params.messageId) }
81
+ : {}),
82
+ ...(normalizeOptional(params.sessionKey)
83
+ ? { sessionKey: normalizeOptional(params.sessionKey) }
84
+ : {}),
85
+ ...(normalizeOptional(params.sessionId)
86
+ ? { sessionId: normalizeOptional(params.sessionId) }
87
+ : {}),
88
+ ...(normalizePeerKind(params.peerKind) ? { peerKind: normalizePeerKind(params.peerKind) } : {}),
89
+ ...(normalizePeerId(params.peerId) ? { peerId: normalizePeerId(params.peerId) } : {}),
90
+ ...(normalizeOptional(params.upstreamCorpId)
91
+ ? { upstreamCorpId: normalizeOptional(params.upstreamCorpId) }
92
+ : {}),
93
+ };
94
+ if (snapshot.messageId) {
95
+ messageFacts.set(messageFactKey(accountId, snapshot.messageId), snapshot);
96
+ pruneOldest(messageFacts, MAX_MESSAGE_FACTS);
97
+ }
98
+ writeSessionSnapshot(snapshot);
99
+ writeConversationSnapshot(snapshot);
100
+ }
101
+ export function resolveWecomSourceSnapshot(params) {
102
+ const accountId = normalizeOptional(params.accountId);
103
+ const sessionKey = normalizeOptional(params.sessionKey);
104
+ const sessionId = normalizeOptional(params.sessionId);
105
+ const peerKind = normalizePeerKind(params.peerKind);
106
+ const peerId = normalizePeerId(params.peerId);
107
+ if (accountId && sessionKey) {
108
+ const scoped = sessionSnapshotsByAccountKey.get(accountScopedSessionKey(accountId, "sessionKey", sessionKey));
109
+ if (scoped)
110
+ return scoped;
111
+ }
112
+ if (accountId && sessionId) {
113
+ const scoped = sessionSnapshotsByAccountKey.get(accountScopedSessionKey(accountId, "sessionId", sessionId));
114
+ if (scoped)
115
+ return scoped;
116
+ }
117
+ if (sessionKey) {
118
+ const loose = sessionSnapshotsByLooseKey.get(`sessionKey::${sessionKey}`);
119
+ if (loose)
120
+ return loose;
121
+ }
122
+ if (sessionId) {
123
+ const loose = sessionSnapshotsByLooseKey.get(`sessionId::${sessionId}`);
124
+ if (loose)
125
+ return loose;
126
+ }
127
+ if (accountId && peerKind && peerId) {
128
+ const scoped = conversationSnapshotsByAccountKey.get(accountScopedConversationKey(accountId, peerKind, peerId));
129
+ if (scoped)
130
+ return scoped;
131
+ }
132
+ if (peerKind && peerId) {
133
+ const loose = conversationSnapshotsByLooseKey.get(`peer::${peerKind}::${peerId}`);
134
+ if (loose)
135
+ return loose;
136
+ }
137
+ return undefined;
138
+ }
139
+ export function clearWecomSourceAccount(accountId) {
140
+ const normalized = normalizeOptional(accountId);
141
+ if (!normalized)
142
+ return;
143
+ for (const [key, value] of messageFacts) {
144
+ if (value.accountId === normalized || key.startsWith(`${normalized}::`)) {
145
+ messageFacts.delete(key);
146
+ }
147
+ }
148
+ for (const [key, value] of sessionSnapshotsByAccountKey) {
149
+ if (value.accountId === normalized || key.startsWith(`${normalized}::`)) {
150
+ sessionSnapshotsByAccountKey.delete(key);
151
+ }
152
+ }
153
+ for (const [key, value] of sessionSnapshotsByLooseKey) {
154
+ if (value.accountId === normalized) {
155
+ sessionSnapshotsByLooseKey.delete(key);
156
+ }
157
+ }
158
+ for (const [key, value] of conversationSnapshotsByAccountKey) {
159
+ if (value.accountId === normalized || key.startsWith(`${normalized}::`)) {
160
+ conversationSnapshotsByAccountKey.delete(key);
161
+ }
162
+ }
163
+ for (const [key, value] of conversationSnapshotsByLooseKey) {
164
+ if (value.accountId === normalized) {
165
+ conversationSnapshotsByLooseKey.delete(key);
166
+ }
167
+ }
168
+ }
169
+ export function isWecomBotWsSource(params) {
170
+ return resolveWecomSourceSnapshot(params)?.source === "bot-ws";
171
+ }
172
+ export function isWecomAgentSource(params) {
173
+ return resolveWecomSourceSnapshot(params)?.source === "agent-callback";
174
+ }
@@ -0,0 +1 @@
1
+ export { getActiveBotWsReplyHandle, getAccountRuntime, getAccountRuntimeSnapshot, getBotWsPushHandle, getWecomRuntime, registerActiveBotWsReplyHandle, registerAccountRuntime, registerBotWsPushHandle, setWecomRuntime, unregisterActiveBotWsReplyHandle, unregisterBotWsPushHandle, unregisterAccountRuntime, } from "./app/index.js";
@@ -0,0 +1,57 @@
1
+ import { isWecomSenderAllowed, normalizeWecomAllowFromEntry } from "../domain/policies.js";
2
+ export async function resolveWecomCommandAuthorization(params) {
3
+ const { core, cfg, accountConfig, rawBody, senderUserId } = params;
4
+ const dmPolicy = (accountConfig.dm?.policy ?? "pairing");
5
+ const configAllowFrom = (accountConfig.dm?.allowFrom ?? []).map((v) => String(v));
6
+ const shouldComputeAuth = core.channel.commands.shouldComputeCommandAuthorized(rawBody, cfg);
7
+ // WeCom channel currently does NOT support the `openclaw pairing` CLI workflow
8
+ // ("Channel wecom does not support pairing"). So we must not rely on pairing
9
+ // store approvals for command authorization here.
10
+ //
11
+ // Policy semantics:
12
+ // - open: commands are allowed for everyone by default (unless higher-level access-groups deny).
13
+ // - allowlist: commands require allowFrom entries.
14
+ // - pairing: treated the same as allowlist for WeCom (since pairing CLI is unsupported).
15
+ const effectiveAllowFrom = dmPolicy === "open" ? ["*"] : configAllowFrom;
16
+ const senderAllowed = isWecomSenderAllowed(senderUserId, effectiveAllowFrom);
17
+ const allowAllConfigured = effectiveAllowFrom.some((entry) => normalizeWecomAllowFromEntry(entry) === "*");
18
+ const authorizerConfigured = allowAllConfigured || effectiveAllowFrom.length > 0;
19
+ const useAccessGroups = cfg.commands?.useAccessGroups !== false;
20
+ const commandAuthorized = shouldComputeAuth
21
+ ? core.channel.commands.resolveCommandAuthorizedFromAuthorizers({
22
+ useAccessGroups,
23
+ authorizers: [{ configured: authorizerConfigured, allowed: senderAllowed }],
24
+ })
25
+ : undefined;
26
+ return {
27
+ shouldComputeAuth,
28
+ dmPolicy,
29
+ senderAllowed,
30
+ authorizerConfigured,
31
+ commandAuthorized,
32
+ effectiveAllowFrom,
33
+ };
34
+ }
35
+ export function buildWecomUnauthorizedCommandPrompt(params) {
36
+ const user = params.senderUserId || "unknown";
37
+ const policy = params.dmPolicy;
38
+ const scopeLabel = params.scope === "bot" ? "Bot(智能机器人)" : "Agent(自建应用)";
39
+ const dmPrefix = params.scope === "bot" ? "channels.wecom.bot.dm" : "channels.wecom.agent.dm";
40
+ const allowCmd = (value) => `openclaw config set ${dmPrefix}.allowFrom '${value}'`;
41
+ const policyCmd = (value) => `openclaw config set ${dmPrefix}.policy "${value}"`;
42
+ if (policy === "disabled") {
43
+ return [
44
+ `无权限执行命令(${scopeLabel} 已禁用:dm.policy=disabled)`,
45
+ `触发者:${user}`,
46
+ `管理员:${policyCmd("open")}(全放开)或 ${policyCmd("allowlist")}(白名单)`,
47
+ ].join("\n");
48
+ }
49
+ // WeCom 不支持 pairing CLI,因此这里统一给出“open / allowlist”两种明确的配置指令
50
+ return [
51
+ `无权限执行命令(入口:${scopeLabel},userid:${user})`,
52
+ `管理员全放开:${policyCmd("open")}`,
53
+ `管理员放行该用户:${policyCmd("allowlist")}`,
54
+ `然后设置白名单:${allowCmd(JSON.stringify([user]))}`,
55
+ `如果仍被拦截:检查 commands.useAccessGroups/访问组`,
56
+ ].join("\n");
57
+ }
@@ -1,5 +1,4 @@
1
1
  /**
2
2
  * WeCom Shared 模块导出
3
3
  */
4
-
5
4
  export { parseXml, extractMsgType, extractFromUser, extractContent, extractChatId, extractToUser } from "./xml-parser.js";
@@ -0,0 +1,65 @@
1
+ import path from "node:path";
2
+ import { resolveWecomEgressProxyUrlFromNetwork } from "../config/index.js";
3
+ import { wecomFetch } from "../http.js";
4
+ function inferContentTypeFromFilePath(filePath) {
5
+ const ext = path.extname(filePath).slice(1).toLowerCase();
6
+ const mimeTypes = {
7
+ jpg: "image/jpeg",
8
+ jpeg: "image/jpeg",
9
+ png: "image/png",
10
+ gif: "image/gif",
11
+ webp: "image/webp",
12
+ bmp: "image/bmp",
13
+ mp3: "audio/mpeg",
14
+ wav: "audio/wav",
15
+ amr: "audio/amr",
16
+ mp4: "video/mp4",
17
+ pdf: "application/pdf",
18
+ doc: "application/msword",
19
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
20
+ xls: "application/vnd.ms-excel",
21
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
22
+ ppt: "application/vnd.ms-powerpoint",
23
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
24
+ txt: "text/plain",
25
+ csv: "text/csv",
26
+ tsv: "text/tab-separated-values",
27
+ md: "text/markdown",
28
+ json: "application/json",
29
+ xml: "application/xml",
30
+ yaml: "application/yaml",
31
+ yml: "application/yaml",
32
+ zip: "application/zip",
33
+ rar: "application/vnd.rar",
34
+ "7z": "application/x-7z-compressed",
35
+ tar: "application/x-tar",
36
+ gz: "application/gzip",
37
+ tgz: "application/gzip",
38
+ rtf: "application/rtf",
39
+ odt: "application/vnd.oasis.opendocument.text",
40
+ };
41
+ return mimeTypes[ext] || "application/octet-stream";
42
+ }
43
+ export async function resolveOutboundMediaAsset(params) {
44
+ const { mediaUrl, network, timeoutMs = 30000 } = params;
45
+ if (/^https?:\/\//i.test(mediaUrl)) {
46
+ const response = await wecomFetch(mediaUrl, { method: "GET" }, {
47
+ proxyUrl: resolveWecomEgressProxyUrlFromNetwork(network),
48
+ timeoutMs,
49
+ });
50
+ if (!response.ok) {
51
+ throw new Error(`Failed to download media: ${response.status}`);
52
+ }
53
+ const buffer = Buffer.from(await response.arrayBuffer());
54
+ const contentType = response.headers.get("content-type") || "application/octet-stream";
55
+ const filename = path.basename(new URL(mediaUrl).pathname) || "media";
56
+ return { buffer, filename, contentType };
57
+ }
58
+ const fs = await import("node:fs/promises");
59
+ const buffer = await fs.readFile(mediaUrl);
60
+ return {
61
+ buffer,
62
+ filename: path.basename(mediaUrl),
63
+ contentType: inferContentTypeFromFilePath(mediaUrl),
64
+ };
65
+ }
@@ -0,0 +1,59 @@
1
+ import { resolveWecomMediaMaxBytes } from "../config/index.js";
2
+ import { decryptWecomMediaWithMeta } from "../media.js";
3
+ export class WecomMediaService {
4
+ core;
5
+ cfg;
6
+ constructor(core, cfg) {
7
+ this.core = core;
8
+ this.cfg = cfg;
9
+ }
10
+ resolveInboundMaxBytes(accountId) {
11
+ return resolveWecomMediaMaxBytes(this.cfg, accountId);
12
+ }
13
+ async downloadRemoteMedia(params) {
14
+ const loaded = await this.core.channel.media.fetchRemoteMedia({
15
+ url: params.url,
16
+ maxBytes: params.maxBytes,
17
+ });
18
+ return {
19
+ buffer: loaded.buffer,
20
+ contentType: loaded.contentType,
21
+ filename: loaded.fileName,
22
+ };
23
+ }
24
+ /**
25
+ * Download and decrypt WeCom AES-encrypted media.
26
+ * Bot-ws: each message carries a unique per-URL aeskey in the message body.
27
+ * Bot-webhook: uses the account-level EncodingAESKey.
28
+ * Both use AES-256-CBC with PKCS#7 padding (32-byte block), IV = key[:16].
29
+ */
30
+ async downloadEncryptedMedia(params) {
31
+ const decrypted = await decryptWecomMediaWithMeta(params.url, params.aesKey, {
32
+ maxBytes: params.maxBytes,
33
+ });
34
+ return {
35
+ buffer: decrypted.buffer,
36
+ contentType: decrypted.sourceContentType,
37
+ filename: decrypted.sourceFilename,
38
+ };
39
+ }
40
+ async saveInboundAttachment(event, attachment) {
41
+ const maxBytes = this.resolveInboundMaxBytes(event.accountId);
42
+ const saved = await this.core.channel.media.saveMediaBuffer(attachment.buffer, attachment.contentType, "inbound", maxBytes, attachment.filename);
43
+ return saved.path;
44
+ }
45
+ async normalizeFirstAttachment(event) {
46
+ const first = event.attachments?.[0];
47
+ if (!first?.remoteUrl) {
48
+ return undefined;
49
+ }
50
+ // Keep fetch/decrypt/save on the same account-aware limit instead of falling back
51
+ // to the core media store default (5MB).
52
+ const maxBytes = this.resolveInboundMaxBytes(event.accountId);
53
+ // Bot-ws media is AES-encrypted; use decryption when aesKey is present
54
+ if (first.aesKey) {
55
+ return this.downloadEncryptedMedia({ url: first.remoteUrl, aesKey: first.aesKey, maxBytes });
56
+ }
57
+ return this.downloadRemoteMedia({ url: first.remoteUrl, maxBytes });
58
+ }
59
+ }
@@ -0,0 +1 @@
1
+ export {};