@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
@@ -4,23 +4,13 @@
4
4
  * 为每个用户/群组自动生成独立的 Agent ID,实现会话隔离。
5
5
  * 参考: openclaw-plugin-wecom/dynamic-agent.js
6
6
  */
7
-
8
- import type { OpenClawConfig } from "openclaw/plugin-sdk";
9
-
10
- export interface DynamicAgentConfig {
11
- enabled: boolean;
12
- dmCreateAgent: boolean;
13
- groupEnabled: boolean;
14
- adminUsers: string[];
15
- }
16
-
17
7
  /**
18
8
  * **getDynamicAgentConfig (读取动态 Agent 配置)**
19
9
  *
20
10
  * 从全局配置中读取动态 Agent 配置,提供默认值。
21
11
  */
22
- export function getDynamicAgentConfig(config: OpenClawConfig): DynamicAgentConfig {
23
- const dynamicAgents = (config as { channels?: { wecom?: { dynamicAgents?: Partial<DynamicAgentConfig> } } })?.channels?.wecom?.dynamicAgents;
12
+ export function getDynamicAgentConfig(config) {
13
+ const dynamicAgents = config?.channels?.wecom?.dynamicAgents;
24
14
  return {
25
15
  enabled: dynamicAgents?.enabled ?? false,
26
16
  dmCreateAgent: dynamicAgents?.dmCreateAgent ?? true,
@@ -28,114 +18,89 @@ export function getDynamicAgentConfig(config: OpenClawConfig): DynamicAgentConfi
28
18
  adminUsers: dynamicAgents?.adminUsers ?? [],
29
19
  };
30
20
  }
31
-
32
- function sanitizeDynamicIdPart(value: string): string {
21
+ function sanitizeDynamicIdPart(value) {
33
22
  return String(value)
34
23
  .trim()
35
24
  .toLowerCase()
36
25
  .replace(/[^a-z0-9_-]/g, "_");
37
26
  }
38
-
39
27
  /**
40
28
  * **generateAgentId (生成动态 Agent ID)**
41
29
  *
42
30
  * 根据账号 + 聊天类型 + 对端 ID 生成确定性的 Agent ID,避免多账号串会话。
43
31
  * 格式: wecom-{accountId}-{type}-{sanitizedPeerId}
44
32
  */
45
- export function generateAgentId(chatType: "dm" | "group", peerId: string, accountId?: string): string {
33
+ export function generateAgentId(chatType, peerId, accountId) {
46
34
  const sanitizedPeer = sanitizeDynamicIdPart(peerId) || "unknown";
47
35
  const sanitizedAccountId = sanitizeDynamicIdPart(accountId ?? "default") || "default";
48
36
  return `wecom-${sanitizedAccountId}-${chatType}-${sanitizedPeer}`;
49
37
  }
50
-
51
- export function buildAgentSessionTarget(userId: string, accountId?: string): string {
38
+ export function buildAgentSessionTarget(userId, accountId) {
52
39
  const normalizedUserId = String(userId).trim();
53
40
  const sanitizedAccountId = sanitizeDynamicIdPart(accountId ?? "default") || "default";
54
41
  // Always use explicit user: prefix to avoid ambiguity with numeric party IDs
55
42
  return `wecom-agent:${sanitizedAccountId}:user:${normalizedUserId}`;
56
43
  }
57
-
58
44
  /**
59
45
  * **shouldUseDynamicAgent (检查是否使用动态 Agent)**
60
46
  *
61
47
  * 根据配置和发送者信息判断是否应使用动态 Agent。
62
48
  * 管理员(adminUsers)始终绕过动态路由,使用主 Agent。
63
49
  */
64
- export function shouldUseDynamicAgent(params: {
65
- chatType: "dm" | "group";
66
- senderId: string;
67
- config: OpenClawConfig;
68
- }): boolean {
50
+ export function shouldUseDynamicAgent(params) {
69
51
  const { chatType, senderId, config } = params;
70
52
  const dynamicConfig = getDynamicAgentConfig(config);
71
-
72
53
  if (!dynamicConfig.enabled) {
73
54
  return false;
74
55
  }
75
-
76
56
  // 管理员绕过动态路由
77
57
  const sender = String(senderId).trim().toLowerCase();
78
- const isAdmin = dynamicConfig.adminUsers.some(
79
- (admin) => admin.trim().toLowerCase() === sender
80
- );
58
+ const isAdmin = dynamicConfig.adminUsers.some((admin) => admin.trim().toLowerCase() === sender);
81
59
  if (isAdmin) {
82
60
  return false;
83
61
  }
84
-
85
62
  if (chatType === "group") {
86
63
  return dynamicConfig.groupEnabled;
87
64
  }
88
65
  return dynamicConfig.dmCreateAgent;
89
66
  }
90
-
91
67
  /**
92
68
  * 内存中已确保的 Agent ID(避免重复写入)
93
69
  */
94
- const ensuredDynamicAgentIds = new Set<string>();
95
-
70
+ const ensuredDynamicAgentIds = new Set();
96
71
  /**
97
72
  * 写入队列(避免并发冲突)
98
73
  */
99
- let ensureDynamicAgentWriteQueue: Promise<void> = Promise.resolve();
100
-
74
+ let ensureDynamicAgentWriteQueue = Promise.resolve();
101
75
  /**
102
76
  * 将 Agent ID 插入 agents.list(如果不存在)
103
77
  */
104
- function upsertAgentIdOnlyEntry(cfg: Record<string, unknown>, agentId: string): boolean {
78
+ function upsertAgentIdOnlyEntry(cfg, agentId) {
105
79
  if (!cfg.agents || typeof cfg.agents !== "object") {
106
80
  cfg.agents = {};
107
81
  }
108
-
109
- const agentsObj = cfg.agents as Record<string, unknown>;
110
- const currentList: Array<{ id: string }> = Array.isArray(agentsObj.list) ? agentsObj.list as Array<{ id: string }> : [];
111
- const existingIds = new Set(
112
- currentList
113
- .map((entry) => entry?.id?.trim().toLowerCase())
114
- .filter((id): id is string => Boolean(id))
115
- );
116
-
82
+ const agentsObj = cfg.agents;
83
+ const currentList = Array.isArray(agentsObj.list) ? agentsObj.list : [];
84
+ const existingIds = new Set(currentList
85
+ .map((entry) => entry?.id?.trim().toLowerCase())
86
+ .filter((id) => Boolean(id)));
117
87
  let changed = false;
118
88
  const nextList = [...currentList];
119
-
120
89
  // 首次创建时保留 main 作为默认
121
90
  if (nextList.length === 0) {
122
91
  nextList.push({ id: "main" });
123
92
  existingIds.add("main");
124
93
  changed = true;
125
94
  }
126
-
127
95
  if (!existingIds.has(agentId.toLowerCase())) {
128
96
  nextList.push({ id: agentId });
129
97
  changed = true;
130
98
  }
131
-
132
99
  if (changed) {
133
100
  agentsObj.list = nextList;
134
101
  }
135
-
136
102
  return changed;
137
103
  }
138
-
139
104
  /**
140
105
  * **ensureDynamicAgentListed (确保动态 Agent 已添加到 agents.list)**
141
106
  *
@@ -146,40 +111,38 @@ function upsertAgentIdOnlyEntry(cfg: Record<string, unknown>, agentId: string):
146
111
  * - 异步:不阻塞消息处理流程
147
112
  */
148
113
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
149
- export async function ensureDynamicAgentListed(agentId: string, runtime: any): Promise<void> {
114
+ export async function ensureDynamicAgentListed(agentId, runtime) {
150
115
  const normalizedId = String(agentId).trim().toLowerCase();
151
- if (!normalizedId) return;
152
- if (ensuredDynamicAgentIds.has(normalizedId)) return;
153
-
116
+ if (!normalizedId)
117
+ return;
118
+ if (ensuredDynamicAgentIds.has(normalizedId))
119
+ return;
154
120
  const configRuntime = runtime?.config;
155
- if (!configRuntime?.loadConfig || !configRuntime?.writeConfigFile) return;
156
-
121
+ if (!configRuntime?.loadConfig || !configRuntime?.writeConfigFile)
122
+ return;
157
123
  ensureDynamicAgentWriteQueue = ensureDynamicAgentWriteQueue
158
124
  .then(async () => {
159
- if (ensuredDynamicAgentIds.has(normalizedId)) return;
160
-
161
- const latestConfig = configRuntime.loadConfig!();
162
- if (!latestConfig || typeof latestConfig !== "object") return;
163
-
164
- const changed = upsertAgentIdOnlyEntry(latestConfig as Record<string, unknown>, normalizedId);
165
- if (changed) {
166
- await configRuntime.writeConfigFile!(latestConfig as unknown);
167
- }
168
-
169
- ensuredDynamicAgentIds.add(normalizedId);
170
- })
125
+ if (ensuredDynamicAgentIds.has(normalizedId))
126
+ return;
127
+ const latestConfig = configRuntime.loadConfig();
128
+ if (!latestConfig || typeof latestConfig !== "object")
129
+ return;
130
+ const changed = upsertAgentIdOnlyEntry(latestConfig, normalizedId);
131
+ if (changed) {
132
+ await configRuntime.writeConfigFile(latestConfig);
133
+ }
134
+ ensuredDynamicAgentIds.add(normalizedId);
135
+ })
171
136
  .catch((err) => {
172
- console.warn(`[wecom] 动态 Agent 添加失败: ${normalizedId}`, err);
173
- });
174
-
137
+ console.warn(`[wecom] 动态 Agent 添加失败: ${normalizedId}`, err);
138
+ });
175
139
  await ensureDynamicAgentWriteQueue;
176
140
  }
177
-
178
141
  /**
179
142
  * **resetEnsuredCache (重置已确保缓存)**
180
143
  *
181
144
  * 主要用于测试场景,重置内存中的缓存状态。
182
145
  */
183
- export function resetEnsuredCache(): void {
146
+ export function resetEnsuredCache() {
184
147
  ensuredDynamicAgentIds.clear();
185
148
  }
@@ -0,0 +1,139 @@
1
+ import { listWecomAccountIds, resolveWecomAccount, resolveWecomAccountConflict, } from "./config/index.js";
2
+ import { createAccountRuntime } from "./app/bootstrap.js";
3
+ import { registerAccountRuntime, unregisterAccountRuntime } from "./app/index.js";
4
+ import { WecomBotCapabilityService } from "./capability/bot/index.js";
5
+ import { WecomAgentIngressService } from "./capability/agent/index.js";
6
+ const accountRouteRegistry = new Map();
7
+ function logRegisteredRouteSummary(ctx, preferredOrder) {
8
+ const seen = new Set();
9
+ const orderedAccountIds = [
10
+ ...preferredOrder.filter((accountId) => accountRouteRegistry.has(accountId)),
11
+ ...Array.from(accountRouteRegistry.keys())
12
+ .filter((accountId) => !seen.has(accountId))
13
+ .sort((a, b) => a.localeCompare(b)),
14
+ ].filter((accountId) => {
15
+ if (seen.has(accountId))
16
+ return false;
17
+ seen.add(accountId);
18
+ return true;
19
+ });
20
+ const entries = orderedAccountIds
21
+ .map((accountId) => {
22
+ const routes = accountRouteRegistry.get(accountId);
23
+ if (!routes)
24
+ return undefined;
25
+ const botText = routes.botPaths.length > 0 ? routes.botPaths.join(", ") : "未启用";
26
+ const agentText = routes.agentPaths.length > 0 ? routes.agentPaths.join(", ") : "未启用";
27
+ return `accountId=${accountId}(Bot: ${botText};Agent: ${agentText})`;
28
+ })
29
+ .filter((entry) => Boolean(entry));
30
+ const summary = entries.length > 0 ? entries.join("; ") : "无";
31
+ ctx.log?.info(`[${ctx.account.accountId}] 已注册账号路由汇总:${summary}`);
32
+ }
33
+ function resolveExpectedRouteSummaryAccountIds(cfg) {
34
+ return listWecomAccountIds(cfg)
35
+ .filter((accountId) => {
36
+ const conflict = resolveWecomAccountConflict({ cfg, accountId });
37
+ if (conflict)
38
+ return false;
39
+ const account = resolveWecomAccount({ cfg, accountId });
40
+ if (!account.enabled || !account.configured)
41
+ return false;
42
+ return Boolean(account.bot?.configured || account.agent?.configured);
43
+ })
44
+ .sort((a, b) => a.localeCompare(b));
45
+ }
46
+ function waitForAbortSignal(abortSignal) {
47
+ if (abortSignal.aborted) {
48
+ return Promise.resolve();
49
+ }
50
+ return new Promise((resolve) => {
51
+ const onAbort = () => {
52
+ abortSignal.removeEventListener("abort", onAbort);
53
+ resolve();
54
+ };
55
+ abortSignal.addEventListener("abort", onAbort, { once: true });
56
+ });
57
+ }
58
+ /**
59
+ * Keeps WeCom webhook targets registered for the account lifecycle.
60
+ * The promise only settles after gateway abort/reload signals shutdown.
61
+ */
62
+ export async function monitorWecomProvider(ctx) {
63
+ const account = ctx.account;
64
+ const cfg = ctx.cfg;
65
+ const expectedRouteSummaryAccountIds = resolveExpectedRouteSummaryAccountIds(cfg);
66
+ const conflict = resolveWecomAccountConflict({
67
+ cfg,
68
+ accountId: account.accountId,
69
+ });
70
+ if (conflict) {
71
+ ctx.setStatus({
72
+ accountId: account.accountId,
73
+ running: false,
74
+ configured: false,
75
+ lastError: conflict.message,
76
+ });
77
+ throw new Error(conflict.message);
78
+ }
79
+ const bot = account.bot;
80
+ const agent = account.agent;
81
+ const botConfigured = Boolean(bot?.configured);
82
+ const agentConfigured = Boolean(agent?.configured);
83
+ if (!botConfigured && !agentConfigured) {
84
+ ctx.log?.warn(`[${account.accountId}] wecom not configured; channel is idle`);
85
+ ctx.setStatus({ accountId: account.accountId, running: false, configured: false });
86
+ await waitForAbortSignal(ctx.abortSignal);
87
+ return;
88
+ }
89
+ const accountRuntime = createAccountRuntime(ctx);
90
+ registerAccountRuntime(accountRuntime);
91
+ const botPaths = [];
92
+ const agentPaths = [];
93
+ const runtimeEnv = {
94
+ log: (message) => ctx.log?.info(message),
95
+ error: (message) => ctx.log?.error(message),
96
+ };
97
+ const botService = new WecomBotCapabilityService(accountRuntime, cfg, runtimeEnv);
98
+ const agentIngress = new WecomAgentIngressService(accountRuntime, cfg, runtimeEnv);
99
+ try {
100
+ ctx.log?.info(`[${account.accountId}] wecom runtime start bot=${bot?.primaryTransport ?? "disabled"} agent=${agentConfigured ? "callback/api" : "disabled"}`);
101
+ const botRegistration = botService.start();
102
+ if (botRegistration) {
103
+ botPaths.push(...botRegistration.descriptors);
104
+ ctx.log?.info(`[${account.accountId}] wecom bot ${botRegistration.transport} started: ${botRegistration.descriptors.join(", ")}`);
105
+ }
106
+ const agentRegistration = agentIngress.start();
107
+ if (agentRegistration) {
108
+ agentPaths.push(...agentRegistration.descriptors);
109
+ ctx.log?.info(`[${account.accountId}] wecom agent ${agentRegistration.transport} started: ${agentRegistration.descriptors.join(", ")}`);
110
+ }
111
+ accountRouteRegistry.set(account.accountId, { botPaths, agentPaths });
112
+ const shouldLogSummary = expectedRouteSummaryAccountIds.length <= 1 ||
113
+ expectedRouteSummaryAccountIds.every((accountId) => accountRouteRegistry.has(accountId));
114
+ if (shouldLogSummary) {
115
+ logRegisteredRouteSummary(ctx, expectedRouteSummaryAccountIds);
116
+ }
117
+ ctx.setStatus({
118
+ running: true,
119
+ configured: true,
120
+ webhookPath: botPaths[0] ?? agentPaths[0] ?? null,
121
+ lastStartAt: Date.now(),
122
+ ...accountRuntime.buildRuntimeStatus(),
123
+ });
124
+ ctx.log?.info(`[${account.accountId}] runtime status health=${accountRuntime.buildRuntimeStatus().health} transports=${(accountRuntime.buildRuntimeStatus().transportSessions ?? []).join(" | ") || "none"}`);
125
+ await waitForAbortSignal(ctx.abortSignal);
126
+ }
127
+ finally {
128
+ botService.stop();
129
+ agentIngress.stop();
130
+ accountRouteRegistry.delete(account.accountId);
131
+ unregisterAccountRuntime(account.accountId);
132
+ ctx.setStatus({
133
+ running: false,
134
+ lastStopAt: Date.now(),
135
+ ...accountRuntime.buildRuntimeStatus(),
136
+ });
137
+ ctx.log?.info(`[${account.accountId}] wecom runtime stopped`);
138
+ }
139
+ }
@@ -0,0 +1,114 @@
1
+ import { ProxyAgent, fetch as undiciFetch } from "undici";
2
+ const proxyDispatchers = new Map();
3
+ /**
4
+ * **getProxyDispatcher (获取代理 Dispatcher)**
5
+ *
6
+ * 缓存并复用 ProxyAgent,避免重复创建连接池。
7
+ */
8
+ function getProxyDispatcher(proxyUrl) {
9
+ const existing = proxyDispatchers.get(proxyUrl);
10
+ if (existing)
11
+ return existing;
12
+ const created = new ProxyAgent(proxyUrl);
13
+ proxyDispatchers.set(proxyUrl, created);
14
+ return created;
15
+ }
16
+ function summarizeHttpTarget(input) {
17
+ try {
18
+ const url = typeof input === "string" ? new URL(input) : input;
19
+ return `${url.origin}${url.pathname}`;
20
+ }
21
+ catch {
22
+ return String(input);
23
+ }
24
+ }
25
+ function mergeAbortSignal(params) {
26
+ const signals = [];
27
+ if (params.signal)
28
+ signals.push(params.signal);
29
+ if (params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0) {
30
+ signals.push(AbortSignal.timeout(params.timeoutMs));
31
+ }
32
+ if (!signals.length)
33
+ return undefined;
34
+ if (signals.length === 1)
35
+ return signals[0];
36
+ return AbortSignal.any(signals);
37
+ }
38
+ /**
39
+ * **wecomFetch (统一 HTTP 请求)**
40
+ *
41
+ * 基于 `undici` 的 fetch 封装,自动处理 ProxyAgent 和 Timeout。
42
+ * 所有对企业微信 API 的调用都应经过此函数。
43
+ */
44
+ export async function wecomFetch(input, init, opts) {
45
+ const proxyUrl = opts?.proxyUrl?.trim() ?? "";
46
+ const dispatcher = proxyUrl ? getProxyDispatcher(proxyUrl) : undefined;
47
+ const startedAt = Date.now();
48
+ const method = (init?.method ?? "GET").toUpperCase();
49
+ const target = summarizeHttpTarget(input);
50
+ const initSignal = init?.signal ?? undefined;
51
+ const signal = mergeAbortSignal({ signal: opts?.signal ?? initSignal, timeoutMs: opts?.timeoutMs });
52
+ const headers = new Headers(init?.headers ?? {});
53
+ if (!headers.has("User-Agent")) {
54
+ headers.set("User-Agent", "OpenClaw/2.0 (WeCom-Agent)");
55
+ }
56
+ const nextInit = {
57
+ ...(init ?? {}),
58
+ ...(signal ? { signal } : {}),
59
+ ...(dispatcher ? { dispatcher } : {}),
60
+ headers,
61
+ };
62
+ try {
63
+ console.log(`[wecom-http] request method=${method} target=${target} proxy=${proxyUrl || "none"} timeoutMs=${String(opts?.timeoutMs ?? "none")}`);
64
+ const response = await undiciFetch(input, nextInit);
65
+ console.log(`[wecom-http] response method=${method} target=${target} status=${response.status} durationMs=${Date.now() - startedAt}`);
66
+ return response;
67
+ }
68
+ catch (err) {
69
+ if (err instanceof Error && err.name === "TimeoutError") {
70
+ console.error(`[wecom-http] timeout method=${method} target=${target} durationMs=${Date.now() - startedAt} proxy=${proxyUrl || "none"}`);
71
+ }
72
+ else if (err instanceof Error && err.name === "TypeError" && err.message === "fetch failed") {
73
+ const cause = err.cause;
74
+ console.error(`[wecom-http] fetch failed method=${method} target=${target} durationMs=${Date.now() - startedAt} proxy=${proxyUrl || "none"}${cause ? ` cause=${String(cause)}` : ""}`);
75
+ }
76
+ else if (err instanceof Error && err.name === "AbortError") {
77
+ console.error(`[wecom-http] aborted method=${method} target=${target} durationMs=${Date.now() - startedAt} proxy=${proxyUrl || "none"}`);
78
+ }
79
+ throw err;
80
+ }
81
+ }
82
+ /**
83
+ * **readResponseBodyAsBuffer (读取响应 Body)**
84
+ *
85
+ * 将 Response Body 读取为 Buffer,支持最大字节限制以防止内存溢出。
86
+ * 适用于下载媒体文件等场景。
87
+ */
88
+ export async function readResponseBodyAsBuffer(res, maxBytes) {
89
+ if (!res.body)
90
+ return Buffer.alloc(0);
91
+ const limit = maxBytes && Number.isFinite(maxBytes) && maxBytes > 0 ? maxBytes : undefined;
92
+ const chunks = [];
93
+ let total = 0;
94
+ const reader = res.body.getReader();
95
+ while (true) {
96
+ const { done, value } = await reader.read();
97
+ if (done)
98
+ break;
99
+ if (!value)
100
+ continue;
101
+ total += value.byteLength;
102
+ if (limit && total > limit) {
103
+ try {
104
+ await reader.cancel("body too large");
105
+ }
106
+ catch {
107
+ // ignore
108
+ }
109
+ throw new Error(`response body too large (>${limit} bytes)`);
110
+ }
111
+ chunks.push(value);
112
+ }
113
+ return Buffer.concat(chunks.map((c) => Buffer.from(c)));
114
+ }
@@ -1,56 +1,49 @@
1
1
  import crypto from "node:crypto";
2
2
  import { decodeEncodingAESKey, pkcs7Unpad, WECOM_PKCS7_BLOCK_SIZE } from "./crypto.js";
3
- import { readResponseBodyAsBuffer, wecomFetch, type WecomHttpOptions } from "./http.js";
4
-
5
- export type DecryptedWecomMedia = {
6
- buffer: Buffer;
7
- sourceContentType?: string;
8
- sourceFilename?: string;
9
- sourceUrl?: string;
10
- };
11
-
12
- function normalizeMime(contentType?: string | null): string | undefined {
3
+ import { readResponseBodyAsBuffer, wecomFetch } from "./http.js";
4
+ function normalizeMime(contentType) {
13
5
  const raw = String(contentType ?? "").trim();
14
- if (!raw) return undefined;
6
+ if (!raw)
7
+ return undefined;
15
8
  return raw.split(";")[0]?.trim().toLowerCase() || undefined;
16
9
  }
17
-
18
- function extractFilenameFromContentDisposition(disposition?: string | null): string | undefined {
10
+ function extractFilenameFromContentDisposition(disposition) {
19
11
  const raw = String(disposition ?? "").trim();
20
- if (!raw) return undefined;
21
-
12
+ if (!raw)
13
+ return undefined;
22
14
  const star = raw.match(/filename\*\s*=\s*([^;]+)/i);
23
15
  if (star?.[1]) {
24
16
  const v = star[1].trim().replace(/^UTF-8''/i, "").replace(/^"(.*)"$/, "$1");
25
17
  try {
26
18
  const decoded = decodeURIComponent(v);
27
- if (decoded.trim()) return decoded.trim();
28
- } catch { /* ignore */ }
29
- if (v.trim()) return v.trim();
19
+ if (decoded.trim())
20
+ return decoded.trim();
21
+ }
22
+ catch { /* ignore */ }
23
+ if (v.trim())
24
+ return v.trim();
30
25
  }
31
-
32
26
  const plain = raw.match(/filename\s*=\s*([^;]+)/i);
33
27
  if (plain?.[1]) {
34
28
  const v = plain[1].trim().replace(/^"(.*)"$/, "$1").trim();
35
- if (v) return v;
29
+ if (v)
30
+ return v;
36
31
  }
37
32
  return undefined;
38
33
  }
39
-
40
34
  /**
41
35
  * **decryptWecomMedia (解密企业微信媒体文件)**
42
- *
36
+ *
43
37
  * 简易封装:直接传入 URL 和 AES Key 下载并解密。
44
38
  * 企业微信媒体文件使用与消息体相同的 AES-256-CBC 加密,IV 为 AES Key 前16字节。
45
39
  * 解密后需移除 PKCS#7 填充。
46
40
  */
47
- export async function decryptWecomMedia(url: string, encodingAESKey: string, maxBytes?: number): Promise<Buffer> {
41
+ export async function decryptWecomMedia(url, encodingAESKey, maxBytes) {
48
42
  return decryptWecomMediaWithHttp(url, encodingAESKey, { maxBytes });
49
43
  }
50
-
51
44
  /**
52
45
  * **decryptWecomMediaWithHttp (解密企业微信媒体 - 高级)**
53
- *
46
+ *
54
47
  * 支持传递 HTTP 选项(如 Proxy、Timeout)。
55
48
  * 流程:
56
49
  * 1. 下载加密内容。
@@ -58,26 +51,17 @@ export async function decryptWecomMedia(url: string, encodingAESKey: string, max
58
51
  * 3. AES-CBC 解密。
59
52
  * 4. PKCS#7 去除填充。
60
53
  */
61
- export async function decryptWecomMediaWithHttp(
62
- url: string,
63
- encodingAESKey: string,
64
- params?: { maxBytes?: number; http?: WecomHttpOptions },
65
- ): Promise<Buffer> {
54
+ export async function decryptWecomMediaWithHttp(url, encodingAESKey, params) {
66
55
  const decrypted = await decryptWecomMediaWithMeta(url, encodingAESKey, params);
67
56
  return decrypted.buffer;
68
57
  }
69
-
70
58
  /**
71
59
  * **decryptWecomMediaWithMeta (解密企业微信媒体并返回源信息)**
72
- *
60
+ *
73
61
  * 在返回解密结果的同时,保留下载响应中的元信息(content-type / filename / final url),
74
62
  * 供上层更准确地推断文件后缀和 MIME。
75
63
  */
76
- export async function decryptWecomMediaWithMeta(
77
- url: string,
78
- encodingAESKey: string,
79
- params?: { maxBytes?: number; http?: WecomHttpOptions },
80
- ): Promise<DecryptedWecomMedia> {
64
+ export async function decryptWecomMediaWithMeta(url, encodingAESKey, params) {
81
65
  // 1. Download encrypted content
82
66
  const res = await wecomFetch(url, undefined, { ...params?.http, timeoutMs: params?.http?.timeoutMs ?? 15_000 });
83
67
  if (!res.ok) {
@@ -87,11 +71,9 @@ export async function decryptWecomMediaWithMeta(
87
71
  const sourceFilename = extractFilenameFromContentDisposition(res.headers.get("content-disposition"));
88
72
  const sourceUrl = res.url || url;
89
73
  const encryptedData = await readResponseBodyAsBuffer(res, params?.maxBytes);
90
-
91
74
  // 2. Prepare Key and IV
92
75
  const aesKey = decodeEncodingAESKey(encodingAESKey);
93
76
  const iv = aesKey.subarray(0, 16);
94
-
95
77
  // 3. Decrypt
96
78
  const decipher = crypto.createDecipheriv("aes-256-cbc", aesKey, iv);
97
79
  decipher.setAutoPadding(false); // We handle padding manually
@@ -99,7 +81,6 @@ export async function decryptWecomMediaWithMeta(
99
81
  decipher.update(encryptedData),
100
82
  decipher.final(),
101
83
  ]);
102
-
103
84
  // 4. Unpad
104
85
  // Note: Unlike msg bodies, usually removing PKCS#7 padding is enough for media files.
105
86
  // The Python SDK logic: pad_len = decrypted_data[-1]; decrypted_data = decrypted_data[:-pad_len]
@@ -0,0 +1,7 @@
1
+ export const LIMITS = {
2
+ STREAM_TTL_MS: 10 * 60 * 1000,
3
+ ACTIVE_REPLY_TTL_MS: 60 * 60 * 1000,
4
+ DEFAULT_DEBOUNCE_MS: 500,
5
+ STREAM_MAX_BYTES: 20_480,
6
+ REQUEST_TIMEOUT_MS: 15_000,
7
+ };
@@ -0,0 +1,28 @@
1
+ import { LegacyOperationalEventStore } from "../observability/legacy-operational-event-store.js";
2
+ import { ActiveReplyStore } from "../store/active-reply-store.js";
3
+ import { StreamStore } from "../store/stream-batch-store.js";
4
+ import { LIMITS } from "./limits.js";
5
+ export { LIMITS, StreamStore, ActiveReplyStore };
6
+ class MonitorState {
7
+ streamStore = new StreamStore();
8
+ activeReplyStore = new ActiveReplyStore("multi");
9
+ operationalEvents = new LegacyOperationalEventStore();
10
+ pruneInterval;
11
+ startPruning(intervalMs = 60_000) {
12
+ if (this.pruneInterval)
13
+ return;
14
+ this.pruneInterval = setInterval(() => {
15
+ const now = Date.now();
16
+ this.streamStore.prune(now);
17
+ this.activeReplyStore.prune(now);
18
+ this.operationalEvents.prune(now);
19
+ }, intervalMs);
20
+ }
21
+ stopPruning() {
22
+ if (this.pruneInterval) {
23
+ clearInterval(this.pruneInterval);
24
+ this.pruneInterval = undefined;
25
+ }
26
+ }
27
+ }
28
+ export const monitorState = new MonitorState();