shennian 0.3.1 → 0.3.3

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 (296) hide show
  1. package/README.md +3 -1
  2. package/dist/publish-build-manifest.json +291 -341
  3. package/dist/src/agents/codex.js +1 -1
  4. package/dist/src/agents/command-spec.js +2 -2
  5. package/dist/src/agents/detect.js +1 -1
  6. package/dist/src/agents/model-registry/discovery.js +2 -2
  7. package/dist/src/agents/model-registry/parsers.d.ts +1 -0
  8. package/dist/src/agents/model-registry/parsers.js +4 -4
  9. package/dist/src/agents/model-registry/service.js +1 -1
  10. package/dist/src/agents/native-capabilities.d.ts +10 -0
  11. package/dist/src/agents/native-capabilities.js +1 -0
  12. package/dist/src/agents/platform-instructions.js +5 -5
  13. package/dist/src/agents/runtime-compatibility.d.ts +11 -0
  14. package/dist/src/agents/runtime-compatibility.js +1 -0
  15. package/dist/src/agents/workbuddy.d.ts +44 -0
  16. package/dist/src/agents/workbuddy.js +3 -0
  17. package/dist/src/commands/daemon.d.ts +1 -0
  18. package/dist/src/commands/daemon.js +9 -9
  19. package/dist/src/commands/integration.d.ts +2 -0
  20. package/dist/src/commands/integration.js +1 -0
  21. package/dist/src/commands/manager.js +1 -2
  22. package/dist/src/commands/pair-browser.d.ts +43 -0
  23. package/dist/src/commands/pair-browser.js +1 -0
  24. package/dist/src/commands/pair.d.ts +2 -0
  25. package/dist/src/commands/pair.js +11 -8
  26. package/dist/src/commands/room.d.ts +13 -0
  27. package/dist/src/commands/room.js +4 -0
  28. package/dist/src/commands/upgrade.d.ts +3 -0
  29. package/dist/src/commands/upgrade.js +1 -1
  30. package/dist/src/config/index.d.ts +3 -1
  31. package/dist/src/config/index.js +2 -1
  32. package/dist/src/daemon-ipc/client.d.ts +8 -0
  33. package/dist/src/daemon-ipc/client.js +3 -0
  34. package/dist/src/daemon-ipc/paths.d.ts +7 -0
  35. package/dist/src/daemon-ipc/paths.js +1 -0
  36. package/dist/src/daemon-ipc/protocol.d.ts +30 -0
  37. package/dist/src/daemon-ipc/protocol.js +1 -0
  38. package/dist/src/daemon-ipc/secret.d.ts +3 -0
  39. package/dist/src/daemon-ipc/secret.js +3 -0
  40. package/dist/src/daemon-ipc/server.d.ts +13 -0
  41. package/dist/src/daemon-ipc/server.js +3 -0
  42. package/dist/src/index.js +2 -2
  43. package/dist/src/integrations/claude-room-plugin-lifecycle.d.ts +47 -0
  44. package/dist/src/integrations/claude-room-plugin-lifecycle.js +1 -0
  45. package/dist/src/integrations/codex-room-plugin-lifecycle.d.ts +49 -0
  46. package/dist/src/integrations/codex-room-plugin-lifecycle.js +1 -0
  47. package/dist/src/integrations/room-plugin-bundles.d.ts +23 -0
  48. package/dist/src/integrations/room-plugin-bundles.js +1 -0
  49. package/dist/src/integrations/room-plugin-lifecycle-common.d.ts +17 -0
  50. package/dist/src/integrations/room-plugin-lifecycle-common.js +1 -0
  51. package/dist/src/integrations/room-plugin-shared.d.ts +24 -0
  52. package/dist/src/integrations/room-plugin-shared.js +2 -0
  53. package/dist/src/integrations/room-session-hook.d.ts +30 -0
  54. package/dist/src/integrations/room-session-hook.js +2 -0
  55. package/dist/src/integrations/workbuddy-plugin-bundle.d.ts +15 -0
  56. package/dist/src/integrations/workbuddy-plugin-bundle.js +1 -0
  57. package/dist/src/integrations/workbuddy-plugin-lifecycle.d.ts +52 -0
  58. package/dist/src/integrations/workbuddy-plugin-lifecycle.js +2 -0
  59. package/dist/src/integrations/workbuddy-session-hook.d.ts +22 -0
  60. package/dist/src/integrations/workbuddy-session-hook.js +1 -0
  61. package/dist/src/manager/prompt.d.ts +1 -1
  62. package/dist/src/manager/prompt.js +1 -1
  63. package/dist/src/manager/retired-external-channel-compat.d.ts +78 -0
  64. package/dist/src/manager/retired-external-channel-compat.js +1 -0
  65. package/dist/src/manager/runtime.d.ts +1 -3
  66. package/dist/src/manager/runtime.js +7 -7
  67. package/dist/src/relay/client.d.ts +2 -0
  68. package/dist/src/relay/client.js +1 -1
  69. package/dist/src/room/activation-audit.d.ts +15 -0
  70. package/dist/src/room/activation-audit.js +2 -0
  71. package/dist/src/room/activation-policy.d.ts +8 -0
  72. package/dist/src/room/activation-policy.js +1 -0
  73. package/dist/src/room/activation-service.d.ts +25 -0
  74. package/dist/src/room/activation-service.js +2 -0
  75. package/dist/src/room/agent-tool.d.ts +62 -0
  76. package/dist/src/room/agent-tool.js +1 -0
  77. package/dist/src/room/attachment-cache.d.ts +40 -0
  78. package/dist/src/room/attachment-cache.js +2 -0
  79. package/dist/src/room/attachment-upload.d.ts +20 -0
  80. package/dist/src/room/attachment-upload.js +1 -0
  81. package/dist/src/room/authorized-action-coordinator.d.ts +40 -0
  82. package/dist/src/room/authorized-action-coordinator.js +1 -0
  83. package/dist/src/room/authorized-action-ipc.d.ts +11 -0
  84. package/dist/src/room/authorized-action-ipc.js +1 -0
  85. package/dist/src/room/device-authorization-client.d.ts +49 -0
  86. package/dist/src/room/device-authorization-client.js +1 -0
  87. package/dist/src/room/host-session-observation-ipc.d.ts +9 -0
  88. package/dist/src/room/host-session-observation-ipc.js +1 -0
  89. package/dist/src/room/host-session-observations.d.ts +54 -0
  90. package/dist/src/room/host-session-observations.js +2 -0
  91. package/dist/src/room/invite-bootstrap.d.ts +35 -0
  92. package/dist/src/room/invite-bootstrap.js +1 -0
  93. package/dist/src/room/invocation-token-ipc.d.ts +9 -0
  94. package/dist/src/room/invocation-token-ipc.js +1 -0
  95. package/dist/src/room/invocation-token.d.ts +53 -0
  96. package/dist/src/room/invocation-token.js +1 -0
  97. package/dist/src/room/jsonl-mirror.d.ts +73 -0
  98. package/dist/src/room/jsonl-mirror.js +6 -0
  99. package/dist/src/room/managed-room-binding.d.ts +42 -0
  100. package/dist/src/room/managed-room-binding.js +1 -0
  101. package/dist/src/room/managed-room-session.d.ts +143 -0
  102. package/dist/src/room/managed-room-session.js +3 -0
  103. package/dist/src/room/mirror-sync-service.d.ts +33 -0
  104. package/dist/src/room/mirror-sync-service.js +1 -0
  105. package/dist/src/room/operator-ipc.d.ts +11 -0
  106. package/dist/src/room/operator-ipc.js +1 -0
  107. package/dist/src/room/pending-authorized-actions.d.ts +69 -0
  108. package/dist/src/room/pending-authorized-actions.js +3 -0
  109. package/dist/src/room/room-action-client.d.ts +210 -0
  110. package/dist/src/room/room-action-client.js +1 -0
  111. package/dist/src/room/room-action-ipc.d.ts +33 -0
  112. package/dist/src/room/room-action-ipc.js +1 -0
  113. package/dist/src/room/room-action-schema.d.ts +3 -0
  114. package/dist/src/room/room-action-schema.js +1 -0
  115. package/dist/src/room/room-authorized-action-executor.d.ts +23 -0
  116. package/dist/src/room/room-authorized-action-executor.js +1 -0
  117. package/dist/src/room/room-binding-client.d.ts +51 -0
  118. package/dist/src/room/room-binding-client.js +1 -0
  119. package/dist/src/room/room-binding-ipc.d.ts +29 -0
  120. package/dist/src/room/room-binding-ipc.js +1 -0
  121. package/dist/src/room/session-registry-ipc.d.ts +14 -0
  122. package/dist/src/room/session-registry-ipc.js +1 -0
  123. package/dist/src/room/session-registry.d.ts +128 -0
  124. package/dist/src/room/session-registry.js +2 -0
  125. package/dist/src/room/tool-schema.d.ts +118 -0
  126. package/dist/src/room/tool-schema.js +1 -0
  127. package/dist/src/room/tool-server.d.ts +5 -0
  128. package/dist/src/room/tool-server.js +3 -0
  129. package/dist/src/session/handlers/agent-capabilities.d.ts +3 -0
  130. package/dist/src/session/handlers/agent-capabilities.js +1 -0
  131. package/dist/src/session/handlers/agent-config.js +1 -1
  132. package/dist/src/session/handlers/chat.js +2 -2
  133. package/dist/src/session/handlers/control.js +1 -1
  134. package/dist/src/session/handlers/fs.d.ts +1 -0
  135. package/dist/src/session/handlers/fs.js +1 -1
  136. package/dist/src/session/handlers/room-managed.d.ts +4 -0
  137. package/dist/src/session/handlers/room-managed.js +1 -0
  138. package/dist/src/session/manager.d.ts +18 -6
  139. package/dist/src/session/manager.js +1 -1
  140. package/dist/src/session/types.d.ts +5 -0
  141. package/dist/src/upgrade/engine.d.ts +14 -1
  142. package/dist/src/upgrade/engine.js +2 -2
  143. package/dist/src/upgrade/scheduler.d.ts +45 -0
  144. package/dist/src/upgrade/scheduler.js +1 -0
  145. package/node_modules/@shennian/wire/dist/agent-capabilities.d.ts +17 -0
  146. package/node_modules/@shennian/wire/dist/agent-capabilities.d.ts.map +1 -0
  147. package/node_modules/@shennian/wire/dist/agent-capabilities.js +2 -0
  148. package/node_modules/@shennian/wire/dist/agent-capabilities.js.map +1 -0
  149. package/node_modules/@shennian/wire/dist/agent-workspace.d.ts +41 -0
  150. package/node_modules/@shennian/wire/dist/agent-workspace.d.ts.map +1 -0
  151. package/node_modules/@shennian/wire/dist/agent-workspace.js +3 -0
  152. package/node_modules/@shennian/wire/dist/agent-workspace.js.map +1 -0
  153. package/node_modules/@shennian/wire/dist/index.d.ts +3 -1
  154. package/node_modules/@shennian/wire/dist/index.d.ts.map +1 -1
  155. package/node_modules/@shennian/wire/dist/index.js +3 -1
  156. package/node_modules/@shennian/wire/dist/index.js.map +1 -1
  157. package/node_modules/@shennian/wire/dist/machine.d.ts +1 -0
  158. package/node_modules/@shennian/wire/dist/machine.d.ts.map +1 -1
  159. package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
  160. package/node_modules/@shennian/wire/dist/methods.d.ts.map +1 -1
  161. package/node_modules/@shennian/wire/dist/room.d.ts +112 -0
  162. package/node_modules/@shennian/wire/dist/room.d.ts.map +1 -0
  163. package/node_modules/@shennian/wire/dist/room.js +4 -0
  164. package/node_modules/@shennian/wire/dist/room.js.map +1 -0
  165. package/node_modules/@shennian/wire/dist/session.d.ts +15 -2
  166. package/node_modules/@shennian/wire/dist/session.d.ts.map +1 -1
  167. package/node_modules/@shennian/wire/dist/session.js +1 -1
  168. package/node_modules/@shennian/wire/dist/session.js.map +1 -1
  169. package/package.json +3 -5
  170. package/dist/scripts/wechat-rpa-confirmation.mjs +0 -5
  171. package/dist/src/agents/pi-context.d.ts +0 -42
  172. package/dist/src/agents/pi-context.js +0 -12
  173. package/dist/src/agents/pi.d.ts +0 -70
  174. package/dist/src/agents/pi.js +0 -17
  175. package/dist/src/channels/base.d.ts +0 -144
  176. package/dist/src/channels/base.js +0 -1
  177. package/dist/src/channels/registry.d.ts +0 -8
  178. package/dist/src/channels/registry.js +0 -1
  179. package/dist/src/channels/reply-split.d.ts +0 -1
  180. package/dist/src/channels/reply-split.js +0 -10
  181. package/dist/src/channels/runtime.d.ts +0 -138
  182. package/dist/src/channels/runtime.js +0 -5
  183. package/dist/src/channels/secret-registry.d.ts +0 -35
  184. package/dist/src/channels/secret-registry.js +0 -1
  185. package/dist/src/channels/websocket.d.ts +0 -55
  186. package/dist/src/channels/websocket.js +0 -8
  187. package/dist/src/channels/wechat-channel/anchor.d.ts +0 -10
  188. package/dist/src/channels/wechat-channel/anchor.js +0 -1
  189. package/dist/src/channels/wechat-channel/automation-lane.d.ts +0 -35
  190. package/dist/src/channels/wechat-channel/automation-lane.js +0 -2
  191. package/dist/src/channels/wechat-channel/client.d.ts +0 -283
  192. package/dist/src/channels/wechat-channel/client.js +0 -1
  193. package/dist/src/channels/wechat-channel/cooldown.d.ts +0 -21
  194. package/dist/src/channels/wechat-channel/cooldown.js +0 -1
  195. package/dist/src/channels/wechat-channel/core/fixture-compare.d.ts +0 -52
  196. package/dist/src/channels/wechat-channel/core/fixture-compare.js +0 -1
  197. package/dist/src/channels/wechat-channel/core/media-action-plan.d.ts +0 -3
  198. package/dist/src/channels/wechat-channel/core/media-action-plan.js +0 -1
  199. package/dist/src/channels/wechat-channel/core/schema.d.ts +0 -67
  200. package/dist/src/channels/wechat-channel/core/schema.js +0 -1
  201. package/dist/src/channels/wechat-channel/core/trace-comparator.d.ts +0 -28
  202. package/dist/src/channels/wechat-channel/core/trace-comparator.js +0 -1
  203. package/dist/src/channels/wechat-channel/fingerprint.d.ts +0 -31
  204. package/dist/src/channels/wechat-channel/fingerprint.js +0 -1
  205. package/dist/src/channels/wechat-channel/helper-assets.d.ts +0 -103
  206. package/dist/src/channels/wechat-channel/helper-assets.js +0 -1
  207. package/dist/src/channels/wechat-channel/helper-client.d.ts +0 -87
  208. package/dist/src/channels/wechat-channel/helper-client.js +0 -3
  209. package/dist/src/channels/wechat-channel/helper-protocol.d.ts +0 -122
  210. package/dist/src/channels/wechat-channel/helper-protocol.js +0 -1
  211. package/dist/src/channels/wechat-channel/human-coordination.d.ts +0 -25
  212. package/dist/src/channels/wechat-channel/human-coordination.js +0 -1
  213. package/dist/src/channels/wechat-channel/index.d.ts +0 -21
  214. package/dist/src/channels/wechat-channel/index.js +0 -1
  215. package/dist/src/channels/wechat-channel/ledger.d.ts +0 -48
  216. package/dist/src/channels/wechat-channel/ledger.js +0 -2
  217. package/dist/src/channels/wechat-channel/media-cache-resolver.d.ts +0 -42
  218. package/dist/src/channels/wechat-channel/media-cache-resolver.js +0 -1
  219. package/dist/src/channels/wechat-channel/media-resolver.d.ts +0 -186
  220. package/dist/src/channels/wechat-channel/media-resolver.js +0 -1
  221. package/dist/src/channels/wechat-channel/message-key.d.ts +0 -19
  222. package/dist/src/channels/wechat-channel/message-key.js +0 -1
  223. package/dist/src/channels/wechat-channel/observer.d.ts +0 -171
  224. package/dist/src/channels/wechat-channel/observer.js +0 -7
  225. package/dist/src/channels/wechat-channel/outbound-ledger.d.ts +0 -96
  226. package/dist/src/channels/wechat-channel/outbound-ledger.js +0 -2
  227. package/dist/src/channels/wechat-channel/outbound-sender.d.ts +0 -74
  228. package/dist/src/channels/wechat-channel/outbound-sender.js +0 -1
  229. package/dist/src/channels/wechat-channel/pacing.d.ts +0 -3
  230. package/dist/src/channels/wechat-channel/pacing.js +0 -1
  231. package/dist/src/channels/wechat-channel/preflight.d.ts +0 -46
  232. package/dist/src/channels/wechat-channel/preflight.js +0 -1
  233. package/dist/src/channels/wechat-channel/runner.d.ts +0 -43
  234. package/dist/src/channels/wechat-channel/runner.js +0 -1
  235. package/dist/src/channels/wechat-channel/runtime.d.ts +0 -50
  236. package/dist/src/channels/wechat-channel/runtime.js +0 -1
  237. package/dist/src/channels/wechat-channel/scheduler.d.ts +0 -74
  238. package/dist/src/channels/wechat-channel/scheduler.js +0 -1
  239. package/dist/src/channels/wechat-channel/vector-store.d.ts +0 -44
  240. package/dist/src/channels/wechat-channel/vector-store.js +0 -1
  241. package/dist/src/channels/wechat-rpa/macos-flow.d.ts +0 -55
  242. package/dist/src/channels/wechat-rpa/macos-flow.js +0 -1
  243. package/dist/src/channels/wechat-rpa/macos.d.ts +0 -11
  244. package/dist/src/channels/wechat-rpa/macos.js +0 -21
  245. package/dist/src/channels/wechat-rpa/normalizer.d.ts +0 -49
  246. package/dist/src/channels/wechat-rpa/normalizer.js +0 -7
  247. package/dist/src/channels/wechat-rpa/product-channel.d.ts +0 -76
  248. package/dist/src/channels/wechat-rpa/product-channel.js +0 -16
  249. package/dist/src/channels/wechat-rpa-session-sync.d.ts +0 -26
  250. package/dist/src/channels/wechat-rpa-session-sync.js +0 -1
  251. package/dist/src/channels/wechat-rpa.d.ts +0 -104
  252. package/dist/src/channels/wechat-rpa.js +0 -6
  253. package/dist/src/channels/wecom.d.ts +0 -51
  254. package/dist/src/channels/wecom.js +0 -4
  255. package/dist/src/commands/external-attachments.d.ts +0 -9
  256. package/dist/src/commands/external-attachments.js +0 -1
  257. package/dist/src/commands/external.d.ts +0 -2
  258. package/dist/src/commands/external.js +0 -1
  259. package/dist/src/commands/helper-runtime-official-download.d.ts +0 -80
  260. package/dist/src/commands/helper-runtime-official-download.js +0 -1
  261. package/dist/src/commands/runtime.d.ts +0 -120
  262. package/dist/src/commands/runtime.js +0 -1
  263. package/dist/src/commands/wechat/command.d.ts +0 -3
  264. package/dist/src/commands/wechat/command.js +0 -1
  265. package/dist/src/commands/wechat/direct.d.ts +0 -14
  266. package/dist/src/commands/wechat/direct.js +0 -2
  267. package/dist/src/commands/wechat/doctor.d.ts +0 -2
  268. package/dist/src/commands/wechat/doctor.js +0 -1
  269. package/dist/src/commands/wechat/ipc.d.ts +0 -9
  270. package/dist/src/commands/wechat/ipc.js +0 -1
  271. package/dist/src/commands/wechat/operations.d.ts +0 -3
  272. package/dist/src/commands/wechat/operations.js +0 -4
  273. package/dist/src/commands/wechat/output.d.ts +0 -7
  274. package/dist/src/commands/wechat/output.js +0 -4
  275. package/dist/src/commands/wechat/types.d.ts +0 -142
  276. package/dist/src/commands/wechat/types.js +0 -1
  277. package/dist/src/commands/wechat/utils.d.ts +0 -16
  278. package/dist/src/commands/wechat/utils.js +0 -1
  279. package/dist/src/commands/wechat.d.ts +0 -6
  280. package/dist/src/commands/wechat.js +0 -1
  281. package/dist/src/devtools/wechat-channel-action-smoke.d.ts +0 -105
  282. package/dist/src/devtools/wechat-channel-action-smoke.js +0 -10
  283. package/dist/src/devtools/wechat-channel-download-report.d.ts +0 -111
  284. package/dist/src/devtools/wechat-channel-download-report.js +0 -2
  285. package/dist/src/devtools/wechat-channel-markdown-report.d.ts +0 -6
  286. package/dist/src/devtools/wechat-channel-markdown-report.js +0 -5
  287. package/dist/src/devtools/wechat-channel-product-steps.d.ts +0 -9
  288. package/dist/src/devtools/wechat-channel-product-steps.js +0 -40
  289. package/dist/src/session/handlers/skills.d.ts +0 -7
  290. package/dist/src/session/handlers/skills.js +0 -1
  291. package/dist/src/session/handlers/wechat-helper-runtime.d.ts +0 -33
  292. package/dist/src/session/handlers/wechat-helper-runtime.js +0 -1
  293. package/dist/src/skills/registry.d.ts +0 -16
  294. package/dist/src/skills/registry.js +0 -15
  295. package/dist/src/skills/setup.d.ts +0 -11
  296. package/dist/src/skills/setup.js +0 -1
