@yanhaidao/wecom 2.4.160 → 2.5.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/dist/index.js +68 -0
  2. package/dist/src/accounts.js +20 -0
  3. package/dist/src/agent/handler.js +895 -0
  4. package/dist/src/agent/index.js +5 -0
  5. package/dist/src/app/account-runtime.js +216 -0
  6. package/dist/src/app/bootstrap.js +19 -0
  7. package/dist/src/app/index.js +118 -0
  8. package/dist/src/capability/agent/delivery-service.js +63 -0
  9. package/dist/src/capability/agent/fallback-policy.js +6 -0
  10. package/dist/src/capability/agent/ingress-service.js +33 -0
  11. package/dist/src/capability/agent/upstream-delivery-service.js +71 -0
  12. package/dist/src/capability/bot/dispatch-config.js +45 -0
  13. package/dist/src/capability/bot/fallback-delivery.js +147 -0
  14. package/dist/src/capability/bot/local-path-delivery.js +178 -0
  15. package/dist/src/capability/bot/sandbox-media.js +138 -0
  16. package/dist/src/capability/bot/service.js +49 -0
  17. package/dist/src/capability/bot/stream-delivery.js +321 -0
  18. package/dist/src/capability/bot/stream-finalizer.js +81 -0
  19. package/dist/src/capability/bot/stream-orchestrator.js +318 -0
  20. package/dist/src/capability/bot/types.js +1 -0
  21. package/{src/capability/calendar/client.ts → dist/src/capability/calendar/client.js} +118 -241
  22. package/{src/capability/calendar/schema.ts → dist/src/capability/calendar/schema.js} +0 -38
  23. package/dist/src/capability/calendar/tool.js +365 -0
  24. package/dist/src/capability/calendar/types.js +12 -0
  25. package/{src/capability/doc/client.ts → dist/src/capability/doc/client.js} +370 -605
  26. package/{src/capability/doc/schema.ts → dist/src/capability/doc/schema.js} +345 -394
  27. package/dist/src/capability/doc/tool.js +1556 -0
  28. package/dist/src/capability/doc/types.js +113 -0
  29. package/dist/src/capability/mcp/index.js +3 -0
  30. package/dist/src/capability/mcp/schema.js +102 -0
  31. package/dist/src/capability/mcp/tool.js +146 -0
  32. package/dist/src/capability/mcp/transport.js +293 -0
  33. package/dist/src/channel.js +224 -0
  34. package/dist/src/config/accounts.js +236 -0
  35. package/dist/src/config/derived-paths.js +31 -0
  36. package/dist/src/config/index.js +7 -0
  37. package/dist/src/config/media.js +110 -0
  38. package/dist/src/config/network.js +32 -0
  39. package/dist/src/config/routing.js +20 -0
  40. package/dist/src/config/runtime-config.js +25 -0
  41. package/dist/src/config/schema.js +4 -0
  42. package/{src/config-schema.ts → dist/src/config-schema.js} +1 -1
  43. package/dist/src/context-store.js +219 -0
  44. package/{src/crypto/aes.ts → dist/src/crypto/aes.js} +11 -28
  45. package/dist/src/crypto/index.js +9 -0
  46. package/{src/crypto/signature.ts → dist/src/crypto/signature.js} +3 -18
  47. package/{src/crypto/xml.ts → dist/src/crypto/xml.js} +3 -11
  48. package/dist/src/crypto.js +145 -0
  49. package/dist/src/domain/models.js +1 -0
  50. package/dist/src/domain/policies.js +32 -0
  51. package/{src/dynamic-agent.ts → dist/src/dynamic-agent.js} +36 -73
  52. package/dist/src/gateway-monitor.js +139 -0
  53. package/dist/src/http.js +114 -0
  54. package/{src/media.ts → dist/src/media.js} +21 -40
  55. package/dist/src/monitor/limits.js +7 -0
  56. package/dist/src/monitor/state.js +28 -0
  57. package/dist/src/monitor.js +84 -0
  58. package/dist/src/observability/audit-log.js +30 -0
  59. package/dist/src/observability/legacy-operational-event-store.js +22 -0
  60. package/dist/src/observability/raw-envelope-log.js +24 -0
  61. package/dist/src/observability/status-registry.js +9 -0
  62. package/dist/src/observability/transport-session-view.js +14 -0
  63. package/dist/src/onboarding.js +546 -0
  64. package/dist/src/outbound.js +557 -0
  65. package/dist/src/runtime/dispatcher.js +57 -0
  66. package/{src/runtime/index.ts → dist/src/runtime/index.js} +0 -1
  67. package/dist/src/runtime/outbound-intent.js +1 -0
  68. package/dist/src/runtime/reply-orchestrator.js +38 -0
  69. package/dist/src/runtime/routing-bridge.js +26 -0
  70. package/dist/src/runtime/session-manager.js +112 -0
  71. package/dist/src/runtime/source-registry.js +174 -0
  72. package/dist/src/runtime.js +1 -0
  73. package/dist/src/shared/command-auth.js +57 -0
  74. package/{src/shared/index.ts → dist/src/shared/index.js} +0 -1
  75. package/dist/src/shared/media-asset.js +65 -0
  76. package/dist/src/shared/media-service.js +59 -0
  77. package/dist/src/shared/media-types.js +1 -0
  78. package/{src/shared/xml-parser.ts → dist/src/shared/xml-parser.js} +72 -63
  79. package/dist/src/store/active-reply-store.js +41 -0
  80. package/dist/src/store/interfaces.js +1 -0
  81. package/dist/src/store/memory-store.js +33 -0
  82. package/dist/src/store/stream-batch-store.js +319 -0
  83. package/{src/target.ts → dist/src/target.js} +15 -48
  84. package/dist/src/transport/agent-api/client.js +168 -0
  85. package/dist/src/transport/agent-api/core.js +337 -0
  86. package/dist/src/transport/agent-api/delivery.js +28 -0
  87. package/dist/src/transport/agent-api/media-upload.js +4 -0
  88. package/dist/src/transport/agent-api/reply.js +24 -0
  89. package/dist/src/transport/agent-api/upstream-delivery.js +30 -0
  90. package/dist/src/transport/agent-api/upstream-media-upload.js +46 -0
  91. package/dist/src/transport/agent-api/upstream-reply.js +26 -0
  92. package/dist/src/transport/agent-callback/http-handler.js +30 -0
  93. package/dist/src/transport/agent-callback/inbound.js +4 -0
  94. package/dist/src/transport/agent-callback/reply.js +8 -0
  95. package/dist/src/transport/agent-callback/request-handler.js +189 -0
  96. package/dist/src/transport/agent-callback/session.js +15 -0
  97. package/dist/src/transport/bot-webhook/active-reply.js +27 -0
  98. package/dist/src/transport/bot-webhook/http-handler.js +31 -0
  99. package/dist/src/transport/bot-webhook/inbound-normalizer.js +496 -0
  100. package/dist/src/transport/bot-webhook/inbound.js +4 -0
  101. package/dist/src/transport/bot-webhook/message-shape.js +98 -0
  102. package/dist/src/transport/bot-webhook/protocol.js +124 -0
  103. package/dist/src/transport/bot-webhook/reply.js +9 -0
  104. package/dist/src/transport/bot-webhook/request-handler.js +285 -0
  105. package/dist/src/transport/bot-webhook/session.js +15 -0
  106. package/dist/src/transport/bot-ws/inbound.js +147 -0
  107. package/dist/src/transport/bot-ws/media.js +236 -0
  108. package/dist/src/transport/bot-ws/reply.js +310 -0
  109. package/dist/src/transport/bot-ws/sdk-adapter.js +257 -0
  110. package/dist/src/transport/bot-ws/session.js +15 -0
  111. package/dist/src/transport/http/common.js +78 -0
  112. package/dist/src/transport/http/registry.js +71 -0
  113. package/dist/src/transport/http/request-handler.js +51 -0
  114. package/{src/transport/index.ts → dist/src/transport/index.js} +2 -10
  115. package/dist/src/types/account.js +1 -0
  116. package/dist/src/types/config.js +1 -0
  117. package/dist/src/types/constants.js +28 -0
  118. package/dist/src/types/events.js +1 -0
  119. package/dist/src/types/index.js +1 -0
  120. package/dist/src/types/legacy-stream.js +1 -0
  121. package/dist/src/types/message.js +5 -0
  122. package/dist/src/types/runtime-context.js +1 -0
  123. package/dist/src/types/runtime.js +1 -0
  124. package/dist/src/types.js +1 -0
  125. package/dist/src/upstream/index.js +111 -0
  126. package/dist/src/wecom_msg_adapter/markdown_adapter.js +280 -0
  127. package/openclaw.plugin.json +15 -0
  128. package/package.json +18 -1
  129. package/.github/workflows/release.yml +0 -143
  130. package/GOVERNANCE.md +0 -26
  131. package/SKILLS_CAL.md +0 -895
  132. package/SKILLS_DOC.md +0 -2288
  133. package/UPSTREAM_CONFIG.md +0 -170
  134. package/UPSTREAM_PLAN.md +0 -175
  135. package/assets/01.bot-add.png +0 -0
  136. package/assets/01.bot-setp2.png +0 -0
  137. package/assets/01.image.jpg +0 -0
  138. package/assets/02.agent.add.png +0 -0
  139. package/assets/02.agent.api-set.png +0 -0
  140. package/assets/02.image.jpg +0 -0
  141. package/assets/03.agent.page.png +0 -0
  142. package/assets/03.bot.page.png +0 -0
  143. package/assets/link-me.jpg +0 -0
  144. package/assets/register.png +0 -0
  145. package/changelog/v2.2.28.md +0 -70
  146. package/changelog/v2.3.10.md +0 -17
  147. package/changelog/v2.3.11.md +0 -19
  148. package/changelog/v2.3.12.md +0 -25
  149. package/changelog/v2.3.13.md +0 -19
  150. package/changelog/v2.3.14.md +0 -48
  151. package/changelog/v2.3.15.md +0 -15
  152. package/changelog/v2.3.16.md +0 -11
  153. package/changelog/v2.3.18.md +0 -22
  154. package/changelog/v2.3.19.md +0 -73
  155. package/changelog/v2.3.2.md +0 -28
  156. package/changelog/v2.3.26.md +0 -21
  157. package/changelog/v2.3.27.md +0 -33
  158. package/changelog/v2.3.4.md +0 -20
  159. package/changelog/v2.3.9.md +0 -22
  160. package/changelog/v2.4.12.md +0 -37
  161. package/changelog/v2.4.16.md +0 -19
  162. package/compat-single-account.md +0 -148
  163. package/index.test.ts +0 -38
  164. package/scripts/test-proxy.ts +0 -70
  165. package/src/accounts.ts +0 -34
  166. package/src/agent/api-client.upload.test.ts +0 -109
  167. package/src/agent/handler.event-filter.test.ts +0 -100
  168. package/src/agent/handler.ts +0 -1105
  169. package/src/agent/index.ts +0 -12
  170. package/src/app/account-runtime.ts +0 -276
  171. package/src/app/bootstrap.ts +0 -29
  172. package/src/app/index.ts +0 -192
  173. package/src/capability/agent/delivery-service.ts +0 -87
  174. package/src/capability/agent/fallback-policy.ts +0 -13
  175. package/src/capability/agent/ingress-service.ts +0 -38
  176. package/src/capability/agent/upstream-delivery-service.ts +0 -96
  177. package/src/capability/bot/dispatch-config.ts +0 -47
  178. package/src/capability/bot/fallback-delivery.ts +0 -178
  179. package/src/capability/bot/local-path-delivery.ts +0 -215
  180. package/src/capability/bot/sandbox-media.test.ts +0 -221
  181. package/src/capability/bot/sandbox-media.ts +0 -176
  182. package/src/capability/bot/service.ts +0 -56
  183. package/src/capability/bot/stream-delivery.ts +0 -379
  184. package/src/capability/bot/stream-finalizer.ts +0 -120
  185. package/src/capability/bot/stream-orchestrator.ts +0 -371
  186. package/src/capability/bot/types.ts +0 -8
  187. package/src/capability/calendar/SKILLS_CHECKLIST.md +0 -251
  188. package/src/capability/calendar/tool.ts +0 -417
  189. package/src/capability/calendar/types.ts +0 -309
  190. package/src/capability/doc/tool.ts +0 -1629
  191. package/src/capability/doc/types.ts +0 -792
  192. package/src/capability/mcp/index.ts +0 -10
  193. package/src/capability/mcp/schema.ts +0 -107
  194. package/src/capability/mcp/tool.ts +0 -174
  195. package/src/capability/mcp/transport.ts +0 -394
  196. package/src/channel.config.test.ts +0 -147
  197. package/src/channel.lifecycle.test.ts +0 -255
  198. package/src/channel.meta.test.ts +0 -26
  199. package/src/channel.ts +0 -256
  200. package/src/config/accounts.resolve.test.ts +0 -75
  201. package/src/config/accounts.ts +0 -296
  202. package/src/config/derived-paths.test.ts +0 -111
  203. package/src/config/derived-paths.ts +0 -41
  204. package/src/config/index.ts +0 -26
  205. package/src/config/media.test.ts +0 -113
  206. package/src/config/media.ts +0 -139
  207. package/src/config/network.ts +0 -53
  208. package/src/config/routing.test.ts +0 -88
  209. package/src/config/routing.ts +0 -26
  210. package/src/config/runtime-config.ts +0 -46
  211. package/src/config/schema.ts +0 -90
  212. package/src/context-store.ts +0 -297
  213. package/src/crypto/index.ts +0 -24
  214. package/src/crypto.test.ts +0 -32
  215. package/src/crypto.ts +0 -176
  216. package/src/domain/models.ts +0 -7
  217. package/src/domain/policies.ts +0 -36
  218. package/src/dynamic-agent.account-scope.test.ts +0 -17
  219. package/src/gateway-monitor.ts +0 -181
  220. package/src/http.ts +0 -145
  221. package/src/media.test.ts +0 -82
  222. package/src/monitor/limits.ts +0 -7
  223. package/src/monitor/state.queue.test.ts +0 -185
  224. package/src/monitor/state.ts +0 -34
  225. package/src/monitor.active.test.ts +0 -245
  226. package/src/monitor.inbound-filter.test.ts +0 -63
  227. package/src/monitor.integration.test.ts +0 -208
  228. package/src/monitor.ts +0 -121
  229. package/src/monitor.webhook.test.ts +0 -774
  230. package/src/observability/audit-log.ts +0 -48
  231. package/src/observability/legacy-operational-event-store.ts +0 -36
  232. package/src/observability/raw-envelope-log.ts +0 -28
  233. package/src/observability/status-registry.ts +0 -13
  234. package/src/observability/transport-session-view.ts +0 -14
  235. package/src/onboarding.test.ts +0 -336
  236. package/src/onboarding.ts +0 -704
  237. package/src/outbound.test.ts +0 -1271
  238. package/src/outbound.ts +0 -746
  239. package/src/runtime/dispatcher.ts +0 -71
  240. package/src/runtime/outbound-intent.ts +0 -4
  241. package/src/runtime/reply-orchestrator.test.ts +0 -71
  242. package/src/runtime/reply-orchestrator.ts +0 -67
  243. package/src/runtime/routing-bridge.test.ts +0 -115
  244. package/src/runtime/routing-bridge.ts +0 -44
  245. package/src/runtime/session-manager.test.ts +0 -174
  246. package/src/runtime/session-manager.ts +0 -139
  247. package/src/runtime/source-registry.ts +0 -249
  248. package/src/runtime.ts +0 -14
  249. package/src/shared/command-auth.ts +0 -87
  250. package/src/shared/media-asset.ts +0 -78
  251. package/src/shared/media-service.test.ts +0 -111
  252. package/src/shared/media-service.ts +0 -84
  253. package/src/shared/media-types.ts +0 -5
  254. package/src/shared/xml-parser.test.ts +0 -50
  255. package/src/store/active-reply-store.ts +0 -42
  256. package/src/store/interfaces.ts +0 -11
  257. package/src/store/memory-store.ts +0 -43
  258. package/src/store/stream-batch-store.ts +0 -350
  259. package/src/transport/agent-api/client.ts +0 -277
  260. package/src/transport/agent-api/core.ts +0 -463
  261. package/src/transport/agent-api/delivery.ts +0 -41
  262. package/src/transport/agent-api/media-upload.ts +0 -11
  263. package/src/transport/agent-api/reply.ts +0 -39
  264. package/src/transport/agent-api/upstream-delivery.ts +0 -45
  265. package/src/transport/agent-api/upstream-media-upload.ts +0 -70
  266. package/src/transport/agent-api/upstream-reply.ts +0 -43
  267. package/src/transport/agent-callback/http-handler.ts +0 -47
  268. package/src/transport/agent-callback/inbound.ts +0 -5
  269. package/src/transport/agent-callback/reply.ts +0 -13
  270. package/src/transport/agent-callback/request-handler.ts +0 -244
  271. package/src/transport/agent-callback/session.ts +0 -23
  272. package/src/transport/bot-webhook/active-reply.ts +0 -39
  273. package/src/transport/bot-webhook/http-handler.ts +0 -48
  274. package/src/transport/bot-webhook/inbound-normalizer.test.ts +0 -433
  275. package/src/transport/bot-webhook/inbound-normalizer.ts +0 -558
  276. package/src/transport/bot-webhook/inbound.ts +0 -5
  277. package/src/transport/bot-webhook/message-shape.ts +0 -92
  278. package/src/transport/bot-webhook/protocol.ts +0 -148
  279. package/src/transport/bot-webhook/reply.ts +0 -15
  280. package/src/transport/bot-webhook/request-handler.ts +0 -394
  281. package/src/transport/bot-webhook/session.ts +0 -23
  282. package/src/transport/bot-ws/inbound.test.ts +0 -290
  283. package/src/transport/bot-ws/inbound.ts +0 -163
  284. package/src/transport/bot-ws/media.test.ts +0 -44
  285. package/src/transport/bot-ws/media.ts +0 -321
  286. package/src/transport/bot-ws/reply.test.ts +0 -450
  287. package/src/transport/bot-ws/reply.ts +0 -365
  288. package/src/transport/bot-ws/sdk-adapter.test.ts +0 -187
  289. package/src/transport/bot-ws/sdk-adapter.ts +0 -314
  290. package/src/transport/bot-ws/session.ts +0 -28
  291. package/src/transport/http/common.ts +0 -109
  292. package/src/transport/http/registry.ts +0 -92
  293. package/src/transport/http/request-handler.ts +0 -84
  294. package/src/types/account.ts +0 -70
  295. package/src/types/config.ts +0 -114
  296. package/src/types/constants.ts +0 -31
  297. package/src/types/events.ts +0 -21
  298. package/src/types/global.d.ts +0 -9
  299. package/src/types/index.ts +0 -17
  300. package/src/types/legacy-stream.ts +0 -50
  301. package/src/types/message.ts +0 -189
  302. package/src/types/runtime-context.ts +0 -28
  303. package/src/types/runtime.ts +0 -165
  304. package/src/types.ts +0 -41
  305. package/src/upstream/index.ts +0 -150
  306. package/src/upstream.test.ts +0 -84
  307. package/src/wecom_msg_adapter/markdown_adapter.ts +0 -331
  308. package/tsconfig.json +0 -22
  309. package/vitest.config.ts +0 -26
  310. /package/{src/capability/agent/index.ts → dist/src/capability/agent/index.js} +0 -0
  311. /package/{src/capability/bot/index.ts → dist/src/capability/bot/index.js} +0 -0
  312. /package/{src/capability/calendar/index.ts → dist/src/capability/calendar/index.js} +0 -0
  313. /package/{src/capability/index.ts → dist/src/capability/index.js} +0 -0
@@ -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 {};