@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
package/src/app/index.ts DELETED
@@ -1,192 +0,0 @@
1
- import type { PluginRuntime } from "openclaw/plugin-sdk";
2
- import { clearWecomSourceAccount } from "../runtime/source-registry.js";
3
- import type { ReplyHandle } from "../types/index.js";
4
- import { WecomAccountRuntime } from "./account-runtime.js";
5
-
6
- let runtime: PluginRuntime | null = null;
7
- const runtimes = new Map<string, WecomAccountRuntime>();
8
- const botWsPushHandles = new Map<string, BotWsPushHandle>();
9
- const activeBotWsReplyHandlesBySession = new Map<string, ReplyHandle>();
10
- const activeBotWsReplyHandlesByPeer = new Map<string, ReplyHandle>();
11
-
12
- export type BotWsPushHandle = {
13
- isConnected: () => boolean;
14
- sendMarkdown: (chatId: string, content: string) => Promise<void>;
15
- replyCommand: (params: {
16
- cmd: string;
17
- body?: Record<string, unknown>;
18
- headers?: ({ req_id?: string } & Record<string, string>) | undefined;
19
- }) => Promise<Record<string, unknown>>;
20
- sendMedia: (params: {
21
- chatId: string;
22
- mediaUrl: string;
23
- text?: string;
24
- mediaLocalRoots?: readonly string[];
25
- maxBytes?: number;
26
- }) => Promise<{
27
- ok: boolean;
28
- messageId?: string;
29
- rejected?: boolean;
30
- rejectReason?: string;
31
- error?: string;
32
- }>;
33
- };
34
-
35
- function normalizeOptional(value: string | null | undefined): string | undefined {
36
- const trimmed = String(value ?? "").trim();
37
- return trimmed || undefined;
38
- }
39
-
40
- function normalizePeerId(value: string | null | undefined): string | undefined {
41
- const trimmed = normalizeOptional(value);
42
- return trimmed ? trimmed.toLowerCase() : undefined;
43
- }
44
-
45
- function buildSessionHandleKey(accountId: string, sessionKey: string): string {
46
- return `${accountId}::session::${sessionKey}`;
47
- }
48
-
49
- function buildPeerHandleKey(
50
- accountId: string,
51
- peerKind: "direct" | "group",
52
- peerId: string,
53
- ): string {
54
- return `${accountId}::peer::${peerKind}::${peerId}`;
55
- }
56
-
57
- export function setWecomRuntime(next: PluginRuntime): void {
58
- runtime = next;
59
- }
60
-
61
- export function getWecomRuntime(): PluginRuntime {
62
- if (!runtime) {
63
- throw new Error("WeCom runtime not initialized");
64
- }
65
- return runtime;
66
- }
67
-
68
- export function registerAccountRuntime(accountRuntime: WecomAccountRuntime): void {
69
- runtimes.set(accountRuntime.account.accountId, accountRuntime);
70
- console.log(`[wecom-runtime] register account=${accountRuntime.account.accountId}`);
71
- }
72
-
73
- export function getAccountRuntime(accountId: string): WecomAccountRuntime | undefined {
74
- return runtimes.get(accountId);
75
- }
76
-
77
- export function getAccountRuntimeSnapshot(accountId: string) {
78
- return runtimes.get(accountId)?.buildRuntimeStatus();
79
- }
80
-
81
- export function registerBotWsPushHandle(accountId: string, handle: BotWsPushHandle): void {
82
- botWsPushHandles.set(accountId, handle);
83
- }
84
-
85
- export function getBotWsPushHandle(accountId: string): BotWsPushHandle | undefined {
86
- return botWsPushHandles.get(accountId);
87
- }
88
-
89
- export function registerActiveBotWsReplyHandle(params: {
90
- accountId: string;
91
- sessionKey?: string | null;
92
- peerKind?: "direct" | "group" | null;
93
- peerId?: string | null;
94
- handle: ReplyHandle;
95
- }): void {
96
- const accountId = normalizeOptional(params.accountId);
97
- const sessionKey = normalizeOptional(params.sessionKey);
98
- const peerId = normalizePeerId(params.peerId);
99
- if (!accountId) {
100
- return;
101
- }
102
- if (sessionKey) {
103
- activeBotWsReplyHandlesBySession.set(
104
- buildSessionHandleKey(accountId, sessionKey),
105
- params.handle,
106
- );
107
- }
108
- if ((params.peerKind === "direct" || params.peerKind === "group") && peerId) {
109
- activeBotWsReplyHandlesByPeer.set(
110
- buildPeerHandleKey(accountId, params.peerKind, peerId),
111
- params.handle,
112
- );
113
- }
114
- }
115
-
116
- export function getActiveBotWsReplyHandle(params: {
117
- accountId: string;
118
- sessionKey?: string | null;
119
- peerKind?: "direct" | "group" | null;
120
- peerId?: string | null;
121
- }): ReplyHandle | undefined {
122
- const accountId = normalizeOptional(params.accountId);
123
- const sessionKey = normalizeOptional(params.sessionKey);
124
- const peerId = normalizePeerId(params.peerId);
125
- if (!accountId) {
126
- return undefined;
127
- }
128
- if (sessionKey) {
129
- const handle = activeBotWsReplyHandlesBySession.get(
130
- buildSessionHandleKey(accountId, sessionKey),
131
- );
132
- if (handle) {
133
- return handle;
134
- }
135
- }
136
- if ((params.peerKind === "direct" || params.peerKind === "group") && peerId) {
137
- return activeBotWsReplyHandlesByPeer.get(
138
- buildPeerHandleKey(accountId, params.peerKind, peerId),
139
- );
140
- }
141
- return undefined;
142
- }
143
-
144
- export function unregisterActiveBotWsReplyHandle(params: {
145
- accountId: string;
146
- sessionKey?: string | null;
147
- peerKind?: "direct" | "group" | null;
148
- peerId?: string | null;
149
- handle?: ReplyHandle;
150
- }): void {
151
- const accountId = normalizeOptional(params.accountId);
152
- const sessionKey = normalizeOptional(params.sessionKey);
153
- const peerId = normalizePeerId(params.peerId);
154
- if (!accountId) {
155
- return;
156
- }
157
- if (sessionKey) {
158
- const key = buildSessionHandleKey(accountId, sessionKey);
159
- const current = activeBotWsReplyHandlesBySession.get(key);
160
- if (!params.handle || current === params.handle) {
161
- activeBotWsReplyHandlesBySession.delete(key);
162
- }
163
- }
164
- if ((params.peerKind === "direct" || params.peerKind === "group") && peerId) {
165
- const key = buildPeerHandleKey(accountId, params.peerKind, peerId);
166
- const current = activeBotWsReplyHandlesByPeer.get(key);
167
- if (!params.handle || current === params.handle) {
168
- activeBotWsReplyHandlesByPeer.delete(key);
169
- }
170
- }
171
- }
172
-
173
- export function unregisterBotWsPushHandle(accountId: string): void {
174
- botWsPushHandles.delete(accountId);
175
- }
176
-
177
- export function unregisterAccountRuntime(accountId: string): void {
178
- runtimes.delete(accountId);
179
- botWsPushHandles.delete(accountId);
180
- for (const key of activeBotWsReplyHandlesBySession.keys()) {
181
- if (key.startsWith(`${accountId}::`)) {
182
- activeBotWsReplyHandlesBySession.delete(key);
183
- }
184
- }
185
- for (const key of activeBotWsReplyHandlesByPeer.keys()) {
186
- if (key.startsWith(`${accountId}::`)) {
187
- activeBotWsReplyHandlesByPeer.delete(key);
188
- }
189
- }
190
- clearWecomSourceAccount(accountId);
191
- console.log(`[wecom-runtime] unregister account=${accountId}`);
192
- }
@@ -1,87 +0,0 @@
1
- import type { ResolvedAgentAccount } from "../../types/index.js";
2
- import { resolveScopedWecomTarget } from "../../target.js";
3
- import { deliverAgentApiMedia, deliverAgentApiText } from "../../transport/agent-api/delivery.js";
4
- import { canUseAgentApiDelivery } from "./fallback-policy.js";
5
- import { getWecomRuntime } from "../../runtime.js";
6
-
7
- export class WecomAgentDeliveryService {
8
- constructor(private readonly agent: ResolvedAgentAccount) { }
9
-
10
- assertAvailable(): void {
11
- if (!canUseAgentApiDelivery(this.agent)) {
12
- throw new Error(
13
- `WeCom outbound requires channels.wecom.accounts.<accountId>.agent.agentId (or legacy channels.wecom.agent.agentId) for account=${this.agent.accountId}.`,
14
- );
15
- }
16
- }
17
-
18
- resolveTargetOrThrow(to: string | undefined) {
19
- const scoped = resolveScopedWecomTarget(to, this.agent.accountId);
20
- if (!scoped) {
21
- console.error(`[wecom-agent-delivery] missing target account=${this.agent.accountId}`);
22
- throw new Error("WeCom outbound requires a target (userid, partyid, tagid or chatid).");
23
- }
24
- if (scoped.accountId && scoped.accountId !== this.agent.accountId) {
25
- console.error(
26
- `[wecom-agent-delivery] account mismatch current=${this.agent.accountId} targetAccount=${scoped.accountId} raw=${String(to ?? "")}`,
27
- );
28
- throw new Error(
29
- `WeCom outbound account mismatch: target belongs to account=${scoped.accountId}, current account=${this.agent.accountId}.`,
30
- );
31
- }
32
- const target = scoped.target;
33
- if (target.chatid) {
34
- console.warn(
35
- `[wecom-agent-delivery] blocked chat target account=${this.agent.accountId} chatId=${target.chatid}`,
36
- );
37
- throw new Error(
38
- `企业微信(WeCom)Agent 主动发送不支持向群 chatId 发送(chatId=${target.chatid})。` +
39
- `该路径在实际环境中经常失败(例如 86008:无权限访问该会话/会话由其他应用创建)。` +
40
- `请改为发送给用户(userid / user:xxx),或由 Bot 模式在群内交付。`,
41
- );
42
- }
43
- return target;
44
- }
45
-
46
- async sendText(params: { to: string | undefined; text: string }): Promise<void> {
47
- this.assertAvailable();
48
- const target = this.resolveTargetOrThrow(params.to);
49
- console.log(
50
- `[wecom-agent-delivery] sendText account=${this.agent.accountId} to=${String(params.to ?? "")} len=${params.text.length}`,
51
- );
52
-
53
- const runtime = getWecomRuntime();
54
- const chunks = runtime.channel.text.chunkText(params.text, 2048);
55
-
56
- for (const chunk of chunks) {
57
- if (!chunk.trim()) continue;
58
- await deliverAgentApiText({
59
- agent: this.agent,
60
- target,
61
- text: chunk,
62
- });
63
- }
64
- }
65
-
66
- async sendMedia(params: {
67
- to: string | undefined;
68
- text?: string;
69
- buffer: Buffer;
70
- filename: string;
71
- contentType: string;
72
- }): Promise<void> {
73
- this.assertAvailable();
74
- const target = this.resolveTargetOrThrow(params.to);
75
- console.log(
76
- `[wecom-agent-delivery] sendMedia account=${this.agent.accountId} to=${String(params.to ?? "")} filename=${params.filename} contentType=${params.contentType}`,
77
- );
78
- await deliverAgentApiMedia({
79
- agent: this.agent,
80
- target,
81
- buffer: params.buffer,
82
- filename: params.filename,
83
- contentType: params.contentType,
84
- text: params.text,
85
- });
86
- }
87
- }
@@ -1,13 +0,0 @@
1
- import type { ResolvedAgentAccount } from "../../types/index.js";
2
-
3
- export function canUseAgentApiDelivery(agent: ResolvedAgentAccount | undefined): boolean {
4
- return Boolean(agent?.apiConfigured && typeof agent.agentId === "number");
5
- }
6
-
7
- export function shouldFallbackToAgentApi(params: {
8
- agent: ResolvedAgentAccount | undefined;
9
- hasText?: boolean;
10
- hasMedia?: boolean;
11
- }): boolean {
12
- return canUseAgentApiDelivery(params.agent) && Boolean(params.hasText || params.hasMedia);
13
- }
@@ -1,38 +0,0 @@
1
- import type { OpenClawConfig } from "openclaw/plugin-sdk";
2
-
3
- import type { WecomRuntimeEnv } from "../../types/runtime-context.js";
4
- import type { WecomAccountRuntime } from "../../app/account-runtime.js";
5
- import { startAgentCallbackTransport } from "../../transport/agent-callback/http-handler.js";
6
-
7
- export class WecomAgentIngressService {
8
- private stopTransport?: () => void;
9
-
10
- constructor(
11
- private readonly runtime: WecomAccountRuntime,
12
- private readonly cfg: OpenClawConfig,
13
- private readonly runtimeEnv: WecomRuntimeEnv,
14
- ) {}
15
-
16
- start(): { transport: "agent-callback"; descriptors: string[] } | undefined {
17
- const agent = this.runtime.account.agent;
18
- if (!agent?.callbackConfigured) {
19
- return undefined;
20
- }
21
- const callback = startAgentCallbackTransport({
22
- account: agent,
23
- cfg: this.cfg,
24
- runtime: this.runtime,
25
- runtimeEnv: this.runtimeEnv,
26
- });
27
- this.stopTransport = callback.stop;
28
- return {
29
- transport: "agent-callback",
30
- descriptors: callback.paths,
31
- };
32
- }
33
-
34
- stop(): void {
35
- this.stopTransport?.();
36
- this.stopTransport = undefined;
37
- }
38
- }
@@ -1,96 +0,0 @@
1
- import type { ResolvedAgentAccount } from "../../types/index.js";
2
- import { resolveScopedWecomTarget } from "../../target.js";
3
- import { deliverUpstreamAgentApiMedia, deliverUpstreamAgentApiText } from "../../transport/agent-api/upstream-delivery.js";
4
- import { canUseAgentApiDelivery } from "./fallback-policy.js";
5
- import { getWecomRuntime } from "../../runtime.js";
6
-
7
- /**
8
- * 上下游企业消息发送服务
9
- *
10
- * 使用下游企业的 access_token 和 agentId 发送消息
11
- */
12
- export class WecomUpstreamAgentDeliveryService {
13
- constructor(
14
- private readonly upstreamAgent: ResolvedAgentAccount,
15
- private readonly primaryAgent: ResolvedAgentAccount,
16
- ) { }
17
-
18
- assertAvailable(): void {
19
- if (!canUseAgentApiDelivery(this.upstreamAgent)) {
20
- throw new Error(
21
- `WeCom upstream outbound requires channels.wecom.accounts.<accountId>.agent.agentId for upstream corp=${this.upstreamAgent.corpId}.`,
22
- );
23
- }
24
- }
25
-
26
- resolveTargetOrThrow(to: string | undefined) {
27
- const scoped = resolveScopedWecomTarget(to, this.upstreamAgent.accountId);
28
- if (!scoped) {
29
- console.error(`[wecom-upstream-delivery] missing target account=${this.upstreamAgent.accountId}`);
30
- throw new Error("WeCom upstream outbound requires a target (userid, partyid, tagid or chatid).");
31
- }
32
- if (scoped.accountId && scoped.accountId !== this.upstreamAgent.accountId) {
33
- console.error(
34
- `[wecom-upstream-delivery] account mismatch current=${this.upstreamAgent.accountId} targetAccount=${scoped.accountId} raw=${String(to ?? "")}`,
35
- );
36
- throw new Error(
37
- `WeCom upstream outbound account mismatch: target belongs to account=${scoped.accountId}, current account=${this.upstreamAgent.accountId}.`,
38
- );
39
- }
40
- const target = scoped.target;
41
- if (target.chatid) {
42
- console.warn(
43
- `[wecom-upstream-delivery] blocked chat target account=${this.upstreamAgent.accountId} chatId=${target.chatid}`,
44
- );
45
- throw new Error(
46
- `企业微信(WeCom)上下游 Agent 主动发送不支持向群 chatId 发送(chatId=${target.chatid})。` +
47
- `请改为发送给用户(userid / user:xxx)。`,
48
- );
49
- }
50
- return target;
51
- }
52
-
53
- async sendText(params: { to: string | undefined; text: string }): Promise<void> {
54
- this.assertAvailable();
55
- const target = this.resolveTargetOrThrow(params.to);
56
- console.log(
57
- `[wecom-upstream-delivery] sendText account=${this.upstreamAgent.accountId} corpId=${this.upstreamAgent.corpId} to=${String(params.to ?? "")} len=${params.text.length}`,
58
- );
59
-
60
- const runtime = getWecomRuntime();
61
- const chunks = runtime.channel.text.chunkText(params.text, 2048);
62
-
63
- for (const chunk of chunks) {
64
- if (!chunk.trim()) continue;
65
- await deliverUpstreamAgentApiText({
66
- upstreamAgent: this.upstreamAgent,
67
- primaryAgent: this.primaryAgent,
68
- target,
69
- text: chunk,
70
- });
71
- }
72
- }
73
-
74
- async sendMedia(params: {
75
- to: string | undefined;
76
- text?: string;
77
- buffer: Buffer;
78
- filename: string;
79
- contentType: string;
80
- }): Promise<void> {
81
- this.assertAvailable();
82
- const target = this.resolveTargetOrThrow(params.to);
83
- console.log(
84
- `[wecom-upstream-delivery] sendMedia account=${this.upstreamAgent.accountId} corpId=${this.upstreamAgent.corpId} to=${String(params.to ?? "")} filename=${params.filename} contentType=${params.contentType}`,
85
- );
86
- await deliverUpstreamAgentApiMedia({
87
- upstreamAgent: this.upstreamAgent,
88
- primaryAgent: this.primaryAgent,
89
- target,
90
- buffer: params.buffer,
91
- filename: params.filename,
92
- contentType: params.contentType,
93
- text: params.text,
94
- });
95
- }
96
- }
@@ -1,47 +0,0 @@
1
- import type { OpenClawConfig } from "openclaw/plugin-sdk";
2
-
3
- export function buildWecomBotDispatchConfig(config: OpenClawConfig): OpenClawConfig {
4
- const baseAgents = (config as any)?.agents ?? {};
5
- const baseAgentDefaults = (baseAgents as any)?.defaults ?? {};
6
- const baseBlockChunk = (baseAgentDefaults as any)?.blockStreamingChunk ?? {};
7
- const baseBlockCoalesce = (baseAgentDefaults as any)?.blockStreamingCoalesce ?? {};
8
- const baseTools = (config as any)?.tools ?? {};
9
- const baseSandbox = (baseTools as any)?.sandbox ?? {};
10
- const baseSandboxTools = (baseSandbox as any)?.tools ?? {};
11
- const existingTopLevelDeny = Array.isArray((baseTools as any).deny) ? ((baseTools as any).deny as string[]) : [];
12
- const existingSandboxDeny = Array.isArray((baseSandboxTools as any).deny) ? ((baseSandboxTools as any).deny as string[]) : [];
13
- const topLevelDeny = Array.from(new Set([...existingTopLevelDeny, "message"]));
14
- const sandboxDeny = Array.from(new Set([...existingSandboxDeny, "message"]));
15
- return {
16
- ...(config as any),
17
- agents: {
18
- ...baseAgents,
19
- defaults: {
20
- ...baseAgentDefaults,
21
- blockStreamingChunk: {
22
- ...baseBlockChunk,
23
- minChars: baseBlockChunk.minChars ?? 120,
24
- maxChars: baseBlockChunk.maxChars ?? 360,
25
- breakPreference: baseBlockChunk.breakPreference ?? "sentence",
26
- },
27
- blockStreamingCoalesce: {
28
- ...baseBlockCoalesce,
29
- minChars: baseBlockCoalesce.minChars ?? 120,
30
- maxChars: baseBlockCoalesce.maxChars ?? 360,
31
- idleMs: baseBlockCoalesce.idleMs ?? 250,
32
- },
33
- },
34
- },
35
- tools: {
36
- ...baseTools,
37
- deny: topLevelDeny,
38
- sandbox: {
39
- ...baseSandbox,
40
- tools: {
41
- ...baseSandboxTools,
42
- deny: sandboxDeny,
43
- },
44
- },
45
- },
46
- } as OpenClawConfig;
47
- }
@@ -1,178 +0,0 @@
1
- import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk";
2
-
3
- import { resolveWecomAccount } from "../../config/index.js";
4
- import { wecomFetch } from "../../http.js";
5
- import { LIMITS } from "../../monitor/state.js";
6
- import type { StreamState } from "../../types/legacy-stream.js";
7
- import type { ResolvedAgentAccount } from "../../types/index.js";
8
- import { sendMedia as sendAgentMedia, sendText as sendAgentText, uploadMedia } from "../../transport/agent-api/core.js";
9
- import { buildStreamReplyFromState } from "../../transport/bot-webhook/protocol.js";
10
- import { useActiveReplyOnce } from "../../transport/bot-webhook/active-reply.js";
11
- import { guessContentTypeFromPath } from "../../transport/bot-webhook/inbound-normalizer.js";
12
-
13
- const STREAM_MAX_DM_BYTES = 200_000;
14
-
15
- export function appendDmContent(state: StreamState, text: string): void {
16
- const next = state.dmContent ? `${state.dmContent}\n\n${text}`.trim() : text.trim();
17
- const buf = Buffer.from(next, "utf8");
18
- state.dmContent = buf.length <= STREAM_MAX_DM_BYTES ? next : buf.subarray(buf.length - STREAM_MAX_DM_BYTES).toString("utf8");
19
- }
20
-
21
- export function resolveAgentAccountOrUndefined(cfg: OpenClawConfig, accountId: string): ResolvedAgentAccount | undefined {
22
- const agent = resolveWecomAccount({ cfg, accountId }).agent;
23
- return agent?.configured ? agent : undefined;
24
- }
25
-
26
- export function buildFallbackPrompt(params: {
27
- kind: "media" | "timeout" | "error";
28
- agentConfigured: boolean;
29
- userId?: string;
30
- filename?: string;
31
- chatType?: "group" | "direct";
32
- }): string {
33
- const who = params.userId ? `(${params.userId})` : "";
34
- const scope = params.chatType === "group" ? "群聊" : params.chatType === "direct" ? "私聊" : "会话";
35
- if (!params.agentConfigured) {
36
- return `${scope}中需要通过应用私信发送${params.filename ? `(${params.filename})` : ""},但管理员尚未配置企业微信自建应用(Agent)通道。请联系管理员配置后再试。${who}`.trim();
37
- }
38
- if (!params.userId) {
39
- return `${scope}中需要通过应用私信兜底发送${params.filename ? `(${params.filename})` : ""},但本次回调未能识别触发者 userid(请检查企微回调字段 from.userid / fromuserid)。请联系管理员排查配置。`.trim();
40
- }
41
- if (params.kind === "media") {
42
- return `已生成文件${params.filename ? `(${params.filename})` : ""},将通过应用私信发送给你。${who}`.trim();
43
- }
44
- if (params.kind === "timeout") {
45
- return `内容较长,为避免超时,后续内容将通过应用私信发送给你。${who}`.trim();
46
- }
47
- return `交付出现异常,已尝试通过应用私信发送给你。${who}`.trim();
48
- }
49
-
50
- export async function sendBotFallbackPromptNow(params: { streamId: string; text: string }): Promise<void> {
51
- await useActiveReplyOnce(params.streamId, async ({ responseUrl, proxyUrl }) => {
52
- const payload = {
53
- msgtype: "stream",
54
- stream: {
55
- id: params.streamId,
56
- finish: true,
57
- content: params.text.trim() || "1",
58
- },
59
- };
60
- const res = await wecomFetch(
61
- responseUrl,
62
- {
63
- method: "POST",
64
- headers: { "Content-Type": "application/json" },
65
- body: JSON.stringify(payload),
66
- },
67
- { proxyUrl, timeoutMs: LIMITS.REQUEST_TIMEOUT_MS },
68
- );
69
- if (!res.ok) {
70
- throw new Error(`fallback prompt push failed: ${res.status}`);
71
- }
72
- });
73
- }
74
-
75
- export async function pushFinalStreamReplyNow(params: { streamId: string; state: StreamState }): Promise<void> {
76
- const finalReply = buildStreamReplyFromState(params.state) as unknown as Record<string, unknown>;
77
- await useActiveReplyOnce(params.streamId, async ({ responseUrl, proxyUrl }) => {
78
- const res = await wecomFetch(
79
- responseUrl,
80
- {
81
- method: "POST",
82
- headers: { "Content-Type": "application/json" },
83
- body: JSON.stringify(finalReply),
84
- },
85
- { proxyUrl, timeoutMs: LIMITS.REQUEST_TIMEOUT_MS },
86
- );
87
- if (!res.ok) {
88
- throw new Error(`final stream push failed: ${res.status}`);
89
- }
90
- });
91
- }
92
-
93
- export async function sendAgentDmText(params: {
94
- agent: ResolvedAgentAccount;
95
- userId: string;
96
- text: string;
97
- core: PluginRuntime;
98
- }): Promise<void> {
99
- const chunks = params.core.channel.text.chunkText(params.text, 2048);
100
- for (const chunk of chunks) {
101
- const trimmed = chunk.trim();
102
- if (!trimmed) continue;
103
- await sendAgentText({ agent: params.agent, toUser: params.userId, text: trimmed });
104
- }
105
- }
106
-
107
- export async function sendAgentDmMedia(params: {
108
- agent: ResolvedAgentAccount;
109
- userId: string;
110
- mediaUrlOrPath: string;
111
- contentType?: string;
112
- filename: string;
113
- }): Promise<void> {
114
- let buffer: Buffer;
115
- let inferredContentType = params.contentType;
116
- const looksLikeUrl = /^https?:\/\//i.test(params.mediaUrlOrPath);
117
- if (looksLikeUrl) {
118
- const res = await fetch(params.mediaUrlOrPath, { signal: AbortSignal.timeout(30_000) });
119
- if (!res.ok) throw new Error(`media download failed: ${res.status}`);
120
- buffer = Buffer.from(await res.arrayBuffer());
121
- inferredContentType = inferredContentType || res.headers.get("content-type") || "application/octet-stream";
122
- } else {
123
- const fs = await import("node:fs/promises");
124
- buffer = await fs.readFile(params.mediaUrlOrPath);
125
- }
126
-
127
- let mediaType: "image" | "voice" | "video" | "file" = "file";
128
- const ct = (inferredContentType || "").toLowerCase();
129
- if (ct.startsWith("image/")) mediaType = "image";
130
- else if (ct.startsWith("audio/")) mediaType = "voice";
131
- else if (ct.startsWith("video/")) mediaType = "video";
132
-
133
- const mediaId = await uploadMedia({
134
- agent: params.agent,
135
- type: mediaType,
136
- buffer,
137
- filename: params.filename,
138
- });
139
- await sendAgentMedia({
140
- agent: params.agent,
141
- toUser: params.userId,
142
- mediaId,
143
- mediaType,
144
- });
145
- }
146
-
147
- export function extractLocalImagePathsFromText(params: { text: string; mustAlsoAppearIn: string }): string[] {
148
- const text = params.text;
149
- const mustAlsoAppearIn = params.mustAlsoAppearIn;
150
- if (!text.trim()) return [];
151
- const exts = "(png|jpg|jpeg|gif|webp|bmp)";
152
- const re = new RegExp(String.raw`(\/(?:Users|tmp|root|home)\/[^\s"'<>]+?\.${exts})`, "gi");
153
- const found = new Set<string>();
154
- let m: RegExpExecArray | null;
155
- while ((m = re.exec(text))) {
156
- const p = m[1];
157
- if (!p) continue;
158
- if (!mustAlsoAppearIn.includes(p)) continue;
159
- found.add(p);
160
- }
161
- return Array.from(found);
162
- }
163
-
164
- export function extractLocalFilePathsFromText(text: string): string[] {
165
- if (!text.trim()) return [];
166
- const re = /\/(?:Users|tmp|root|home)\/[^\s"'<>]+/g;
167
- const found = new Set<string>();
168
- let m: RegExpExecArray | null;
169
- while ((m = re.exec(text))) {
170
- const p = m[0]?.trim();
171
- if (p) found.add(p);
172
- }
173
- return Array.from(found);
174
- }
175
-
176
- export function guessLocalPathContentType(filePath: string): string | undefined {
177
- return guessContentTypeFromPath(filePath);
178
- }