@@ -1,11 +0,0 @@
1
- import type { WeChatRpaObservedMessage } from './normalizer.js';
2
- export type WeChatRpaProbeResult = {
3
- ok: boolean;
4
- platform: NodeJS.Platform;
5
- wechatRunning: boolean;
6
- frontmost: boolean;
7
- windowTitle?: string;
8
- message?: string;
9
- };
10
- export declare function probeMacWeChat(): Promise<WeChatRpaProbeResult>;
11
- export declare function observedMessageFromProbe(result: WeChatRpaProbeResult): WeChatRpaObservedMessage | null;
@@ -1,21 +0,0 @@
1
- import{execFile as r}from"node:child_process";import{promisify as s}from"node:util";const i=s(r),a=`
2
- tell application "System Events"
3
- set isRunning to exists process "WeChat"
4
- if isRunning is false then
5
- return "not_running"
6
- end if
7
- tell process "WeChat"
8
- set isFront to frontmost
9
- set titleText to ""
10
- if (count of windows) > 0 then
11
- try
12
- set titleText to name of window 1
13
- end try
14
- end if
15
- return (isFront as text) & "
16
- " & titleText
17
- end tell
18
- end tell
19
- `;async function m(){if(process.platform!=="darwin")return{ok:!1,platform:process.platform,wechatRunning:!1,frontmost:!1,message:"macOS only"};try{const{stdout:t}=await i("osascript",["-e",a],{timeout:5e3}),e=t.trim();if(e==="not_running")return{ok:!0,platform:process.platform,wechatRunning:!1,frontmost:!1};const[n,...o]=e.split(`
20
- `);return{ok:!0,platform:process.platform,wechatRunning:!0,frontmost:n==="true",windowTitle:o.join(`
21
- `).trim()||void 0}}catch(t){return{ok:!1,platform:process.platform,wechatRunning:!1,frontmost:!1,message:t instanceof Error?t.message:String(t)}}}function c(t){return!t.ok||!t.wechatRunning||!t.windowTitle?null:{conversationName:t.windowTitle,senderName:"WeChat RPA",text:`[\u5FAE\u4FE1\u7A97\u53E3\u53EF\u89C1\u6027\u63A2\u6D4B] WeChat ${t.frontmost?"\u4F4D\u4E8E\u524D\u53F0":"\u672A\u4F4D\u4E8E\u524D\u53F0"}\uFF0C\u5F53\u524D\u7A97\u53E3\uFF1A${t.windowTitle}`,observedAt:new Date().toISOString(),rawId:`probe:${t.windowTitle}:${t.frontmost}`}}export{c as observedMessageFromProbe,m as probeMacWeChat};
@@ -1,49 +0,0 @@
1
- export type WeChatRpaObservedMessage = {
2
- conversationName: string;
3
- senderName?: string | null;
4
- text: string;
5
- attachments?: WeChatRpaObservedAttachment[];
6
- observedAt?: string | null;
7
- isMentioned?: boolean | null;
8
- rawId?: string | null;
9
- };
10
- export type WeChatRpaObservedAttachment = {
11
- type: string;
12
- name?: string;
13
- url?: string;
14
- mimeType?: string;
15
- size?: number;
16
- localPath?: string;
17
- thumbnailPath?: string;
18
- hash?: string;
19
- availability?: 'edge-local' | 'server-url' | 'pending-download' | 'metadata-only' | 'unavailable-large';
20
- machineId?: string;
21
- expiresAt?: string;
22
- providerError?: string;
23
- };
24
- export type WeChatRpaNormalizedMessage = {
25
- conversationId: string;
26
- conversationName: string;
27
- messageId: string;
28
- sender: {
29
- id: string;
30
- name?: string | null;
31
- };
32
- text: string;
33
- attachments: WeChatRpaObservedAttachment[];
34
- receivedAt: string;
35
- isMentioned: boolean;
36
- rawRef: string | null;
37
- };
38
- export declare class WeChatRpaDeduper {
39
- private seen;
40
- private queue;
41
- constructor(initialMessageIds?: string[]);
42
- accept(messageId: string): boolean;
43
- snapshot(): string[];
44
- private trim;
45
- }
46
- export declare function normalizeWeChatRpaMessage(input: WeChatRpaObservedMessage, options?: {
47
- selfNicknames?: string[];
48
- }): WeChatRpaNormalizedMessage | null;
49
- export declare function weChatRpaConversationId(conversationName: string): string;
@@ -1,7 +0,0 @@
1
- import{createHash as m}from"node:crypto";const g=500;class I{seen=new Set;queue=[];constructor(e=[]){for(const t of e)!t||this.seen.has(t)||(this.seen.add(t),this.queue.push(t));this.trim()}accept(e){return this.seen.has(e)?!1:(this.seen.add(e),this.queue.push(e),this.trim(),!0)}snapshot(){return[...this.queue]}trim(){for(;this.queue.length>g;){const e=this.queue.shift();e&&this.seen.delete(e)}}}function N(n,e={}){const t=r(n.conversationName),a=r(n.text),s=w(n.attachments);if(!t||!a&&s.length===0)return null;const i=r(n.senderName||"")||null,h=$(n.observedAt),u=y(t),d=o("wechat-sender",i||"unknown"),c=r(n.rawId||"")||null,f=n.isMentioned===!0||x(a,e.selfNicknames),p=c?o("wechat-message",`${t}
2
- ${c}`):o("wechat-message",`${t}
3
- ${i||""}
4
- ${a}
5
- ${s.map(l=>l.name||l.url||l.type).join(`
6
- `)}
7
- ${h}`);return{conversationId:u,conversationName:t,messageId:p,sender:{id:d,name:i},text:a,attachments:s,receivedAt:h,isMentioned:f,rawRef:c}}function y(n){return o("wechat-conversation",r(n))}function r(n){return n.replace(/\s+/g," ").trim()}function w(n){return Array.isArray(n)?n.map(e=>{const t=e?.url?r(e.url):"",a=e?.localPath?r(e.localPath):"",s=b(e?.availability,{url:t,localPath:a});return{type:r(String(e?.type||"file"))||"file",...e?.name?{name:r(e.name)}:{},...t?{url:t}:{},...e?.mimeType?{mimeType:r(e.mimeType)}:{},...Number.isFinite(e?.size)&&Number(e.size)>0?{size:Number(e.size)}:{},...a?{localPath:a}:{},...e?.thumbnailPath?{thumbnailPath:r(e.thumbnailPath)}:{},...e?.hash?{hash:r(e.hash)}:{},...s?{availability:s}:{},...e?.machineId?{machineId:r(e.machineId)}:{},...e?.expiresAt?{expiresAt:r(e.expiresAt)}:{},...e?.providerError?{providerError:r(e.providerError)}:{}}}).filter(e=>e.type):[]}function b(n,e){return n==="edge-local"||n==="server-url"||n==="pending-download"||n==="metadata-only"||n==="unavailable-large"?n:e.localPath?"edge-local":e.url?"server-url":"metadata-only"}function $(n){if(!n)return new Date().toISOString();const e=new Date(n);return Number.isNaN(e.getTime())?new Date().toISOString():e.toISOString()}function o(n,e){return`${n}:${m("sha256").update(e).digest("hex").slice(0,24)}`}function x(n,e){const t=Array.isArray(e)?e.map(a=>r(a)).filter(Boolean):[];return t.length?t.some(a=>{const s=a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`@\\s*${s}(?=$|\\s|[\uFF0C\u3002\uFF01\uFF1F,.!?:\uFF1A\uFF1B;\u3001)])`).test(n)}):!1}export{I as WeChatRpaDeduper,N as normalizeWeChatRpaMessage,y as weChatRpaConversationId};
@@ -1,76 +0,0 @@
1
- import type { ExternalChannelConfig, ExternalMessageEvent, ExternalReply } from '../base.js';
2
- import type { WeChatRpaPendingReplyProjection } from '@shennian/wire';
3
- import { type WeChatChannelProductRunnerOptions } from '../wechat-channel/runner.js';
4
- import type { WeChatChannelSchedulerTickOptions } from '../wechat-channel/scheduler.js';
5
- import { type WeChatChannelBindingConfig, type WeChatChannelRuntime } from '../wechat-channel/runtime.js';
6
- import type { WeChatChannelObservedMessage } from '../wechat-channel/client.js';
7
- import { type WeChatChannelOutboundRecord } from '../wechat-channel/outbound-ledger.js';
8
- export declare const WECHAT_RPA_PRODUCT_SOURCE: "wechat-channel";
9
- export type WeChatRpaProductSourceSecret = {
10
- groups?: Array<{
11
- name: string;
12
- }>;
13
- pollIntervalMs?: number;
14
- forceForeground?: boolean;
15
- downloadAttachments?: boolean;
16
- canReply?: boolean;
17
- selfTriggerMarker?: string;
18
- };
19
- export type WeChatChannelProductRunnerLike = {
20
- start(): Promise<void>;
21
- tick(options?: WeChatChannelSchedulerTickOptions): Promise<unknown>;
22
- stop(): Promise<void>;
23
- };
24
- export type WeChatRpaProductSourceConnection = {
25
- runtime: WeChatChannelRuntime;
26
- runner: WeChatChannelProductRunnerLike;
27
- emitted: ExternalMessageEvent[];
28
- ledgerPath: string;
29
- outboundLedgerPath: string;
30
- helperTracePath: string;
31
- };
32
- export type WeChatRpaProductRunnerFactory = (options: WeChatChannelProductRunnerOptions) => WeChatChannelProductRunnerLike;
33
- export declare function createWeChatRpaProductSourceConnection(input: {
34
- config: ExternalChannelConfig;
35
- secret: WeChatRpaProductSourceSecret;
36
- createRunner?: WeChatRpaProductRunnerFactory;
37
- onMessage?: (event: ExternalMessageEvent & {
38
- managerSessionId: string;
39
- }) => ExternalMessageEvent | void;
40
- }): WeChatRpaProductSourceConnection;
41
- export declare function enqueueWeChatRpaProductOutboundReply(input: {
42
- config: ExternalChannelConfig;
43
- secret: WeChatRpaProductSourceSecret;
44
- reply: ExternalReply;
45
- conversationName: string;
46
- }): WeChatChannelOutboundRecord;
47
- export declare function countPendingWeChatRpaProductOutbound(input: {
48
- config: ExternalChannelConfig;
49
- }): number;
50
- export declare function listPendingWeChatRpaProductOutbound(input: {
51
- config: ExternalChannelConfig;
52
- }): WeChatRpaPendingReplyProjection[];
53
- export declare function listWeChatRpaProductRecentMessages(input: {
54
- config: ExternalChannelConfig;
55
- product: Pick<WeChatRpaProductSourceConnection, 'runtime' | 'ledgerPath'>;
56
- limit?: number;
57
- }): ExternalMessageEvent[];
58
- export declare function cancelWeChatRpaProductOutboundReply(input: {
59
- config: ExternalChannelConfig;
60
- idempotencyKey?: string | null;
61
- replyId?: string | null;
62
- reason?: string;
63
- }): {
64
- cancelled: boolean;
65
- status?: WeChatChannelOutboundRecord['sendStatus'];
66
- record?: WeChatChannelOutboundRecord;
67
- };
68
- export declare function isWeChatRpaProductSource(source: unknown): source is typeof WECHAT_RPA_PRODUCT_SOURCE;
69
- export declare function weChatChannelConversationId(conversationName: string): string;
70
- export declare function weChatChannelBindingId(channelId: string, conversationName: string): string;
71
- export declare function weChatChannelLedgerPath(workDir: string, runtimeId: string): string;
72
- export declare function weChatChannelOutboundLedgerPath(workDir: string, runtimeId: string): string;
73
- export declare function weChatChannelHelperTracePath(workDir: string, runtimeId: string): string;
74
- export declare function safeWeChatChannelRuntimePathSegment(runtimeId: string): string;
75
- export declare function observedMessageToExternalEvent(config: ExternalChannelConfig, binding: WeChatChannelBindingConfig, message: WeChatChannelObservedMessage): ExternalMessageEvent | null;
76
- export declare function observedMessagesToExternalEvent(config: ExternalChannelConfig, binding: WeChatChannelBindingConfig, messages: WeChatChannelObservedMessage[]): ExternalMessageEvent | null;
@@ -1,16 +0,0 @@
1
- import C from"node:crypto";import p from"node:fs";import y from"node:path";import{loadConfig as W}from"../../config/index.js";import{createWeChatChannelProductRunner as $}from"../wechat-channel/runner.js";import{createWeChatChannelApiClient as O}from"../wechat-channel/client.js";import{createWeChatAutomationLane as k}from"../wechat-channel/automation-lane.js";import{createWeChatChannelRuntime as D}from"../wechat-channel/runtime.js";import{cancelWeChatOutboundRecord as T,enqueueWeChatOutboundReply as K,loadWeChatChannelOutboundLedger as b,saveWeChatChannelOutboundLedger as S}from"../wechat-channel/outbound-ledger.js";import{loadWeChatChannelLedger as P}from"../wechat-channel/ledger.js";const j="wechat-channel";function fe(e){const t=D({runtimeId:e.config.id,machineId:ee(),pollIntervalMs:e.secret.pollIntervalMs,foregroundPolicy:E(e.secret.forceForeground),bindings:q(e.config,e.secret)}),n=x(e.config.workDir,t.runtimeId),o=f(e.config.workDir,t.runtimeId),r=F(e.config.workDir,t.runtimeId),a=[];p.mkdirSync(y.dirname(r),{recursive:!0}),p.writeFileSync(r,"");const s=e.createRunner??$,l=z(),u=s({runtime:t,workDir:e.config.workDir,api:l,ledgerPath:n,outboundLedgerPath:o,lane:k(),helperRequestLogger:B(r),onInboundMessages:(h,c)=>{const m=t.bindings.find(N=>N.bindingId===h.bindingId)??h,g=v(e.config,m,c);if(!g)return;const M=e.onMessage?.({...g,managerSessionId:e.config.managerSessionId})??g;a.push(M)}});return{runtime:t,runner:u,emitted:a,ledgerPath:n,outboundLedgerPath:o,helperTracePath:r}}function z(){let e=null;const t=()=>(e??=O(),e);return{observe:(...n)=>t().observe(...n),structureWindow:(...n)=>t().structureWindow(...n),embedVisual:(...n)=>t().embedVisual(...n),classifyWindow:(...n)=>t().classifyWindow(...n),upsertRuntime:async(...n)=>(await Promise.resolve().then(()=>t().upsertRuntime(...n)).catch(()=>null),{ok:!0}),ingest:async(...n)=>(await Promise.resolve().then(()=>t().ingest(...n)).catch(()=>null),{ok:!0}),reportRunStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportRunStatus(...n)).catch(()=>null),{ok:!0}),reportOutboundStatus:async(...n)=>(await Promise.resolve().then(()=>t().reportOutboundStatus(...n)).catch(()=>null),{ok:!0})}}function E(e){return e===!1?"polite":"work"}function he(e){const t=e.config.id,n=R(e.config,e.conversationName),o=P(x(e.config.workDir,t),t),r=f(e.config.workDir,t),a=b(r,t),s=e.reply.attachment?[X(e.reply.attachment)]:[],l=K({ledger:a,replyId:e.reply.messageId||e.reply.idempotencyKey||d("wechat-channel-reply",`${Date.now()}:${e.reply.text}`),idempotencyKey:e.reply.idempotencyKey||d("wechat-channel-send",`${e.config.id}
2
- ${e.reply.conversationId}
3
- ${e.reply.text}
4
- ${s.join(`
5
- `)}`),bindingId:n,runtimeId:t,sessionId:e.config.sessionId||e.config.managerSessionId,conversationName:e.conversationName,replyBaseRevision:o.bindings[n]?.revision??0,text:e.reply.text,attachmentLocalRefs:s});return S(r,a),l}function ge(e){return b(f(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).length}function pe(e){return b(f(e.config.workDir,e.config.id),e.config.id).records.filter(n=>["queued","sending","sent_unconfirmed"].includes(n.sendStatus)).slice(0,100).map(n=>({replyId:n.replyId,idempotencyKey:n.idempotencyKey,status:n.sendStatus,conversationName:n.conversationName,reasonCode:n.deferReason||n.failureCode||null,nextAttemptAt:n.nextAttemptAt??null,queuedAt:n.queuedAt??n.createdAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attemptCount??0,commitStage:n.commitStage??null}))}function ye(e){const t=ne(e.limit,20,1,100),n=P(e.product.ledgerPath,e.product.runtime.runtimeId),o=[];for(const r of e.product.runtime.bindings){const a=(n.bindings[r.bindingId]?.recent??[]).filter(s=>s.senderRole!=="self");for(const s of a.slice(-t)){const l=A(e.config,r,s);l&&o.push(l)}}return o}function be(e){const t=f(e.config.workDir,e.config.id),n=b(t,e.config.id),o=String(e.idempotencyKey??"").trim(),r=String(e.replyId??"").trim();if(!o&&!r)return{cancelled:!1};const a=n.records.find(l=>o&&l.idempotencyKey===o||r&&l.replyId===r);if(!a)return{cancelled:!1};const s=a.sendStatus;return T(a,e.reason||"user_cancelled"),a.sendStatus!==s?(S(t,n),{cancelled:!0,status:a.sendStatus,record:a}):{cancelled:!1,status:a.sendStatus,record:a}}function we(e){return e===j}function L(e){return d("wechat-conversation",i(e))}function _(e,t){return d("wechat-channel-binding",`${e}
6
- ${i(t)}`)}function x(e,t){return y.join(e,"wechat-channel",`${I(t)}.ledger.json`)}function f(e,t){return y.join(e,"wechat-channel",`${I(t)}.outbound-ledger.json`)}function F(e,t){return y.join(e,"wechat-channel",`${I(t)}.helper-trace.jsonl`)}function I(e){return String(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"")||"wechat-channel-runtime"}function B(e){return t=>{p.appendFileSync(e,`${JSON.stringify(H(t))}
7
- `)}}function H(e){const t={phase:e.phase,at:e.at,id:e.id,command:e.command};return e.traceId&&(t.traceId=e.traceId),e.timeoutMs!=null&&(t.timeoutMs=e.timeoutMs),e.durationMs!=null&&(t.durationMs=e.durationMs),e.params!==void 0&&(t.params=w(e.params)),e.ok!==void 0&&(t.ok=e.ok),e.errorCode&&(t.errorCode=e.errorCode),e.errorSummary&&(t.errorSummary=e.errorSummary),e.latencyMs!=null&&(t.latencyMs=e.latencyMs),e.result!==void 0&&(t.result=w(e.result)),t}function w(e,t="",n=0){if(e==null||typeof e=="number"||typeof e=="boolean")return e;if(typeof e=="string")return e.length>240||/(base64|bytes|data|image|screenshot|png|jpeg|jpg|buffer)/i.test(t)&&e.length>48?{type:"string",length:e.length,sha256:C.createHash("sha256").update(e).digest("hex").slice(0,16),preview:e.slice(0,120)}:e;if(Array.isArray(e)){const a=e.slice(0,10).map(s=>w(s,t,n+1));return e.length>a.length?{type:"array",length:e.length,items:a}:a}if(typeof e!="object")return String(e);if(n>=5)return{type:"object",truncated:!0};const o=e,r={};for(const[a,s]of Object.entries(o))r[a]=w(s,a,n+1);return r}function q(e,t){return Y(t).map(n=>({bindingId:R(e,n),sessionId:e.sessionId||e.managerSessionId,conversationDisplayName:n,enabled:!0,allowReply:t.canReply!==!1,downloadMedia:t.downloadAttachments!==!1,selfTriggerMarker:i(t.selfTriggerMarker||"")||null}))}function R(e,t){return e.managedBy==="session-sync"||e.id.startsWith("wechat-binding:")?e.id:_(e.id,t)}function A(e,t,n){if(n.senderRole==="self")return null;const o=i(n.normalizedText||n.anchorText||n.textExcerpt||""),r=V(n);if(!o&&r.length===0)return null;const a=i(n.senderName||"");return{type:"external.message",channelId:e.id,channelType:"wechat-rpa",conversationId:L(t.conversationDisplayName),conversationName:t.conversationDisplayName,messageId:d("wechat-message",`${t.bindingId}
8
- ${n.stableMessageKey}`),sender:{id:d("wechat-sender",`${t.bindingId}
9
- ${a||n.senderRole}`),name:a||null},text:o,attachments:r,receivedAt:te(n.observedAt),isMentioned:!1,replyTarget:"",rawRef:n.stableMessageKey}}function v(e,t,n){const o=n.map(c=>A(e,t,c)).filter(c=>c!=null);if(o.length<=1)return o[0]??null;const r=o[0],a=n.map(c=>i(c.stableMessageKey||"")).filter(Boolean),l=new Set(o.map(c=>i(c.sender.name||""))).size===1?r.sender.name??null:null,u=new Set(o.map(c=>c.sender.id)).size===1?r.sender.id:d("wechat-sender-batch",`${t.bindingId}
10
- ${a.join(`
11
- `)}`),h=o.map(c=>{const m=i(c.text);return m?l!==null?m:`${i(c.sender.name||"")||"\u5BF9\u65B9"}: ${m}`:""}).filter(Boolean).join(`
12
- `);return{...r,messageId:d("wechat-message-batch",`${t.bindingId}
13
- ${a.join(`
14
- `)}`),sender:{id:u,name:l},text:h,attachments:G(o.flatMap(c=>c.attachments)),receivedAt:J(o.map(c=>c.receivedAt)),rawRef:a.join(`
15
- `)}}function V(e){const t=e.mediaMetadata;return U(t).map(o=>{const r=o,a=typeof r.localPath=="string"?i(r.localPath):"",s=typeof r.thumbnailPath=="string"?i(r.thumbnailPath):"",l=typeof r.url=="string"?i(r.url):"",u=Number(r.size??r.sizeBytes??0);return{type:i(String(r.type||r.kind||e.kind||"file"))||"file",...r.name||r.fileName?{name:i(String(r.name||r.fileName))}:{},...l?{url:l}:{},...typeof r.mimeType=="string"?{mimeType:i(r.mimeType)}:{},...typeof r.extension=="string"?{extension:i(r.extension)}:{},...Number.isFinite(u)&&u>0?{size:u}:{},...a?{localPath:a}:{},...s?{thumbnailPath:s}:{},...typeof r.hash=="string"||typeof r.sha256=="string"?{hash:i(String(r.hash||r.sha256))}:{},availability:Z(r.availability,{localPath:a,url:l}),...typeof r.providerError=="string"?{providerError:i(r.providerError)}:{},...typeof r.sourceAction=="string"?{sourceAction:i(r.sourceAction)}:{},...Q(r.materializationKind)?{materializationKind:r.materializationKind}:{},...typeof r.isOriginal=="boolean"?{isOriginal:r.isOriginal}:{},...typeof r.mimeKindMatches=="boolean"?{mimeKindMatches:r.mimeKindMatches}:{}}})}function U(e){if(!e||typeof e!="object")return[];const t=e;return Array.isArray(t.attachments)?t.attachments:t.attachment&&typeof t.attachment=="object"?[t.attachment]:t.localPath||t.url||t.name||t.fileName||t.availability?[t]:[]}function G(e){const t=new Set,n=[];for(const o of e){const r=[o.localPath||"",o.url||"",o.thumbnailPath||"",o.hash||"",o.name||"",o.mimeType||"",o.type||""].join(`
16
- `);t.has(r)||(t.add(r),n.push(o))}return n}function J(e){let t=0;for(const n of e){const o=Date.parse(n);Number.isFinite(o)&&(t=Math.max(t,o))}return t?new Date(t).toISOString():new Date().toISOString()}function Z(e,t){return e==="edge-local"||e==="server-url"||e==="pending-download"||e==="metadata-only"||e==="unavailable-large"?e:t.localPath?"edge-local":t.url?"server-url":"metadata-only"}function Q(e){return e==="original-file"||e==="clipboard-image"||e==="preview-crop"||e==="metadata"}function X(e){const t=i(e.localPath||"");if(!t)throw new Error("WeChat channel product send requires a local attachment path on this machine");if(!p.statSync(t).isFile())throw new Error(`WeChat channel product attachment is not a file: ${t}`);return t}function Y(e){return Array.isArray(e.groups)?e.groups.map(t=>i(t?.name||"")).filter(Boolean):[]}function ee(){return process.env.SHENNIAN_MACHINE_ID?.trim()||W().machineId||"local"}function te(e){if(!e)return new Date().toISOString();const t=new Date(e);return Number.isNaN(t.getTime())?new Date().toISOString():t.toISOString()}function i(e){return e.replace(/\s+/g," ").trim()}function ne(e,t,n,o){const r=Number(e);return Number.isFinite(r)?Math.max(n,Math.min(o,Math.floor(r))):t}function d(e,t){return`${e}:${C.createHash("sha256").update(t).digest("hex").slice(0,24)}`}export{j as WECHAT_RPA_PRODUCT_SOURCE,be as cancelWeChatRpaProductOutboundReply,ge as countPendingWeChatRpaProductOutbound,fe as createWeChatRpaProductSourceConnection,he as enqueueWeChatRpaProductOutboundReply,we as isWeChatRpaProductSource,pe as listPendingWeChatRpaProductOutbound,ye as listWeChatRpaProductRecentMessages,A as observedMessageToExternalEvent,v as observedMessagesToExternalEvent,I as safeWeChatChannelRuntimePathSegment,_ as weChatChannelBindingId,L as weChatChannelConversationId,F as weChatChannelHelperTracePath,x as weChatChannelLedgerPath,f as weChatChannelOutboundLedgerPath};
@@ -1,26 +0,0 @@
1
- import type { ExternalChannelSessionStatus } from '@shennian/wire';
2
- import type { ChannelRuntime } from './runtime.js';
3
- export type WeChatRpaSessionContext = {
4
- sessionId: string;
5
- workDir?: string | null;
6
- agentType?: string | null;
7
- agentSessionId?: string | null;
8
- modelId?: string | null;
9
- externalChannel?: ExternalChannelSessionStatus | null;
10
- };
11
- export type WeChatRpaSessionBindingSyncOptions = {
12
- channelRuntime: Pick<ChannelRuntime, 'upsertManagerWeChatRpaChannel' | 'deleteManagerWeChatRpaChannel' | 'listChannelIdsManagedBy'>;
13
- getLocalMachineId: () => string;
14
- listSessions: () => Iterable<WeChatRpaSessionContext>;
15
- listAuthoritativeSessions?: () => Promise<Iterable<WeChatRpaSessionContext> | null | undefined>;
16
- };
17
- export declare class WeChatRpaSessionBindingSync {
18
- private options;
19
- private reconciling;
20
- constructor(options: WeChatRpaSessionBindingSyncOptions);
21
- reconcileAll(): Promise<void>;
22
- private listDesiredSessionContexts;
23
- reconcileSession(session: WeChatRpaSessionContext): Promise<void>;
24
- private applyUpsert;
25
- private toDesiredBinding;
26
- }
@@ -1 +0,0 @@
1
- import c from"node:os";const l="session-sync";class d{options;reconciling=null;constructor(e){this.options=e}async reconcileAll(){for(;this.reconciling;)await this.reconciling;let e=()=>{};this.reconciling=new Promise(t=>{e=t});try{const t=this.options.getLocalMachineId(),n=new Map,s=await this.listDesiredSessionContexts();for(const a of s){const o=this.toDesiredBinding(a,t);o&&n.set(o.channelId,o)}const i=new Set(this.options.channelRuntime.listChannelIdsManagedBy(l));for(const a of i)n.has(a)||await this.options.channelRuntime.deleteManagerWeChatRpaChannel(a).catch(()=>{});for(const a of n.values())await this.applyUpsert(a)}finally{e(),this.reconciling=null}}async listDesiredSessionContexts(){if(this.options.listAuthoritativeSessions){const e=await this.options.listAuthoritativeSessions().catch(t=>(console.error(`[wechat-rpa-sync] authoritative list failed: ${t instanceof Error?t.message:String(t)}`),null));if(e)return e}return this.options.listSessions()}async reconcileSession(e){for(;this.reconciling;)await this.reconciling;let t=()=>{};this.reconciling=new Promise(n=>{t=n});try{const n=this.options.getLocalMachineId(),s=this.toDesiredBinding(e,n);if(s){await this.applyUpsert(s);return}const i=e.externalChannel?.channelId;i&&this.options.channelRuntime.listChannelIdsManagedBy(l).includes(i)&&await this.options.channelRuntime.deleteManagerWeChatRpaChannel(i).catch(()=>{})}finally{t(),this.reconciling=null}}async applyUpsert(e){await this.options.channelRuntime.upsertManagerWeChatRpaChannel({id:e.channelId,managerSessionId:e.sessionId,sessionId:e.sessionId,workDir:e.workDir,name:e.conversationName,agentType:e.agentType,agentSessionId:e.agentSessionId,modelId:e.modelId,enabled:!0,groups:[{name:e.conversationName}],canReply:e.canReply,source:e.source,downloadAttachments:e.downloadAttachments,privacyConsentAccepted:!0,managedBy:l}).catch(t=>{console.error(`[wechat-rpa-sync] upsert failed channelId=${e.channelId}: ${t instanceof Error?t.message:String(t)}`)})}toDesiredBinding(e,t){const n=e.externalChannel;if(!n||n.type!=="wechat-rpa"||!n.configured||!n.channelId)return null;const s=(n.name??"").trim();return!s||n.machineId&&t&&n.machineId!==t?null:{channelId:n.channelId,sessionId:e.sessionId,conversationName:s,workDir:e.workDir?.trim()||c.homedir(),agentType:e.agentType??void 0,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null,canReply:n.canReply!==!1,downloadAttachments:n.downloadAttachments!==!1,source:"wechat-channel"}}}export{d as WeChatRpaSessionBindingSync};
@@ -1,104 +0,0 @@
1
- import type { ExternalChannelAdapter, ExternalChannelConfig, ExternalChannelRuntimeStatus, ExternalChannelSendResult, ExternalMessageEvent, ExternalReply } from './base.js';
2
- import { type WeChatRpaObservedMessage } from './wechat-rpa/normalizer.js';
3
- import { type WeChatRpaProductRunnerFactory } from './wechat-rpa/product-channel.js';
4
- import type { WeChatAutomationLane } from './wechat-channel/automation-lane.js';
5
- type WeChatRpaSecret = {
6
- type: 'wechat-rpa';
7
- source?: 'wechat-channel' | 'macos-probe' | 'macos-flow' | 'windows-visual-flow' | 'wechat-rpa-lab' | 'fixture-jsonl';
8
- fixturePath?: string;
9
- pollIntervalMs?: number;
10
- groups?: Array<{
11
- name: string;
12
- }>;
13
- forceForeground?: boolean;
14
- noRestore?: boolean;
15
- downloadAttachments?: boolean;
16
- downloadAttachmentsDir?: string;
17
- idleSeconds?: number;
18
- recentLimit?: number;
19
- selfNickname?: string;
20
- selfTriggerMarker?: string;
21
- deferInitialPoll?: boolean;
22
- flowScriptPath?: string;
23
- canReply?: boolean;
24
- systemPrompt?: string;
25
- updatedAt: string;
26
- };
27
- export declare function selectWeChatRpaLabReadKind(recentLimit?: number | null): 'read-latest' | 'scroll-read';
28
- type WeChatRpaEvent = ExternalMessageEvent & {
29
- type: 'external.message';
30
- managerSessionId: string;
31
- };
32
- export declare class WeChatRpaChannelAdapter implements ExternalChannelAdapter {
33
- private onMessage?;
34
- private options;
35
- readonly type: "wechat-rpa";
36
- private secrets;
37
- private connections;
38
- constructor(onMessage?: ((event: WeChatRpaEvent) => ExternalMessageEvent | void) | undefined, options?: {
39
- createProductRunner?: WeChatRpaProductRunnerFactory;
40
- automationLane?: WeChatAutomationLane;
41
- });
42
- connect(config: ExternalChannelConfig): Promise<void>;
43
- disconnect(config: ExternalChannelConfig): Promise<void>;
44
- syncNow(config: ExternalChannelConfig): Promise<ExternalMessageEvent[]>;
45
- cancelOutbound(config: ExternalChannelConfig, input: {
46
- idempotencyKey?: string | null;
47
- replyId?: string | null;
48
- reason?: string;
49
- }): Promise<{
50
- cancelled: boolean;
51
- status?: string;
52
- record?: unknown;
53
- }>;
54
- send(config: ExternalChannelConfig, reply: ExternalReply): Promise<ExternalChannelSendResult>;
55
- health(config: ExternalChannelConfig): Promise<{
56
- ok: boolean;
57
- message?: string;
58
- }>;
59
- defaultConversation(config: ExternalChannelConfig): Promise<{
60
- conversationId: string;
61
- conversationName?: string;
62
- }>;
63
- runtimeStatus(config: ExternalChannelConfig): Partial<ExternalChannelRuntimeStatus>;
64
- private ensureConnection;
65
- private enqueueOperation;
66
- private readSecret;
67
- private pollOnce;
68
- private pollProductOnce;
69
- private ensureProductConnection;
70
- private stopProductWhenControlPlaneDisabled;
71
- private drainPendingReplies;
72
- private readObservedMessages;
73
- private seedConfiguredConversations;
74
- private resolveConversationName;
75
- }
76
- export declare function windowsVisualFlowHealth(secret: {
77
- groups?: Array<{
78
- name: string;
79
- }>;
80
- }, platform?: NodeJS.Platform): {
81
- ok: boolean;
82
- message?: string;
83
- };
84
- export declare function weChatRpaProductSourceHealth(secret: {
85
- groups?: Array<{
86
- name: string;
87
- }>;
88
- }, platform?: NodeJS.Platform): {
89
- ok: boolean;
90
- message?: string;
91
- };
92
- export declare function planWeChatRpaLabSendTasks(input: {
93
- groupName: string;
94
- text?: string;
95
- attachmentPath?: string;
96
- dedupeKey?: string;
97
- requestId?: string;
98
- policy?: 'work' | 'polite';
99
- }): Array<Record<string, unknown>>;
100
- export declare function weChatRpaLabTaskKindForAttachment(filePath: string): 'send-image' | 'send-video' | 'send-file';
101
- export declare function materializeWeChatRpaOutboundAttachment(workDir: string, attachment: NonNullable<ExternalReply['attachment']>): Promise<string>;
102
- export declare function resolveWeChatRpaInboundAttachmentDir(config: Pick<ExternalChannelConfig, 'id'>, secret: Pick<WeChatRpaSecret, 'downloadAttachments' | 'downloadAttachmentsDir'>): string | undefined;
103
- export declare function annotateWeChatRpaInboundAttachments(attachments: WeChatRpaObservedMessage['attachments']): WeChatRpaObservedMessage['attachments'];
104
- export {};
@@ -1,6 +0,0 @@
1
- import I from"node:crypto";import m from"node:fs";import h from"node:path";import{fileURLToPath as ee,pathToFileURL as te}from"node:url";import{ChannelSecretRegistry as re}from"./secret-registry.js";import{resolveShennianPath as ne}from"../config/index.js";import{probeMacWeChat as q,observedMessageFromProbe as ae}from"./wechat-rpa/macos.js";import{runMacWeChatRpaFlow as F}from"./wechat-rpa/macos-flow.js";import{normalizeWeChatRpaMessage as se,WeChatRpaDeduper as K,weChatRpaConversationId as x}from"./wechat-rpa/normalizer.js";import{cancelWeChatRpaProductOutboundReply as ie,countPendingWeChatRpaProductOutbound as oe,createWeChatRpaProductSourceConnection as ue,enqueueWeChatRpaProductOutboundReply as de,isWeChatRpaProductSource as y,listPendingWeChatRpaProductOutbound as ce,weChatChannelConversationId as N}from"./wechat-rpa/product-channel.js";import{createWeChatChannelApiClient as H}from"./wechat-channel/client.js";const le=5e3,A=5,pe=8,S=Number(process.env.SHENNIAN_WECHAT_RPA_OUTBOUND_ATTACHMENT_MAX_BYTES||20*1024*1024),W=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_THRESHOLD||3),E=Number(process.env.SHENNIAN_WECHAT_RPA_INTERRUPTION_COOLDOWN_MS||300*1e3),he=10;function fe(t){return P(t,A,1,50)>=pe?"scroll-read":"read-latest"}class at{onMessage;options;type="wechat-rpa";secrets=new re;connections=new Map;constructor(e,r={}){this.onMessage=e,this.options=r}async connect(e){if(!e.enabled)return;const r=this.readSecret(e),n=this.ensureConnection(e);if(n.stopped=!1,n.config=e,O(n,e),M(n,e),this.seedConfiguredConversations(n,r),y(r.source)){const a=await this.stopProductWhenControlPlaneDisabled(n,r);if(a||await this.ensureProductConnection(n,r),n.timer)return;n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))).catch(i=>{console.error(`[wechat-rpa] autonomous poll failed id=${e.id}: ${i instanceof Error?i.message:String(i)}`)})},n.product?.runtime.policy.pollIntervalMs??j(r.pollIntervalMs)),n.timer.unref(),!r.deferInitialPoll&&!a&&this.enqueueOperation(n,()=>this.pollProductOnce(n,this.readSecret(n.config))).catch(i=>{console.error(`[wechat-rpa] initial poll failed id=${e.id}: ${i instanceof Error?i.message:String(i)}`)});return}n.timer||(n.timer=setInterval(()=>{this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))).catch(()=>{})},j(r.pollIntervalMs)),n.timer.unref(),r.deferInitialPoll||this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config))).catch(()=>{}))}async disconnect(e){const r=this.connections.get(e.id);if(!r)return;r.stopped=!0,r.timer&&clearInterval(r.timer),r.timer=null;const n=r.product;await this.enqueueOperation(r,async()=>{n&&await n.runner.stop()}).catch(()=>{}),this.connections.delete(e.id)}async syncNow(e){if(!e.enabled)throw new Error("WeChat RPA channel is disabled");const r=this.readSecret(e),n=this.ensureConnection(e);return n.stopped=!1,n.config=e,O(n,e),M(n,e),this.seedConfiguredConversations(n,r),y(r.source)?this.enqueueOperation(n,async()=>{const a=this.readSecret(n.config);return await this.stopProductWhenControlPlaneDisabled(n,a)?[]:this.pollProductOnce(n,a,{forceObserve:!0})}):this.enqueueOperation(n,()=>this.pollOnce(n,this.readSecret(n.config)))}async cancelOutbound(e,r){const n=this.readSecret(e);return y(n.source)?ie({config:e,idempotencyKey:r.idempotencyKey,replyId:r.replyId,reason:r.reason}):{cancelled:!1,status:"unsupported_source"}}async send(e,r){const n=this.readSecret(e);if(n.canReply===!1)throw new Error("WeChat RPA channel does not allow replies");const a=this.ensureConnection(e);if(a.config=e,O(a,e),M(a,e),this.seedConfiguredConversations(a,n),y(n.source)){const d=this.resolveConversationName(e,n,r.conversationId);if(!d)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const f=r.text.trim();if(!f&&!r.attachment)throw new Error("Reply text or attachment is required");if(await this.stopProductWhenControlPlaneDisabled(a,n))throw new Error("WeChat RPA channel is disabled");return de({config:a.config,secret:n,reply:{...r,text:f},conversationName:d}),a.runtimeState="retrying",w(a,{status:"sent",runId:a.lastRunId??null,summary:`queued ${d}`}),this.enqueueOperation(a,()=>this.pollProductOnce(a,this.readSecret(a.config))).catch(v=>{a.lastError=v instanceof Error?v.message:String(v)}),{status:"queued"}}if(!We(n.source))throw new Error("WeChat RPA reply requires source=macos-flow, windows-visual-flow, or wechat-rpa-lab");const i=this.resolveConversationName(e,n,r.conversationId);if(!i)throw new Error(`Unknown WeChat RPA conversation: ${r.conversationId}`);const s=Ee(e,r);if(a.completedPendingReplyKeys.has(s))return{status:"sent"};const u=a.manualReviewReplies.get(s);if(u)return{status:"manual-review",reason:u.reason};const o=r.attachment?await He(e.workDir,r.attachment):void 0,c=r.text.trim();if(!c&&!o)throw new Error("Reply text or attachment is required");if(Z(a,n)){const d="WeChat RPA is cooling down after repeated user activity";return a.runtimeState="cooldown",T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:c,attachmentPath:o,reason:d}),{status:"queued",reason:d}}let l;try{a.runtimeState="syncing",l=await this.enqueueOperation(a,()=>(a.lastRunAt=new Date().toISOString(),V(e,n,i,c,o,s)))}catch(d){throw a.lastError=d instanceof Error?d.message:String(d),w(a,{status:b(a.lastError),runId:a.lastRunId??null,summary:a.lastError}),d}$(a,l);const p=Y(l,{text:c,attachmentPath:o});if(p.status==="queued"){const d=p.reason;return T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:p.text,attachmentPath:p.attachmentPath,reason:d,skipText:p.skipText}),a.lastError=d,{status:"queued",reason:d}}if(l.interrupted){const d=l.error||"WeChat RPA send was interrupted by user activity";return _(a,l,d),T(a,{key:s,conversationId:r.conversationId,conversationName:i,text:p.text,attachmentPath:o,reason:d,skipText:p.skipText}),{status:"queued",reason:d}}if(!l.ok){const d=l.error||"WeChat RPA send validator failed; manual review required before retry";return Q(a,s,d),{status:"manual-review",reason:d}}return w(a,{status:"sent",runId:a.lastRunId??null,summary:l.rpaTraceSummary||`sent ${i}`}),C(a),a.lastError=null,{status:"sent"}}async health(e){const r=this.readSecret(e);if((r.source??"macos-probe")==="fixture-jsonl")return r.fixturePath&&m.existsSync(r.fixturePath)?{ok:!0}:{ok:!1,message:"WeChat RPA fixture file is missing"};if(r.source==="macos-flow"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA macOS flow requires macOS"};if(!g(r).length)return{ok:!1,message:"WeChat RPA macOS flow requires at least one group"}}if(y(r.source))return ve(r,process.platform);if(r.source==="wechat-rpa-lab"){if(process.platform!=="darwin")return{ok:!1,message:"WeChat RPA Lab source requires macOS for live runs"};if(!g(r).length)return{ok:!1,message:"WeChat RPA Lab source requires at least one group"};try{J(e.workDir)}catch(a){return{ok:!1,message:a instanceof Error?a.message:String(a)}}return{ok:!0,message:"WeChat RPA Lab source configured"}}if(r.source==="windows-visual-flow")return Re(r,process.platform);const n=await q();return n.ok?{ok:!0,message:n.wechatRunning?"WeChat detected":"WeChat is not running"}:{ok:!1,message:n.message}}async defaultConversation(e){const r=this.readSecret(e),n=g(r)[0];if(!n)throw new Error("WeChat RPA channel has no configured groups");return y(r.source)?{conversationId:N(n),conversationName:n}:{conversationId:x(n),conversationName:n}}runtimeStatus(e){const r=this.connections.get(e.id);return r?{wechatRpaRuntimeState:r.runtimeState,wechatRpaLastRunAt:r.lastRunAt??null,wechatRpaLastMessageAt:r.lastMessageAt??null,wechatRpaLastInterruptedAt:r.lastInterruptedAt??null,wechatRpaLastError:r.lastError??null,wechatRpaLastRunId:r.lastRunId??null,wechatRpaLastTracePath:r.lastTracePath??r.product?.helperTracePath??null,wechatRpaLastTraceSummary:r.lastTraceSummary??null,wechatRpaPendingReplyCount:r.pendingReplies.size+oe({config:e}),wechatRpaPendingReplies:Te(r,e),wechatRpaRecentTaskSummaries:r.recentTaskSummaries}:{}}ensureConnection(e){let r=this.connections.get(e.id);return r||(r={config:e,timer:null,deduper:new K,stopped:!1,conversations:new Map,pendingReplies:new Map,manualReviewReplies:new Map,completedPendingReplyKeys:new Set,pendingStatePath:void 0,messageStatePath:void 0,operation:Promise.resolve(),recentTaskSummaries:[],runtimeState:"idle_waiting",consecutiveInterruptions:0},this.connections.set(e.id,r)),r}enqueueOperation(e,r){const n=this.options.automationLane?()=>this.options.automationLane?.run(`wechat-rpa:${e.config.id}`,r):r,a=e.operation.then(n,n);return e.operation=a.then(()=>{},()=>{}),a}readSecret(e){const r=this.secrets.get(e.secretRef);if(!r||r.type!=="wechat-rpa")throw new Error("WeChat RPA channel is not configured on this daemon");return r}async pollOnce(e,r){if(e.stopped)return[];const n=[];e.lastRunAt=new Date().toISOString();try{if(Z(e,r))return e.runtimeState="cooldown",[];if(await this.drainPendingReplies(e,r))return[];let i=!1;e.runtimeState="syncing";const s=await this.readObservedMessages(e.config,r,u=>{$(e,u),u.interrupted&&(i=!0,_(e,u,u.error||"WeChat RPA poll was interrupted by user activity"))});e.lastError=null;for(const u of s){const o=se(u,{selfNicknames:U(r)});if(!o||!e.deduper.accept(o.messageId))continue;De(e),e.conversations.set(o.conversationId,o.conversationName),e.lastMessageAt=o.receivedAt;const c={type:"external.message",managerSessionId:e.config.managerSessionId,channelId:e.config.id,channelType:"wechat-rpa",conversationId:o.conversationId,conversationName:o.conversationName,messageId:o.messageId,sender:o.sender,text:o.text,attachments:o.attachments,receivedAt:o.receivedAt,isMentioned:o.isMentioned,replyTarget:"",rawRef:o.rawRef};n.push(this.onMessage?.(c)??c),w(e,{status:"received",runId:e.lastRunId??null,summary:`received ${o.conversationName}${o.sender.name?` from ${o.sender.name}`:""}: ${D(o.text||o.attachments[0]?.name||"attachment")}`})}return i||C(e),n}catch(a){throw e.lastError=a instanceof Error?a.message:String(a),w(e,{status:b(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),a}}async pollProductOnce(e,r,n){if(e.stopped)return[];if(!y(r.source))return[];if(await this.stopProductWhenControlPlaneDisabled(e,r))return[];const a=await this.ensureProductConnection(e,r);await B(a).catch(()=>null);const i=a.emitted.length;e.lastRunAt=new Date().toISOString(),e.runtimeState="syncing";try{await a.runner.tick(n);const s=a.emitted.slice(i);e.lastError=null;for(const u of s)e.conversations.set(u.conversationId,u.conversationName||""),e.lastMessageAt=u.receivedAt,w(e,{status:"received",runId:e.lastRunId??null,summary:`received ${u.conversationName||u.conversationId}${u.sender.name?` from ${u.sender.name}`:""}: ${D(u.text||u.attachments[0]?.name||"attachment")}`});return C(e),s}catch(s){throw e.lastError=s instanceof Error?s.message:String(s),e.runtimeState=b(e.lastError)==="blocked"?"interrupted":"idle_waiting",w(e,{status:b(e.lastError),runId:e.lastRunId??null,summary:e.lastError}),s}}async ensureProductConnection(e,r){const n=me(e.config,r);return e.product&&e.productConfigKey===n||(e.product&&await e.product.runner.stop().catch(()=>{}),e.product=ue({config:e.config,secret:r,createRunner:this.options.createProductRunner,onMessage:this.onMessage}),await B(e.product).catch(()=>{}),e.productConfigKey=n,e.lastTracePath=e.product.helperTracePath),e.product}async stopProductWhenControlPlaneDisabled(e,r){if(!y(r.source))return!1;let n=null;try{n=await ge(e.config,r)}catch(a){return e.productDisabled?(e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=a instanceof Error?a.message:String(a),!0):!1}if(n?.enabled!==!1)return e.productDisabled=!1,!1;if(e.productDisabled=!0,e.lastRunAt=new Date().toISOString(),e.runtimeState="idle_waiting",e.lastError=null,e.product){const a=e.product;e.product=void 0,e.productConfigKey=void 0,await a.runner.stop().catch(()=>{})}return!0}async drainPendingReplies(e,r){if(!e.pendingReplies.size)return!1;for(const n of Array.from(e.pendingReplies.values())){e.runtimeState="retrying",n.attempts+=1,n.lastAttemptAt=new Date().toISOString(),R(e);const a=await V(e.config,r,n.conversationName,n.skipText?"":n.text,n.attachmentPath,n.key);$(e,a);const i=Y(a,{text:n.skipText?"":n.text,attachmentPath:n.attachmentPath});if(i.status==="queued")return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=i.reason,e.lastError=i.reason,R(e),!0;if(a.interrupted){const s=a.error||"WeChat RPA pending send was interrupted by user activity";return n.text=i.text,n.attachmentPath=i.attachmentPath,n.skipText=i.skipText,n.lastInterruptedReason=s,_(e,a,s),R(e),!0}if(!a.ok){const s=a.error||"WeChat RPA pending send validator failed; manual review required before retry";return e.pendingReplies.delete(n.key),Q(e,n.key,s),R(e),!0}e.pendingReplies.delete(n.key),e.completedPendingReplyKeys.add(n.key),R(e),e.lastError=null,w(e,{status:"sent",runId:e.lastRunId??null,summary:a.rpaTraceSummary||`sent ${n.conversationName}`}),C(e)}return!1}async readObservedMessages(e,r,n){if(r.source==="fixture-jsonl")return Ke(r.fixturePath);if(r.source==="macos-flow")return xe(e,r,n);if(r.source==="wechat-rpa-lab")return Se(e,r,n);if(r.source==="windows-visual-flow")return Ne(e,r,n);const a=await q(),i=ae(a);return i?[i]:[]}seedConfiguredConversations(e,r){for(const n of g(r))e.conversations.set(x(n),n),e.conversations.set(N(n),n)}resolveConversationName(e,r,n){const i=this.connections.get(e.id)?.conversations.get(n);return i||(g(r).find(s=>x(s)===n||N(s)===n)??null)}}function me(t,e){return JSON.stringify({id:t.id,workDir:t.workDir,managerSessionId:t.managerSessionId,pollIntervalMs:e.pollIntervalMs,forceForeground:k(e),downloadAttachments:e.downloadAttachments!==!1,canReply:e.canReply!==!1,selfTriggerMarker:e.selfTriggerMarker||"",groups:g(e)})}async function ge(t,e,r){const n=r??H(),a=await n.getRuntimePolicy();if(a.feature?.enabled===!1)return{enabled:!1,reasonCode:a.feature.reasonCode||"feature_disabled"};if(a.runtime?.current?.status==="disabled")return{enabled:!1,reasonCode:"runtime_disabled"};const i=t.sessionId||t.managerSessionId;if(!i)return{enabled:!1,reasonCode:"session_missing"};let s;try{s=(await n.getBinding({sessionId:i})).binding}catch(o){const c=we(o);if(c==="binding_not_found"||c==="session_not_found"||c==="machine_not_found")return{enabled:!1,reasonCode:c};throw o}if(!s||s.enabled===!1)return{enabled:!1,reasonCode:"no_enabled_binding"};const u=g(e);return u.length?u.includes(s.conversationName)?{enabled:!0}:{enabled:!1,reasonCode:"binding_conversation_changed"}:{enabled:!1,reasonCode:"no_configured_conversation"}}async function B(t){const r=(await H().getRuntimePolicy()).runtime?.current;return t.runtime.foregroundPolicy="work",{enabled:r?.status!=="disabled"}}function we(t){if(t&&typeof t=="object"){const e=t.reasonCode;if(typeof e=="string")return e}return""}function j(t){return Number.isFinite(t)?Math.min(6e4,Math.max(1e3,Number(t))):le}function g(t){return Array.isArray(t.groups)?t.groups.map(e=>String(e?.name||"").trim()).filter(Boolean):[]}function U(t){return String(t.selfNickname||"").split(/[,\n,、]/).map(e=>e.trim()).filter(Boolean)}function ye(t,e){return e.length?e.some(r=>{const n=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`@\\s*${n}(?=$|\\s|[\uFF0C\u3002\uFF01\uFF1F,.!?:\uFF1A\uFF1B;\u3001)])`).test(t)}):!1}function Re(t,e=process.platform){return e!=="win32"?{ok:!1,message:"WeChat RPA Windows visual flow requires Windows"}:{ok:!1,message:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}}function ve(t,e=process.platform){return e!=="darwin"&&e!=="win32"?{ok:!1,message:"WeChat channel product source requires macOS or Windows"}:(Array.isArray(t.groups)?t.groups.map(n=>String(n?.name||"").trim()).filter(Boolean):[]).length?{ok:!0,message:"WeChat channel product source configured"}:{ok:!1,message:"WeChat channel product source requires at least one group"}}async function Se(t,e,r){const n=await z(t.workDir),a=[];for(const i of g(e)){const s=await Pe(n,t,e,i);if(r?.(s),!(!s.ok||s.interrupted))for(const u of s.newMessages??[]){const o=G(i,u,e);o&&a.push(o)}}return a}async function Pe(t,e,r,n){const a=fe(r.recentLimit),i={kind:a,requestId:`wechat-rpa:${n}:read:${Date.now()}`,targetGroup:n,policy:r.forceForeground?"work":"polite",limit:P(r.recentLimit,A,1,50),attachmentsDir:L(e,r)},s=await t.runWechatRpaReadLatest(i),u=Array.isArray(s.data?.structuredMessages)?s.data.structuredMessages:[],o=s.validation?.deterministic?.latestMessages??[],c=u.length?u.map((l,p)=>Ie(l,s.runId||"lab-read",p)):o.map((l,p)=>({id:`${s.runId||"lab-read"}:${p}`,text:l,confidence:.8}));return{ok:!!s.ok,groupName:n,interrupted:s.status==="interrupted",reason:s.status,rpaRunId:s.runId,rpaTraceSummary:`${a} ${s.status||(s.ok?"success":"failed")} ${n}`,recentMessages:c,newMessages:c,screenshotPath:s.tracePath,error:X(s)}}function Ie(t,e,r){return{id:`${e}:${t.index??r}`,text:String(t.text||t.card?.title||"").trim(),confidence:typeof t.confidence=="number"?t.confidence:.75,senderName:typeof t.senderName=="string"?t.senderName:null,attachments:Ae(t)}}function Ae(t){const e=String(t.kind||"");if(!["image","file","video-file","video-card","link-card","official-account-card","mini-program-card"].includes(e))return[];const r=typeof t.localPath=="string"?t.localPath:void 0;return[{type:e==="video-file"||e==="video-card"?"video":e==="link-card"||e==="official-account-card"||e==="mini-program-card"?"file":e,localPath:r,availability:r?"edge-local":"metadata-only"}]}async function Ce(t,e,r,n,a,i){const s=await z(t.workDir),u=be({groupName:r,text:n,attachmentPath:a,dedupeKey:i,requestId:i,policy:"work"}),o=[];for(const f of u){const v=await s.runWechatRpaTask(f);if(o.push(v),!v.ok)break}const c=o.find(f=>f.kind==="send-text"),l=o.find(f=>f.kind==="send-image"||f.kind==="send-video"||f.kind==="send-file"),p=u.length>0&&o.length===u.length&&o.every(f=>f.ok),d=o.some(f=>f.status==="interrupted");return{ok:p,groupName:r,interrupted:d,reason:o.at(-1)?.status,rpaRunId:o.at(-1)?.runId,rpaTraceSummary:`send ${p?"success":o.at(-1)?.status||"failed"} ${r} (${o.length}/${u.length})`,sentReply:!!n,sentReplyObserved:n?!!c?.ok:!1,sentAttachment:!!a,sentAttachmentObserved:a?!!l?.ok:!1,postSendScreenshotPath:o.at(-1)?.tracePath,error:p?void 0:X(o.at(-1))}}function be(t){const e=[],r=String(t.text||"").trim(),n=t.policy||"work";if(r&&e.push({kind:"send-text",requestId:t.requestId?`${t.requestId}:text`:void 0,targetGroup:t.groupName,policy:n,text:r,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:text`}:{}}),t.attachmentPath){const a=ke(t.attachmentPath);e.push({kind:a,requestId:t.requestId?`${t.requestId}:attachment`:void 0,targetGroup:t.groupName,policy:n,filePath:t.attachmentPath,...t.dedupeKey?{dedupeKey:`${t.dedupeKey}:attachment`}:{}})}return e}function ke(t){const e=h.extname(t).toLowerCase();return[".png",".jpg",".jpeg",".gif",".webp",".heic"].includes(e)?"send-image":[".mp4",".mov",".avi",".mkv"].includes(e)?"send-video":"send-file"}async function z(t){const e=J(t);return await import(te(e).href)}function J(t){const e=h.dirname(ee(import.meta.url)),n=[process.env.SHENNIAN_WECHAT_RPA_LAB_INDEX,h.resolve(e,"../../../../scripts/wechat-rpa-lab/index.mjs"),t?h.join(t,"scripts/wechat-rpa-lab/index.mjs"):"",h.resolve(process.cwd(),"scripts/wechat-rpa-lab/index.mjs")].filter(Boolean).find(a=>m.existsSync(a));if(!n)throw new Error("WeChat RPA Lab API is missing; set SHENNIAN_WECHAT_RPA_LAB_INDEX or run from the repository root");return h.resolve(n)}function X(t){return t?typeof t.error=="string"?t.error:t.error?.message||(t.ok?void 0:`WeChat RPA Lab task ${t.kind||"<unknown>"} ${t.status||"failed"}`):"WeChat RPA Lab did not return a result"}async function xe(t,e,r){const n=[];for(const a of g(e)){const i=await F({groupName:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:k(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:L(t,e)});if(r?.(i),!i.interrupted)for(const s of i.newMessages??[]){const u=G(a,s,e);u&&n.push(u)}}return n}async function Ne(t,e,r){const n=g(e)[0]||"<unbound>";return r?.({ok:!1,groupName:n,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}),[]}function G(t,e,r){const n=String(e.text||"").trim(),a=Array.isArray(e.attachments)?e.attachments:[];if(!n&&a.length===0)return null;const i=typeof e.senderName=="string"?e.senderName.trim():"",s=String(e.observedAt||e.timestampIso||"").trim()||new Date().toISOString();return{conversationName:t,senderName:i||null,text:n,attachments:ze(a),observedAt:s,isMentioned:e.isMentioned===!0||ye(n,U(r)),rawId:String(e.id||`${t}:${i}:${n}:${s}`)}}async function V(t,e,r,n,a,i){return e.source==="windows-visual-flow"?{ok:!1,groupName:r,interrupted:!0,reason:"windows-visual-flow-archived",newMessages:[],recentMessages:[],error:"WeChat RPA Windows visual flow is archived; redesign Windows support before enabling it"}:e.source==="wechat-rpa-lab"?Ce(t,e,r,n,a,i):F({groupName:r,replyText:n||void 0,attachmentPath:a,scriptPath:e.flowScriptPath,workDir:t.workDir,forceForeground:k(e),noRestore:e.noRestore!==!1,idleSeconds:P(e.idleSeconds,15,0,3600),recentLimit:P(e.recentLimit,A,0,50),downloadAttachmentsDir:L(t,e)})}function We(t){return t==="macos-flow"||t==="wechat-rpa-lab"}function Y(t,e){return e.text&&e.attachmentPath&&t.sentReplyObserved&&!t.sentAttachmentObserved?{status:t.interrupted?"continue":"queued",text:"",attachmentPath:e.attachmentPath,skipText:!0,reason:t.error||"WeChat RPA sent the text but did not confirm the attachment; queued attachment-only retry"}:{status:"continue",text:e.text,attachmentPath:e.attachmentPath,reason:t.error||""}}function Ee(t,e){return e.idempotencyKey?e.idempotencyKey:I.createHash("sha256").update(`${t.id}
2
- ${e.conversationId}
3
- ${e.text}
4
- ${e.attachment?.name||""}
5
- ${e.attachment?.size||0}`).digest("hex").slice(0,24)}function Te(t,e){return[...Array.from(t.pendingReplies.values()).map(n=>({replyId:n.key,idempotencyKey:n.key,status:"queued",conversationName:n.conversationName,reasonCode:n.lastInterruptedReason??null,nextAttemptAt:null,queuedAt:n.queuedAt,lastAttemptAt:n.lastAttemptAt??null,attemptCount:n.attempts,commitStage:null})),...ce({config:e})].slice(0,100)}function T(t,e){const r=t.pendingReplies.get(e.key);t.pendingReplies.set(e.key,{key:e.key,conversationId:e.conversationId,conversationName:e.conversationName,text:e.text,attachmentPath:e.attachmentPath,skipText:e.skipText,queuedAt:r?.queuedAt??new Date().toISOString(),attempts:r?.attempts??0,lastAttemptAt:r?.lastAttemptAt,lastInterruptedReason:e.reason}),R(t)}function Q(t,e,r){t.manualReviewReplies.set(e,{key:e,reason:r,createdAt:t.manualReviewReplies.get(e)?.createdAt??new Date().toISOString()}),t.lastError=r,w(t,{status:"failed",runId:t.lastRunId??null,summary:`manual review required: ${r}`}),R(t)}function O(t,e){const r=$e(e);if(t.pendingStatePath===r)return;t.pendingStatePath=r;const n=Oe(r);for(const a of n.pending??[])Me(a)&&(t.pendingReplies.has(a.key)||t.pendingReplies.set(a.key,a));for(const a of n.manualReview??[])_e(a)&&(t.manualReviewReplies.has(a.key)||t.manualReviewReplies.set(a.key,a));for(const a of n.completedKeys??[])typeof a=="string"&&a&&t.completedPendingReplyKeys.add(a)}function R(t){if(!t.pendingStatePath)return;const e=Array.from(t.completedPendingReplyKeys).slice(-500),r=Array.from(t.pendingReplies.values()).slice(0,500),n=Array.from(t.manualReviewReplies.values()).slice(-500);try{m.mkdirSync(h.dirname(t.pendingStatePath),{recursive:!0}),m.writeFileSync(t.pendingStatePath,JSON.stringify({version:1,pending:r,manualReview:n,completedKeys:e},null,2))}catch{}}function Oe(t){try{const e=JSON.parse(m.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,pending:[],completedKeys:[]}}catch{return{version:1,pending:[],completedKeys:[]}}}function Me(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.conversationId=="string"&&typeof e.conversationName=="string"&&typeof e.text=="string"&&typeof e.queuedAt=="string"&&typeof e.attempts=="number"&&Number.isFinite(e.attempts)&&(e.attachmentPath===void 0||typeof e.attachmentPath=="string")&&(e.skipText===void 0||typeof e.skipText=="boolean")}function _e(t){if(!t||typeof t!="object")return!1;const e=t;return typeof e.key=="string"&&typeof e.reason=="string"&&typeof e.createdAt=="string"}function $e(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return h.join(t.workDir,".shennian","wechat-rpa-pending-replies",`${e}.json`)}function M(t,e){const r=qe(e);if(t.messageStatePath===r)return;t.messageStatePath=r;const n=Le(r);t.deduper=new K((n.messageIds??[]).filter(a=>typeof a=="string"&&a.length>0))}function De(t){if(t.messageStatePath)try{m.mkdirSync(h.dirname(t.messageStatePath),{recursive:!0}),m.writeFileSync(t.messageStatePath,JSON.stringify({version:1,messageIds:t.deduper.snapshot()},null,2))}catch{}}function Le(t){try{const e=JSON.parse(m.readFileSync(t,"utf8"));return e&&e.version===1?e:{version:1,messageIds:[]}}catch{return{version:1,messageIds:[]}}}function qe(t){const e=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return h.join(t.workDir,".shennian","wechat-rpa-seen-messages",`${e}.json`)}function _(t,e,r){t.lastInterruptedAt=new Date().toISOString(),t.lastError=null,t.consecutiveInterruptions+=1,t.runtimeState="interrupted",Fe(t)&&(t.interruptionCooldownUntil=Date.now()+E,t.runtimeState="cooldown"),w(t,{status:"interrupted",runId:e.rpaRunId||t.lastRunId||null,summary:r})}function C(t){t.consecutiveInterruptions=0,t.interruptionCooldownUntil=void 0,t.runtimeState="idle_waiting"}function $(t,e){const r=e.postSendScreenshotPath||e.screenshotPath;e.rpaRunId&&(t.lastRunId=e.rpaRunId),r&&(t.lastTracePath=r),t.lastTraceSummary=e.rpaTraceSummary||[e.groupName?`group=${e.groupName}`:"",e.ok===!1?"failed":e.interrupted?"interrupted":"ok",e.reason?`reason=${e.reason}`:""].filter(Boolean).join(" ")}function w(t,e){const r=e.runId||t.lastRunId||`local:${Date.now().toString(36)}`;t.lastRunId=r,t.recentTaskSummaries.unshift({at:new Date().toISOString(),status:e.status,runId:r,summary:D(e.summary)}),t.recentTaskSummaries=t.recentTaskSummaries.slice(0,he)}function b(t){const e=String(t||"").toLowerCase();return/permission|accessibility|screen recording|automation|window|foreground|target group|refusing|requires|安全|权限|窗口|目标群/.test(e)?"blocked":"failed"}function D(t){return String(t||"").replace(/\s+/g," ").trim().slice(0,180)}function Z(t,e){if(k(e))return!1;const r=t.interruptionCooldownUntil;return r?Date.now()<r?!0:(t.interruptionCooldownUntil=void 0,t.consecutiveInterruptions=0,!1):!1}function k(t){return t.forceForeground!==!1}function Fe(t){return!Number.isFinite(W)||W<=0||!Number.isFinite(E)||E<=0?!1:t.consecutiveInterruptions>=W}function Ke(t){return!t||!m.existsSync(t)?[]:m.readFileSync(t,"utf-8").split(`
6
- `).map(e=>e.trim()).filter(Boolean).map(e=>{try{return JSON.parse(e)}catch{return null}}).filter(e=>!!e)}async function He(t,e){if(e.localPath){if(!m.existsSync(e.localPath))throw new Error(`WeChat RPA local attachment does not exist: ${e.localPath}`);return e.localPath}if(e.url)return Be(t,e);throw new Error("WeChat RPA attachment requires localPath or url; dataBase64 is not accepted over Manager IPC")}async function Be(t,e){if(!e.url||!/^https?:\/\//i.test(e.url))throw new Error("WeChat RPA attachment url must be http(s)");if(Number.isFinite(e.size)&&e.size>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${e.size} bytes. Max: ${S} bytes.`);const r=await fetch(e.url);if(!r.ok)throw new Error(`WeChat RPA attachment url download failed: ${r.status}`);const n=Number(r.headers.get("content-length")||e.size||0);if(n>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${n} bytes. Max: ${S} bytes.`);const a=Buffer.from(await r.arrayBuffer());return je(t,e,a)}function je(t,e,r){if(!r.byteLength)throw new Error("WeChat RPA attachment is empty");if(r.byteLength>S)throw new Error(`WeChat RPA cross-machine attachment is too large: ${r.byteLength} bytes. Max: ${S} bytes.`);const n=h.join(t,".uploads","wechat-rpa","outbound");m.mkdirSync(n,{recursive:!0});const a=Ue(e.name||"attachment"),i=I.createHash("sha256").update(r).digest("hex").slice(0,12),s=h.extname(a),u=s?a.slice(0,-s.length):a,o=h.join(n,`${u}-${i}${s}`);return m.existsSync(o)||m.writeFileSync(o,r),o}function Ue(t){return h.basename(t||"attachment").normalize("NFKC").replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").replace(/\s+/g," ").replace(/^[ ._]+|[ ._]+$/g,"")||"attachment"}function L(t,e){if(e.downloadAttachments===!1)return;const r=e.downloadAttachmentsDir?.trim();if(r)return h.resolve(r);const n=I.createHash("sha256").update(t.id).digest("hex").slice(0,16);return ne("wechat-rpa","attachments","inbound",n)}function ze(t){if(!Array.isArray(t))return[];const e=process.env.SHENNIAN_MACHINE_ID?.trim();return t.map(r=>r?.localPath?{...r,availability:r.availability||"edge-local",...r.machineId||!e?{}:{machineId:e}}:r)}function P(t,e,r,n){const a=Number(t);return Number.isFinite(a)?Math.min(n,Math.max(r,a)):e}export{at as WeChatRpaChannelAdapter,ze as annotateWeChatRpaInboundAttachments,He as materializeWeChatRpaOutboundAttachment,be as planWeChatRpaLabSendTasks,L as resolveWeChatRpaInboundAttachmentDir,fe as selectWeChatRpaLabReadKind,ke as weChatRpaLabTaskKindForAttachment,ve as weChatRpaProductSourceHealth,Re as windowsVisualFlowHealth};
@@ -1,51 +0,0 @@
1
- import type { ExternalChannelAdapter, ExternalChannelConfig, ExternalReply } from './base.js';
2
- export declare class WeComChannelAdapter implements ExternalChannelAdapter {
3
- private onMessage?;
4
- readonly type: "wecom";
5
- private secrets;
6
- private connections;
7
- constructor(onMessage?: ((event: {
8
- managerSessionId: string;
9
- channelId: string;
10
- channelType: "wecom";
11
- conversationId: string;
12
- messageId: string;
13
- sender: {
14
- id: string;
15
- name?: string | null;
16
- };
17
- text: string;
18
- attachments: Array<{
19
- type: string;
20
- name?: string;
21
- url?: string;
22
- }>;
23
- receivedAt: string;
24
- replyTarget: string;
25
- }) => void) | undefined);
26
- connect(config: ExternalChannelConfig): Promise<void>;
27
- disconnect(config: ExternalChannelConfig): Promise<void>;
28
- send(config: ExternalChannelConfig, reply: ExternalReply): Promise<void>;
29
- health(config: ExternalChannelConfig): Promise<{
30
- ok: boolean;
31
- message?: string;
32
- }>;
33
- private ensureConnection;
34
- private readSecret;
35
- private openConnection;
36
- private scheduleReconnect;
37
- private sendJson;
38
- private sendRequest;
39
- private handlePayload;
40
- private isDuplicate;
41
- private extractText;
42
- private filterContextText;
43
- private normalizeContextLine;
44
- private rememberContextLine;
45
- private raiseForWeComError;
46
- private parsePayload;
47
- private payloadReqId;
48
- private asRecord;
49
- private isCallbackCommand;
50
- private newReqId;
51
- }
@@ -1,4 +0,0 @@
1
- import{randomUUID as m}from"node:crypto";import u from"ws";import{ChannelSecretRegistry as f}from"./secret-registry.js";const C="wss://openws.work.weixin.qq.com",w="aibot_subscribe",_="aibot_msg_callback",y="aibot_callback",S="aibot_send_msg",R="aibot_respond_msg",I="ping",h=[2e3,5e3,1e4,3e4],g=4e3;class q{onMessage;type="wecom";secrets=new f;connections=new Map;constructor(e){this.onMessage=e}async connect(e){if(!e.enabled)return;const t=this.ensureConnection(e);return t.connecting||(t.stopped=!1,t.config=e,t.connecting=this.openConnection(t).finally(()=>{t.connecting=null})),t.connecting}async disconnect(e){const t=this.connections.get(e.id);if(!t)return;t.stopped=!0,t.reconnectTimer&&clearTimeout(t.reconnectTimer),t.pingTimer&&clearInterval(t.pingTimer),t.reconnectTimer=null,t.pingTimer=null;for(const r of t.pending.values())clearTimeout(r.timer),r.reject(new Error("WeCom adapter disconnected"));t.pending.clear();const n=t.socket;t.socket=null,n&&await new Promise(r=>{n.once("close",()=>r()),n.close(),setTimeout(r,500)}),this.connections.delete(e.id)}async send(e,t){const n=this.ensureConnection(e);await this.connect(e);const r=t.text.trim();if(!r)throw new Error("Reply text is required");const s=t.messageId?n.replyReqIds.get(t.messageId):void 0,i=n.lastChatReqIds.get(t.conversationId),o=s||i?{msg:{msgtype:"markdown",markdown:{content:r.slice(0,g)}},req_id:s||i}:{chatid:t.conversationId,msgtype:"markdown",markdown:{content:r.slice(0,g)}},c=s||i?R:S,d=await this.sendRequest(n,c,o);this.raiseForWeComError(d,"send message")}async health(e){const t=this.readSecret(e);return!t?.botId||!t.secret?{ok:!1,message:"WeCom adapter requires local credentials"}:this.connections.get(e.id)?.socket?.readyState===u.OPEN?{ok:!0}:{ok:!1,message:"WeCom websocket disconnected"}}ensureConnection(e){let t=this.connections.get(e.id);return t||(t={config:e,socket:null,connecting:null,deviceId:m().replace(/-/g,""),stopped:!1,reconnectAttempt:0,reconnectTimer:null,pingTimer:null,pending:new Map,replyReqIds:new Map,lastChatReqIds:new Map,dedup:new Set,dedupQueue:[],seenContextLineKeys:new Set,seenContextLineQueue:[]},this.connections.set(e.id,t)),t.config=e,t}readSecret(e){const t=this.secrets.get(e.secretRef);if(!t||t.type!=="wecom"||!t.botId||!t.secret)throw new Error("WeCom outbound adapter is not configured on this daemon");return t}async openConnection(e){const t=this.readSecret(e.config),n=new u(C);e.socket=n,await new Promise((s,i)=>{const o=()=>{n.off("open",c),n.off("error",d)},c=()=>{o(),s()},d=a=>{o(),i(a)};n.once("open",c),n.once("error",d)}),n.on("message",s=>{const i=this.parsePayload(s.toString());i&&this.handlePayload(e,i)}),n.on("close",()=>this.scheduleReconnect(e)),n.on("error",()=>this.scheduleReconnect(e));const r=await this.sendRequest(e,w,{bot_id:t.botId,secret:t.secret,device_id:e.deviceId},2e4);this.raiseForWeComError(r,"subscribe"),e.reconnectAttempt=0,e.pingTimer&&clearInterval(e.pingTimer),e.pingTimer=setInterval(()=>{this.sendJson(e,{cmd:I,headers:{req_id:this.newReqId("ping")},body:{}}).catch(()=>{})},3e4),e.pingTimer.unref()}scheduleReconnect(e){if(e.stopped||e.reconnectTimer||e.connecting)return;const t=h[Math.min(e.reconnectAttempt,h.length-1)];e.reconnectAttempt+=1,e.reconnectTimer=setTimeout(()=>{e.reconnectTimer=null,this.connect(e.config).catch(()=>{})},t),e.reconnectTimer.unref()}async sendJson(e,t){if(!e.socket||e.socket.readyState!==u.OPEN)throw new Error("WeCom websocket is not connected");e.socket.send(JSON.stringify(t))}async sendRequest(e,t,n,r=15e3){const s=this.newReqId(t);return new Promise((i,o)=>{const c=setTimeout(()=>{e.pending.delete(s),o(new Error(`WeCom request timed out: ${t}`))},r);c.unref(),e.pending.set(s,{resolve:i,reject:o,timer:c}),this.sendJson(e,{cmd:t,headers:{req_id:s},body:n}).catch(d=>{clearTimeout(c),e.pending.delete(s),o(d instanceof Error?d:new Error(String(d)))})})}handlePayload(e,t){const n=this.payloadReqId(t),r=String(t.cmd||"");if(n&&e.pending.has(n)&&!this.isCallbackCommand(r)){const p=e.pending.get(n);clearTimeout(p.timer),e.pending.delete(n),p.resolve(t);return}if(!this.isCallbackCommand(r))return;const s=this.asRecord(t.body);if(!s)return;const i=String(s.msgid||n||m());if(this.isDuplicate(e,i))return;n&&e.replyReqIds.set(i,n);const o=this.asRecord(s.from)??{},c=String(o.userid||""),d=String(o.name||o.userid||""),a=String(s.chatid||c).trim();if(!a)return;n&&e.lastChatReqIds.set(a,n);const l=this.filterContextText(e,a,this.extractText(s).trim());l&&this.onMessage?.({managerSessionId:e.config.managerSessionId,channelId:e.config.id,channelType:"wecom",conversationId:a,messageId:i,sender:{id:c||a,name:d||c||a},text:l,attachments:[],receivedAt:new Date().toISOString(),replyTarget:""})}isDuplicate(e,t){if(e.dedup.has(t))return!0;if(e.dedup.add(t),e.dedupQueue.push(t),e.dedupQueue.length>1e3){const n=e.dedupQueue.shift();n&&e.dedup.delete(n)}return!1}extractText(e){const t=String(e.msgtype||"").toLowerCase();if(t==="mixed"){const r=this.asRecord(e.mixed);return(Array.isArray(r?.msg_item)?r?.msg_item:[]).map(i=>{const o=this.asRecord(i);return String(o?.msgtype||"").toLowerCase()!=="text"?"":String(this.asRecord(o?.text)?.content||"").trim()}).filter(Boolean).join(`
2
- `).trim()}const n=String(this.asRecord(e.text)?.content||"").trim();return n||(t==="voice"?String(this.asRecord(e.voice)?.content||"").trim():t==="appmsg"?String(this.asRecord(e.appmsg)?.title||"").trim():"")}filterContextText(e,t,n){if(!n.includes("\u5916\u90E8\u6D88\u606F\u4E0A\u4E0B\u6587")&&!n.includes("\u5916\u90E8\u4F01\u4E1A\u5FAE\u4FE1\u7FA4\u6D88\u606F\u4E0A\u4E0B\u6587"))return n;const r=n.split(/\r?\n/),s=[],i=[];let o=!1;for(const c of r){const d=this.normalizeContextLine(c);if(!d){o||s.push(c);continue}o=!0;const a=`${t}
3
- ${d}`;e.seenContextLineKeys.has(a)||(this.rememberContextLine(e,a),i.push(c))}return o?i.length?[...s,...i].filter(c=>c.trim()).join(`
4
- `).trim():"":n}normalizeContextLine(e){const t=e.trim().replace(/^=>\s*/,"");return/^\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\s+.+/.test(t)?t:null}rememberContextLine(e,t){for(e.seenContextLineKeys.add(t),e.seenContextLineQueue.push(t);e.seenContextLineQueue.length>2e3;){const n=e.seenContextLineQueue.shift();n&&e.seenContextLineKeys.delete(n)}}raiseForWeComError(e,t){const n=typeof e.errcode=="number"?e.errcode:Number(e.errcode||0);if(!Number.isFinite(n)||n===0)return;const r=String(e.errmsg||"unknown error");throw new Error(`WeCom ${t} failed: ${r} (${n})`)}parsePayload(e){try{const t=JSON.parse(e);return t&&typeof t=="object"?t:null}catch{return null}}payloadReqId(e){const n=this.asRecord(e.headers)?.req_id;return typeof n=="string"&&n?n:null}asRecord(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:null}isCallbackCommand(e){return e===_||e===y}newReqId(e){return`${e}_${m().replace(/-/g,"")}`}}export{q as WeComChannelAdapter};
@@ -1,9 +0,0 @@
1
- export type ExternalAttachmentKind = 'image' | 'video' | 'file';
2
- export type ExternalAttachmentPayload = {
3
- kind: ExternalAttachmentKind;
4
- name: string;
5
- mimeType: string;
6
- size: number;
7
- localPath: string;
8
- };
9
- export declare function readExternalAttachment(filePath: string, kind: ExternalAttachmentKind): ExternalAttachmentPayload;
@@ -1 +0,0 @@
1
- import m from"node:fs";import n from"node:path";const p={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".gif":"image/gif",".webp":"image/webp",".mp4":"video/mp4",".mov":"video/quicktime",".pdf":"application/pdf",".txt":"text/plain",".md":"text/markdown",".csv":"text/csv",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation",".zip":"application/zip"},a=Number(process.env.SHENNIAN_EXTERNAL_ATTACHMENT_MAX_BYTES||20*1024*1024);function r(i,t){const e=n.extname(i).toLowerCase();return p[e]?p[e]:t==="image"?"image/jpeg":t==="video"?"video/mp4":"application/octet-stream"}function l(i,t){const e=n.resolve(i),o=m.statSync(e);if(!o.isFile())throw new Error(`Attachment is not a file: ${e}`);if(o.size>a)throw new Error(`Attachment is too large: ${o.size} bytes. Max: ${a} bytes.`);return{kind:t,name:n.basename(e),mimeType:r(e,t),size:o.size,localPath:e}}export{l as readExternalAttachment};
@@ -1,2 +0,0 @@
1
- import type { Command } from 'commander';
2
- export declare function registerExternalCommand(program: Command): void;
@@ -1 +0,0 @@
1
- import d from"node:fs";import s from"chalk";import{resolveShennianPath as c}from"../config/index.js";import{readExternalAttachment as a}from"./external-attachments.js";function p(){const n=c("runtime","manager-ipc.json");try{const t=JSON.parse(d.readFileSync(n,"utf-8")),e=Number(t.pid);return Number.isInteger(e)&&e>0&&!m(e)?{error:`Manager IPC runtime file is stale for PID ${e}. Restart the desktop daemon and retry.`}:{url:typeof t.url=="string"?t.url:void 0,token:typeof t.token=="string"?t.token:void 0}}catch{return{}}}function l(n){const t=!process.env.SHENNIAN_MANAGER_IPC_URL||!process.env.SHENNIAN_MANAGER_IPC_TOKEN?p():{};t.error&&(console.error(s.red(`\u2717 ${t.error}`)),process.exit(1));const e=process.env.SHENNIAN_MANAGER_IPC_URL||t.url,o=process.env.SHENNIAN_MANAGER_IPC_TOKEN||t.token,i=n?.trim()||process.env.SHENNIAN_EXTERNAL_SESSION_ID||process.env.SHENNIAN_MANAGER_SESSION_ID;return(!e||!o||!i)&&(console.error(s.red("\u2717 This command must run inside a Shennian conversation with an external channel, or pass --session-id <id>.")),process.exit(1)),{url:e,token:o,sessionId:i}}function m(n){try{return process.kill(n,0),!0}catch{return!1}}async function r(n){const t=l(n.sessionId),e=n.replyTarget?.trim()||process.env.SHENNIAN_EXTERNAL_REPLY_TARGET||void 0,o=await fetch(`${t.url}/external/reply`,{method:"POST",headers:{authorization:`Bearer ${t.token}`,"content-type":"application/json","x-shennian-manager-session-id":t.sessionId},body:JSON.stringify({managerSessionId:t.sessionId,text:n.text,attachment:n.attachment,idempotencyKey:n.idempotencyKey,replyTarget:e})}),i=await o.json().catch(()=>({ok:!1,error:o.statusText}));if(!o.ok||!i.ok)throw new Error(i.error||`External send failed: ${o.status}`);console.log("ok")}function E(n){const t=n.command("external").description("External channel tools for this Shennian conversation");t.command("send").description("Send a message to the external channel bound to this conversation").requiredOption("--text <text>","Message text").option("--session-id <id>","Shennian conversation/session id; defaults to injected current-session env").option("--reply-target <id>","Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message").option("--idempotency-key <key>","Idempotency key").action(async e=>{await r({text:e.text,replyTarget:e.replyTarget,idempotencyKey:e.idempotencyKey,sessionId:e.sessionId})}),t.command("send-image").description("Send an image file to the external channel bound to this conversation").requiredOption("--path <path>","Image file path").option("--caption <text>","Optional text to send before the image").option("--session-id <id>","Shennian conversation/session id; defaults to injected current-session env").option("--reply-target <id>","Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message").option("--idempotency-key <key>","Idempotency key").action(async e=>{await r({text:e.caption,attachment:a(e.path,"image"),replyTarget:e.replyTarget,idempotencyKey:e.idempotencyKey,sessionId:e.sessionId})}),t.command("send-video").description("Send a video file to the external channel bound to this conversation").requiredOption("--path <path>","Video file path").option("--caption <text>","Optional text to send before the video").option("--session-id <id>","Shennian conversation/session id; defaults to injected current-session env").option("--reply-target <id>","Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message").option("--idempotency-key <key>","Idempotency key").action(async e=>{await r({text:e.caption,attachment:a(e.path,"video"),replyTarget:e.replyTarget,idempotencyKey:e.idempotencyKey,sessionId:e.sessionId})}),t.command("send-file").description("Send a file to the external channel bound to this conversation").requiredOption("--path <path>","File path").option("--caption <text>","Optional text to send before the file").option("--session-id <id>","Shennian conversation/session id; defaults to injected current-session env").option("--reply-target <id>","Daemon-generated reply target; defaults to SHENNIAN_EXTERNAL_REPLY_TARGET or latest external message").option("--idempotency-key <key>","Idempotency key").action(async e=>{await r({text:e.caption,attachment:a(e.path,"file"),replyTarget:e.replyTarget,idempotencyKey:e.idempotencyKey,sessionId:e.sessionId})})}export{E as registerExternalCommand};
@@ -1,80 +0,0 @@
1
- type HelperRuntimePlatform = 'darwin' | 'win32';
2
- export type HelperRuntimeOfficialArtifact = {
3
- platform: HelperRuntimePlatform;
4
- arch: string;
5
- helperVersion: string;
6
- protocolVersion: number;
7
- minCliVersion: string;
8
- url: string;
9
- sizeBytes: number;
10
- sha256: string;
11
- packageManifestSha256?: string;
12
- evidenceUrl?: string;
13
- };
14
- export type HelperRuntimeOfficialManifest = {
15
- schemaVersion: 1;
16
- kind: 'shennian-helper-runtime-index';
17
- channel: string;
18
- generatedAt: string;
19
- artifacts: HelperRuntimeOfficialArtifact[];
20
- };
21
- export type HelperRuntimeOfficialDownloadFailureReason = 'helper_runtime_manifest_unavailable' | 'helper_runtime_artifact_unavailable' | 'helper_runtime_download_failed' | 'helper_runtime_artifact_integrity_mismatch' | 'helper_runtime_artifact_invalid';
22
- export type HelperRuntimeOfficialDownloadResult = {
23
- ok: true;
24
- sourceDir: string;
25
- tempDir: string;
26
- cleanup: () => void;
27
- channel: string;
28
- manifestUrl: string;
29
- artifactUrl: string;
30
- artifactSha256: string;
31
- artifactSizeBytes: number;
32
- platform: HelperRuntimePlatform;
33
- arch: string;
34
- helperVersion: string;
35
- protocolVersion: number;
36
- minCliVersion: string;
37
- } | {
38
- ok: false;
39
- reasonCode: HelperRuntimeOfficialDownloadFailureReason;
40
- message: string;
41
- manifestUrl?: string;
42
- artifactUrl?: string;
43
- };
44
- type FetchLike = (url: string, init?: {
45
- signal?: AbortSignal;
46
- }) => Promise<Response>;
47
- export type HelperRuntimeOfficialDownloadOptions = {
48
- platform: HelperRuntimePlatform;
49
- arch?: string;
50
- env?: NodeJS.ProcessEnv;
51
- channel?: string;
52
- manifestUrl?: string;
53
- fetchImpl?: FetchLike;
54
- timeoutMs?: number;
55
- maxBytes?: number;
56
- workRoot?: string;
57
- downloadFile?: (input: {
58
- url: string;
59
- outputPath: string;
60
- fetchImpl: FetchLike;
61
- timeoutMs: number;
62
- maxBytes: number;
63
- }) => Promise<void>;
64
- extractZip?: (input: {
65
- zipPath: string;
66
- outputDir: string;
67
- platform: HelperRuntimePlatform;
68
- }) => Promise<void> | void;
69
- };
70
- export declare function downloadOfficialHelperRuntimePackage(options: HelperRuntimeOfficialDownloadOptions): Promise<HelperRuntimeOfficialDownloadResult>;
71
- export declare function getOfficialHelperRuntimeManifestUrl(input?: {
72
- env?: NodeJS.ProcessEnv;
73
- channel?: string;
74
- }): string;
75
- export declare function selectOfficialHelperRuntimeArtifact(input: {
76
- manifest: HelperRuntimeOfficialManifest;
77
- platform: HelperRuntimePlatform;
78
- arch: string;
79
- }): HelperRuntimeOfficialArtifact | null;
80
- export {};