@teammy/server 0.1.4 → 0.1.5

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 (444) hide show
  1. package/dist/main.js +682 -24
  2. package/dist/web/assets/Assistant-Dm_p8EZl.js +1 -0
  3. package/dist/web/assets/{Dashboard-dCu2Cdxd.js → Dashboard-fwW9NxcC.js} +1 -1
  4. package/dist/web/assets/{Files-cRhoWviM.js → Files-D-KCDWmO.js} +1 -1
  5. package/dist/web/assets/{Markdown-Bq8P_NBV.js → Markdown-oZagG-wy.js} +1 -1
  6. package/dist/web/assets/{Sessions-BxHsX1C5.js → Sessions-BQhGiLvz.js} +1 -1
  7. package/dist/web/assets/{WorkspaceTree-CD94JsaJ.js → WorkspaceTree-B9733pV3.js} +1 -1
  8. package/dist/web/assets/{file-links-CFCIacLV.js → file-links-CD346_jf.js} +1 -1
  9. package/dist/web/assets/{index-B-MqU_88.css → index-Be5hGscJ.css} +1 -1
  10. package/dist/web/assets/index-BiiPuwDi.js +66 -0
  11. package/dist/web/index.html +2 -2
  12. package/node_modules/@wecom/aibot-node-sdk/README.md +786 -0
  13. package/node_modules/@wecom/aibot-node-sdk/dist/api.d.ts +17 -0
  14. package/node_modules/@wecom/aibot-node-sdk/dist/client.d.ts +217 -0
  15. package/node_modules/@wecom/aibot-node-sdk/dist/crypto.d.ts +12 -0
  16. package/node_modules/@wecom/aibot-node-sdk/dist/index.cjs.js +1563 -0
  17. package/node_modules/@wecom/aibot-node-sdk/dist/index.cjs.js.map +1 -0
  18. package/node_modules/@wecom/aibot-node-sdk/dist/index.d.ts +1324 -0
  19. package/node_modules/@wecom/aibot-node-sdk/dist/index.esm.js +1544 -0
  20. package/node_modules/@wecom/aibot-node-sdk/dist/index.esm.js.map +1 -0
  21. package/node_modules/@wecom/aibot-node-sdk/dist/logger.d.ts +14 -0
  22. package/node_modules/@wecom/aibot-node-sdk/dist/message-handler.d.ts +29 -0
  23. package/node_modules/@wecom/aibot-node-sdk/dist/types/api.d.ts +466 -0
  24. package/node_modules/@wecom/aibot-node-sdk/dist/types/common.d.ts +30 -0
  25. package/node_modules/@wecom/aibot-node-sdk/dist/types/config.d.ts +34 -0
  26. package/node_modules/@wecom/aibot-node-sdk/dist/types/event.d.ts +109 -0
  27. package/node_modules/@wecom/aibot-node-sdk/dist/types/index.d.ts +10 -0
  28. package/node_modules/@wecom/aibot-node-sdk/dist/types/message.d.ts +158 -0
  29. package/node_modules/@wecom/aibot-node-sdk/dist/utils.d.ts +19 -0
  30. package/node_modules/@wecom/aibot-node-sdk/dist/wecom-crypto/index.d.ts +46 -0
  31. package/node_modules/@wecom/aibot-node-sdk/dist/ws.d.ts +161 -0
  32. package/node_modules/@wecom/aibot-node-sdk/package.json +43 -0
  33. package/node_modules/asynckit/LICENSE +21 -0
  34. package/node_modules/asynckit/README.md +233 -0
  35. package/node_modules/asynckit/bench.js +76 -0
  36. package/node_modules/asynckit/index.js +6 -0
  37. package/node_modules/asynckit/lib/abort.js +29 -0
  38. package/node_modules/asynckit/lib/async.js +34 -0
  39. package/node_modules/asynckit/lib/defer.js +26 -0
  40. package/node_modules/asynckit/lib/iterate.js +75 -0
  41. package/node_modules/asynckit/lib/readable_asynckit.js +91 -0
  42. package/node_modules/asynckit/lib/readable_parallel.js +25 -0
  43. package/node_modules/asynckit/lib/readable_serial.js +25 -0
  44. package/node_modules/asynckit/lib/readable_serial_ordered.js +29 -0
  45. package/node_modules/asynckit/lib/state.js +37 -0
  46. package/node_modules/asynckit/lib/streamify.js +141 -0
  47. package/node_modules/asynckit/lib/terminator.js +29 -0
  48. package/node_modules/asynckit/package.json +63 -0
  49. package/node_modules/asynckit/parallel.js +43 -0
  50. package/node_modules/asynckit/serial.js +17 -0
  51. package/node_modules/asynckit/serialOrdered.js +75 -0
  52. package/node_modules/asynckit/stream.js +21 -0
  53. package/node_modules/axios/CHANGELOG.md +1934 -0
  54. package/node_modules/axios/LICENSE +7 -0
  55. package/node_modules/axios/MIGRATION_GUIDE.md +877 -0
  56. package/node_modules/axios/README.md +2786 -0
  57. package/node_modules/axios/dist/axios.js +6460 -0
  58. package/node_modules/axios/dist/axios.min.js +5 -0
  59. package/node_modules/axios/dist/axios.min.js.map +1 -0
  60. package/node_modules/axios/dist/browser/axios.cjs +6509 -0
  61. package/node_modules/axios/dist/esm/axios.js +6530 -0
  62. package/node_modules/axios/dist/esm/axios.min.js +3 -0
  63. package/node_modules/axios/dist/esm/axios.min.js.map +1 -0
  64. package/node_modules/axios/dist/node/axios.cjs +7491 -0
  65. package/node_modules/axios/index.d.cts +785 -0
  66. package/node_modules/axios/index.d.ts +804 -0
  67. package/node_modules/axios/index.js +45 -0
  68. package/node_modules/axios/lib/adapters/README.md +36 -0
  69. package/node_modules/axios/lib/adapters/adapters.js +132 -0
  70. package/node_modules/axios/lib/adapters/fetch.js +749 -0
  71. package/node_modules/axios/lib/adapters/http.js +1609 -0
  72. package/node_modules/axios/lib/adapters/xhr.js +384 -0
  73. package/node_modules/axios/lib/axios.js +89 -0
  74. package/node_modules/axios/lib/cancel/CancelToken.js +135 -0
  75. package/node_modules/axios/lib/cancel/CanceledError.js +22 -0
  76. package/node_modules/axios/lib/cancel/isCancel.js +5 -0
  77. package/node_modules/axios/lib/core/Axios.js +361 -0
  78. package/node_modules/axios/lib/core/AxiosError.js +278 -0
  79. package/node_modules/axios/lib/core/AxiosHeaders.js +487 -0
  80. package/node_modules/axios/lib/core/InterceptorManager.js +182 -0
  81. package/node_modules/axios/lib/core/README.md +8 -0
  82. package/node_modules/axios/lib/core/assertParamsAcyclic.js +49 -0
  83. package/node_modules/axios/lib/core/buildFullPath.js +76 -0
  84. package/node_modules/axios/lib/core/createMultipartBody.js +37 -0
  85. package/node_modules/axios/lib/core/dispatchRequest.js +115 -0
  86. package/node_modules/axios/lib/core/mergeConfig.js +237 -0
  87. package/node_modules/axios/lib/core/methodList.js +17 -0
  88. package/node_modules/axios/lib/core/setFormDataHeaders.js +27 -0
  89. package/node_modules/axios/lib/core/settle.js +27 -0
  90. package/node_modules/axios/lib/core/transformData.js +28 -0
  91. package/node_modules/axios/lib/defaults/index.js +178 -0
  92. package/node_modules/axios/lib/defaults/transitional.js +11 -0
  93. package/node_modules/axios/lib/env/README.md +3 -0
  94. package/node_modules/axios/lib/env/classes/FormData.js +2 -0
  95. package/node_modules/axios/lib/env/data.js +1 -0
  96. package/node_modules/axios/lib/helpers/AxiosTransformStream.js +165 -0
  97. package/node_modules/axios/lib/helpers/AxiosURLSearchParams.js +59 -0
  98. package/node_modules/axios/lib/helpers/Http2Sessions.js +122 -0
  99. package/node_modules/axios/lib/helpers/HttpStatusCode.js +88 -0
  100. package/node_modules/axios/lib/helpers/README.md +7 -0
  101. package/node_modules/axios/lib/helpers/ZlibHeaderTransformStream.js +29 -0
  102. package/node_modules/axios/lib/helpers/bind.js +14 -0
  103. package/node_modules/axios/lib/helpers/buildURL.js +69 -0
  104. package/node_modules/axios/lib/helpers/callbackify.js +18 -0
  105. package/node_modules/axios/lib/helpers/combineURLs.js +23 -0
  106. package/node_modules/axios/lib/helpers/composeSignals.js +71 -0
  107. package/node_modules/axios/lib/helpers/cookies.js +88 -0
  108. package/node_modules/axios/lib/helpers/deprecatedMethod.js +31 -0
  109. package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +193 -0
  110. package/node_modules/axios/lib/helpers/formDataToBlob.js +113 -0
  111. package/node_modules/axios/lib/helpers/formDataToJSON.js +149 -0
  112. package/node_modules/axios/lib/helpers/formDataToStream.js +119 -0
  113. package/node_modules/axios/lib/helpers/fromDataURI.js +75 -0
  114. package/node_modules/axios/lib/helpers/isAbsoluteURL.js +19 -0
  115. package/node_modules/axios/lib/helpers/isAxiosError.js +14 -0
  116. package/node_modules/axios/lib/helpers/isURLSameOrigin.js +11 -0
  117. package/node_modules/axios/lib/helpers/normalizeURLForProtocolCheck.js +24 -0
  118. package/node_modules/axios/lib/helpers/null.js +2 -0
  119. package/node_modules/axios/lib/helpers/parseHeaders.js +72 -0
  120. package/node_modules/axios/lib/helpers/parseProtocol.js +6 -0
  121. package/node_modules/axios/lib/helpers/progressEventReducer.js +54 -0
  122. package/node_modules/axios/lib/helpers/readBlob.js +15 -0
  123. package/node_modules/axios/lib/helpers/resolveConfig.js +120 -0
  124. package/node_modules/axios/lib/helpers/sanitizeHeaderValue.js +60 -0
  125. package/node_modules/axios/lib/helpers/shouldBypassProxy.js +520 -0
  126. package/node_modules/axios/lib/helpers/speedometer.js +55 -0
  127. package/node_modules/axios/lib/helpers/spread.js +28 -0
  128. package/node_modules/axios/lib/helpers/throttle.js +45 -0
  129. package/node_modules/axios/lib/helpers/toFormData.js +279 -0
  130. package/node_modules/axios/lib/helpers/toURLEncodedForm.js +19 -0
  131. package/node_modules/axios/lib/helpers/trackStream.js +89 -0
  132. package/node_modules/axios/lib/helpers/validator.js +112 -0
  133. package/node_modules/axios/lib/platform/browser/classes/Blob.js +3 -0
  134. package/node_modules/axios/lib/platform/browser/classes/FormData.js +3 -0
  135. package/node_modules/axios/lib/platform/browser/classes/URLSearchParams.js +4 -0
  136. package/node_modules/axios/lib/platform/browser/index.js +13 -0
  137. package/node_modules/axios/lib/platform/common/utils.js +52 -0
  138. package/node_modules/axios/lib/platform/index.js +7 -0
  139. package/node_modules/axios/lib/platform/node/classes/Buffer.js +11 -0
  140. package/node_modules/axios/lib/platform/node/classes/FormData.js +3 -0
  141. package/node_modules/axios/lib/platform/node/classes/URLSearchParams.js +4 -0
  142. package/node_modules/axios/lib/platform/node/index.js +37 -0
  143. package/node_modules/axios/lib/utils.js +1201 -0
  144. package/node_modules/axios/node_modules/agent-base/README.md +145 -0
  145. package/node_modules/axios/node_modules/agent-base/dist/src/index.d.ts +78 -0
  146. package/node_modules/axios/node_modules/agent-base/dist/src/index.js +203 -0
  147. package/node_modules/axios/node_modules/agent-base/dist/src/index.js.map +1 -0
  148. package/node_modules/axios/node_modules/agent-base/dist/src/promisify.d.ts +4 -0
  149. package/node_modules/axios/node_modules/agent-base/dist/src/promisify.js +18 -0
  150. package/node_modules/axios/node_modules/agent-base/dist/src/promisify.js.map +1 -0
  151. package/node_modules/axios/node_modules/agent-base/package.json +64 -0
  152. package/node_modules/axios/node_modules/agent-base/src/index.ts +345 -0
  153. package/node_modules/axios/node_modules/agent-base/src/promisify.ts +33 -0
  154. package/node_modules/axios/node_modules/https-proxy-agent/README.md +137 -0
  155. package/node_modules/axios/node_modules/https-proxy-agent/dist/agent.d.ts +30 -0
  156. package/node_modules/axios/node_modules/https-proxy-agent/dist/agent.js +177 -0
  157. package/node_modules/axios/node_modules/https-proxy-agent/dist/agent.js.map +1 -0
  158. package/node_modules/axios/node_modules/https-proxy-agent/dist/index.d.ts +23 -0
  159. package/node_modules/axios/node_modules/https-proxy-agent/dist/index.js +14 -0
  160. package/node_modules/axios/node_modules/https-proxy-agent/dist/index.js.map +1 -0
  161. package/node_modules/axios/node_modules/https-proxy-agent/dist/parse-proxy-response.d.ts +7 -0
  162. package/node_modules/axios/node_modules/https-proxy-agent/dist/parse-proxy-response.js +66 -0
  163. package/node_modules/axios/node_modules/https-proxy-agent/dist/parse-proxy-response.js.map +1 -0
  164. package/node_modules/axios/node_modules/https-proxy-agent/package.json +56 -0
  165. package/node_modules/axios/package.json +202 -0
  166. package/node_modules/call-bind-apply-helpers/.eslintrc +17 -0
  167. package/node_modules/call-bind-apply-helpers/.github/FUNDING.yml +12 -0
  168. package/node_modules/call-bind-apply-helpers/.nycrc +9 -0
  169. package/node_modules/call-bind-apply-helpers/CHANGELOG.md +30 -0
  170. package/node_modules/call-bind-apply-helpers/LICENSE +21 -0
  171. package/node_modules/call-bind-apply-helpers/README.md +62 -0
  172. package/node_modules/call-bind-apply-helpers/actualApply.d.ts +1 -0
  173. package/node_modules/call-bind-apply-helpers/actualApply.js +10 -0
  174. package/node_modules/call-bind-apply-helpers/applyBind.d.ts +19 -0
  175. package/node_modules/call-bind-apply-helpers/applyBind.js +10 -0
  176. package/node_modules/call-bind-apply-helpers/functionApply.d.ts +1 -0
  177. package/node_modules/call-bind-apply-helpers/functionApply.js +4 -0
  178. package/node_modules/call-bind-apply-helpers/functionCall.d.ts +1 -0
  179. package/node_modules/call-bind-apply-helpers/functionCall.js +4 -0
  180. package/node_modules/call-bind-apply-helpers/index.d.ts +64 -0
  181. package/node_modules/call-bind-apply-helpers/index.js +15 -0
  182. package/node_modules/call-bind-apply-helpers/package.json +85 -0
  183. package/node_modules/call-bind-apply-helpers/reflectApply.d.ts +3 -0
  184. package/node_modules/call-bind-apply-helpers/reflectApply.js +4 -0
  185. package/node_modules/call-bind-apply-helpers/test/index.js +63 -0
  186. package/node_modules/call-bind-apply-helpers/tsconfig.json +9 -0
  187. package/node_modules/combined-stream/License +19 -0
  188. package/node_modules/combined-stream/Readme.md +138 -0
  189. package/node_modules/combined-stream/lib/combined_stream.js +208 -0
  190. package/node_modules/combined-stream/package.json +25 -0
  191. package/node_modules/delayed-stream/.npmignore +1 -0
  192. package/node_modules/delayed-stream/License +19 -0
  193. package/node_modules/delayed-stream/Makefile +7 -0
  194. package/node_modules/delayed-stream/Readme.md +141 -0
  195. package/node_modules/delayed-stream/lib/delayed_stream.js +107 -0
  196. package/node_modules/delayed-stream/package.json +27 -0
  197. package/node_modules/dunder-proto/.eslintrc +5 -0
  198. package/node_modules/dunder-proto/.github/FUNDING.yml +12 -0
  199. package/node_modules/dunder-proto/.nycrc +13 -0
  200. package/node_modules/dunder-proto/CHANGELOG.md +24 -0
  201. package/node_modules/dunder-proto/LICENSE +21 -0
  202. package/node_modules/dunder-proto/README.md +54 -0
  203. package/node_modules/dunder-proto/get.d.ts +5 -0
  204. package/node_modules/dunder-proto/get.js +30 -0
  205. package/node_modules/dunder-proto/package.json +76 -0
  206. package/node_modules/dunder-proto/set.d.ts +5 -0
  207. package/node_modules/dunder-proto/set.js +35 -0
  208. package/node_modules/dunder-proto/test/get.js +34 -0
  209. package/node_modules/dunder-proto/test/index.js +4 -0
  210. package/node_modules/dunder-proto/test/set.js +50 -0
  211. package/node_modules/dunder-proto/tsconfig.json +9 -0
  212. package/node_modules/es-define-property/.eslintrc +13 -0
  213. package/node_modules/es-define-property/.github/FUNDING.yml +12 -0
  214. package/node_modules/es-define-property/.nycrc +9 -0
  215. package/node_modules/es-define-property/CHANGELOG.md +29 -0
  216. package/node_modules/es-define-property/LICENSE +21 -0
  217. package/node_modules/es-define-property/README.md +49 -0
  218. package/node_modules/es-define-property/index.d.ts +3 -0
  219. package/node_modules/es-define-property/index.js +14 -0
  220. package/node_modules/es-define-property/package.json +81 -0
  221. package/node_modules/es-define-property/test/index.js +56 -0
  222. package/node_modules/es-define-property/tsconfig.json +10 -0
  223. package/node_modules/es-errors/.eslintrc +5 -0
  224. package/node_modules/es-errors/.github/FUNDING.yml +12 -0
  225. package/node_modules/es-errors/CHANGELOG.md +40 -0
  226. package/node_modules/es-errors/LICENSE +21 -0
  227. package/node_modules/es-errors/README.md +55 -0
  228. package/node_modules/es-errors/eval.d.ts +3 -0
  229. package/node_modules/es-errors/eval.js +4 -0
  230. package/node_modules/es-errors/index.d.ts +3 -0
  231. package/node_modules/es-errors/index.js +4 -0
  232. package/node_modules/es-errors/package.json +80 -0
  233. package/node_modules/es-errors/range.d.ts +3 -0
  234. package/node_modules/es-errors/range.js +4 -0
  235. package/node_modules/es-errors/ref.d.ts +3 -0
  236. package/node_modules/es-errors/ref.js +4 -0
  237. package/node_modules/es-errors/syntax.d.ts +3 -0
  238. package/node_modules/es-errors/syntax.js +4 -0
  239. package/node_modules/es-errors/test/index.js +19 -0
  240. package/node_modules/es-errors/tsconfig.json +49 -0
  241. package/node_modules/es-errors/type.d.ts +3 -0
  242. package/node_modules/es-errors/type.js +4 -0
  243. package/node_modules/es-errors/uri.d.ts +3 -0
  244. package/node_modules/es-errors/uri.js +4 -0
  245. package/node_modules/es-object-atoms/.eslintrc +16 -0
  246. package/node_modules/es-object-atoms/.github/FUNDING.yml +12 -0
  247. package/node_modules/es-object-atoms/CHANGELOG.md +44 -0
  248. package/node_modules/es-object-atoms/LICENSE +21 -0
  249. package/node_modules/es-object-atoms/README.md +63 -0
  250. package/node_modules/es-object-atoms/RequireObjectCoercible.d.ts +3 -0
  251. package/node_modules/es-object-atoms/RequireObjectCoercible.js +11 -0
  252. package/node_modules/es-object-atoms/ToObject.d.ts +7 -0
  253. package/node_modules/es-object-atoms/ToObject.js +10 -0
  254. package/node_modules/es-object-atoms/index.d.ts +3 -0
  255. package/node_modules/es-object-atoms/index.js +4 -0
  256. package/node_modules/es-object-atoms/isObject.d.ts +3 -0
  257. package/node_modules/es-object-atoms/isObject.js +6 -0
  258. package/node_modules/es-object-atoms/package.json +79 -0
  259. package/node_modules/es-object-atoms/test/index.js +38 -0
  260. package/node_modules/es-object-atoms/tsconfig.json +7 -0
  261. package/node_modules/es-set-tostringtag/.eslintrc +13 -0
  262. package/node_modules/es-set-tostringtag/.nycrc +9 -0
  263. package/node_modules/es-set-tostringtag/CHANGELOG.md +67 -0
  264. package/node_modules/es-set-tostringtag/LICENSE +21 -0
  265. package/node_modules/es-set-tostringtag/README.md +53 -0
  266. package/node_modules/es-set-tostringtag/index.d.ts +10 -0
  267. package/node_modules/es-set-tostringtag/index.js +35 -0
  268. package/node_modules/es-set-tostringtag/package.json +78 -0
  269. package/node_modules/es-set-tostringtag/test/index.js +85 -0
  270. package/node_modules/es-set-tostringtag/tsconfig.json +9 -0
  271. package/node_modules/eventemitter3/LICENSE +21 -0
  272. package/node_modules/eventemitter3/README.md +85 -0
  273. package/node_modules/eventemitter3/dist/eventemitter3.esm.js +355 -0
  274. package/node_modules/eventemitter3/dist/eventemitter3.esm.min.js +1 -0
  275. package/node_modules/eventemitter3/dist/eventemitter3.esm.min.js.map +1 -0
  276. package/node_modules/eventemitter3/dist/eventemitter3.umd.js +363 -0
  277. package/node_modules/eventemitter3/dist/eventemitter3.umd.min.js +1 -0
  278. package/node_modules/eventemitter3/dist/eventemitter3.umd.min.js.map +1 -0
  279. package/node_modules/eventemitter3/index.d.ts +135 -0
  280. package/node_modules/eventemitter3/index.js +336 -0
  281. package/node_modules/eventemitter3/index.mjs +4 -0
  282. package/node_modules/eventemitter3/package.json +62 -0
  283. package/node_modules/follow-redirects/LICENSE +18 -0
  284. package/node_modules/follow-redirects/README.md +183 -0
  285. package/node_modules/follow-redirects/debug.js +15 -0
  286. package/node_modules/follow-redirects/http.js +1 -0
  287. package/node_modules/follow-redirects/https.js +1 -0
  288. package/node_modules/follow-redirects/index.js +714 -0
  289. package/node_modules/follow-redirects/package.json +58 -0
  290. package/node_modules/form-data/CHANGELOG.md +686 -0
  291. package/node_modules/form-data/License +19 -0
  292. package/node_modules/form-data/README.md +355 -0
  293. package/node_modules/form-data/index.d.ts +62 -0
  294. package/node_modules/form-data/lib/browser.js +4 -0
  295. package/node_modules/form-data/lib/form_data.js +506 -0
  296. package/node_modules/form-data/lib/populate.js +10 -0
  297. package/node_modules/form-data/package.json +82 -0
  298. package/node_modules/function-bind/.eslintrc +21 -0
  299. package/node_modules/function-bind/.github/FUNDING.yml +12 -0
  300. package/node_modules/function-bind/.github/SECURITY.md +3 -0
  301. package/node_modules/function-bind/.nycrc +13 -0
  302. package/node_modules/function-bind/CHANGELOG.md +136 -0
  303. package/node_modules/function-bind/LICENSE +20 -0
  304. package/node_modules/function-bind/README.md +46 -0
  305. package/node_modules/function-bind/implementation.js +84 -0
  306. package/node_modules/function-bind/index.js +5 -0
  307. package/node_modules/function-bind/package.json +87 -0
  308. package/node_modules/function-bind/test/.eslintrc +9 -0
  309. package/node_modules/function-bind/test/index.js +252 -0
  310. package/node_modules/get-intrinsic/.eslintrc +42 -0
  311. package/node_modules/get-intrinsic/.github/FUNDING.yml +12 -0
  312. package/node_modules/get-intrinsic/.nycrc +9 -0
  313. package/node_modules/get-intrinsic/CHANGELOG.md +186 -0
  314. package/node_modules/get-intrinsic/LICENSE +21 -0
  315. package/node_modules/get-intrinsic/README.md +71 -0
  316. package/node_modules/get-intrinsic/index.js +378 -0
  317. package/node_modules/get-intrinsic/package.json +97 -0
  318. package/node_modules/get-intrinsic/test/GetIntrinsic.js +274 -0
  319. package/node_modules/get-proto/.eslintrc +10 -0
  320. package/node_modules/get-proto/.github/FUNDING.yml +12 -0
  321. package/node_modules/get-proto/.nycrc +9 -0
  322. package/node_modules/get-proto/CHANGELOG.md +21 -0
  323. package/node_modules/get-proto/LICENSE +21 -0
  324. package/node_modules/get-proto/Object.getPrototypeOf.d.ts +5 -0
  325. package/node_modules/get-proto/Object.getPrototypeOf.js +6 -0
  326. package/node_modules/get-proto/README.md +50 -0
  327. package/node_modules/get-proto/Reflect.getPrototypeOf.d.ts +3 -0
  328. package/node_modules/get-proto/Reflect.getPrototypeOf.js +4 -0
  329. package/node_modules/get-proto/index.d.ts +5 -0
  330. package/node_modules/get-proto/index.js +27 -0
  331. package/node_modules/get-proto/package.json +81 -0
  332. package/node_modules/get-proto/test/index.js +68 -0
  333. package/node_modules/get-proto/tsconfig.json +9 -0
  334. package/node_modules/gopd/.eslintrc +16 -0
  335. package/node_modules/gopd/.github/FUNDING.yml +12 -0
  336. package/node_modules/gopd/CHANGELOG.md +45 -0
  337. package/node_modules/gopd/LICENSE +21 -0
  338. package/node_modules/gopd/README.md +40 -0
  339. package/node_modules/gopd/gOPD.d.ts +1 -0
  340. package/node_modules/gopd/gOPD.js +4 -0
  341. package/node_modules/gopd/index.d.ts +5 -0
  342. package/node_modules/gopd/index.js +15 -0
  343. package/node_modules/gopd/package.json +77 -0
  344. package/node_modules/gopd/test/index.js +36 -0
  345. package/node_modules/gopd/tsconfig.json +9 -0
  346. package/node_modules/has-symbols/.eslintrc +11 -0
  347. package/node_modules/has-symbols/.github/FUNDING.yml +12 -0
  348. package/node_modules/has-symbols/.nycrc +9 -0
  349. package/node_modules/has-symbols/CHANGELOG.md +91 -0
  350. package/node_modules/has-symbols/LICENSE +21 -0
  351. package/node_modules/has-symbols/README.md +46 -0
  352. package/node_modules/has-symbols/index.d.ts +3 -0
  353. package/node_modules/has-symbols/index.js +14 -0
  354. package/node_modules/has-symbols/package.json +111 -0
  355. package/node_modules/has-symbols/shams.d.ts +3 -0
  356. package/node_modules/has-symbols/shams.js +45 -0
  357. package/node_modules/has-symbols/test/index.js +22 -0
  358. package/node_modules/has-symbols/test/shams/core-js.js +29 -0
  359. package/node_modules/has-symbols/test/shams/get-own-property-symbols.js +29 -0
  360. package/node_modules/has-symbols/test/tests.js +58 -0
  361. package/node_modules/has-symbols/tsconfig.json +10 -0
  362. package/node_modules/has-tostringtag/.eslintrc +5 -0
  363. package/node_modules/has-tostringtag/.github/FUNDING.yml +12 -0
  364. package/node_modules/has-tostringtag/.nycrc +13 -0
  365. package/node_modules/has-tostringtag/CHANGELOG.md +42 -0
  366. package/node_modules/has-tostringtag/LICENSE +21 -0
  367. package/node_modules/has-tostringtag/README.md +46 -0
  368. package/node_modules/has-tostringtag/index.d.ts +3 -0
  369. package/node_modules/has-tostringtag/index.js +8 -0
  370. package/node_modules/has-tostringtag/package.json +108 -0
  371. package/node_modules/has-tostringtag/shams.d.ts +3 -0
  372. package/node_modules/has-tostringtag/shams.js +8 -0
  373. package/node_modules/has-tostringtag/test/index.js +21 -0
  374. package/node_modules/has-tostringtag/test/shams/core-js.js +31 -0
  375. package/node_modules/has-tostringtag/test/shams/get-own-property-symbols.js +30 -0
  376. package/node_modules/has-tostringtag/test/tests.js +15 -0
  377. package/node_modules/has-tostringtag/tsconfig.json +49 -0
  378. package/node_modules/hasown/.github/FUNDING.yml +12 -0
  379. package/node_modules/hasown/.nycrc +13 -0
  380. package/node_modules/hasown/CHANGELOG.md +58 -0
  381. package/node_modules/hasown/LICENSE +21 -0
  382. package/node_modules/hasown/README.md +40 -0
  383. package/node_modules/hasown/eslint.config.mjs +6 -0
  384. package/node_modules/hasown/index.d.ts +3 -0
  385. package/node_modules/hasown/index.js +8 -0
  386. package/node_modules/hasown/package.json +91 -0
  387. package/node_modules/hasown/tsconfig.json +6 -0
  388. package/node_modules/math-intrinsics/.eslintrc +16 -0
  389. package/node_modules/math-intrinsics/.github/FUNDING.yml +12 -0
  390. package/node_modules/math-intrinsics/CHANGELOG.md +24 -0
  391. package/node_modules/math-intrinsics/LICENSE +21 -0
  392. package/node_modules/math-intrinsics/README.md +50 -0
  393. package/node_modules/math-intrinsics/abs.d.ts +1 -0
  394. package/node_modules/math-intrinsics/abs.js +4 -0
  395. package/node_modules/math-intrinsics/constants/maxArrayLength.d.ts +3 -0
  396. package/node_modules/math-intrinsics/constants/maxArrayLength.js +4 -0
  397. package/node_modules/math-intrinsics/constants/maxSafeInteger.d.ts +3 -0
  398. package/node_modules/math-intrinsics/constants/maxSafeInteger.js +5 -0
  399. package/node_modules/math-intrinsics/constants/maxValue.d.ts +3 -0
  400. package/node_modules/math-intrinsics/constants/maxValue.js +5 -0
  401. package/node_modules/math-intrinsics/floor.d.ts +1 -0
  402. package/node_modules/math-intrinsics/floor.js +4 -0
  403. package/node_modules/math-intrinsics/isFinite.d.ts +3 -0
  404. package/node_modules/math-intrinsics/isFinite.js +12 -0
  405. package/node_modules/math-intrinsics/isInteger.d.ts +3 -0
  406. package/node_modules/math-intrinsics/isInteger.js +16 -0
  407. package/node_modules/math-intrinsics/isNaN.d.ts +1 -0
  408. package/node_modules/math-intrinsics/isNaN.js +6 -0
  409. package/node_modules/math-intrinsics/isNegativeZero.d.ts +3 -0
  410. package/node_modules/math-intrinsics/isNegativeZero.js +6 -0
  411. package/node_modules/math-intrinsics/max.d.ts +1 -0
  412. package/node_modules/math-intrinsics/max.js +4 -0
  413. package/node_modules/math-intrinsics/min.d.ts +1 -0
  414. package/node_modules/math-intrinsics/min.js +4 -0
  415. package/node_modules/math-intrinsics/mod.d.ts +3 -0
  416. package/node_modules/math-intrinsics/mod.js +9 -0
  417. package/node_modules/math-intrinsics/package.json +86 -0
  418. package/node_modules/math-intrinsics/pow.d.ts +1 -0
  419. package/node_modules/math-intrinsics/pow.js +4 -0
  420. package/node_modules/math-intrinsics/round.d.ts +1 -0
  421. package/node_modules/math-intrinsics/round.js +4 -0
  422. package/node_modules/math-intrinsics/sign.d.ts +3 -0
  423. package/node_modules/math-intrinsics/sign.js +11 -0
  424. package/node_modules/math-intrinsics/test/index.js +192 -0
  425. package/node_modules/math-intrinsics/tsconfig.json +3 -0
  426. package/node_modules/mime-db/HISTORY.md +507 -0
  427. package/node_modules/mime-db/LICENSE +23 -0
  428. package/node_modules/mime-db/README.md +100 -0
  429. package/node_modules/mime-db/db.json +8519 -0
  430. package/node_modules/mime-db/index.js +12 -0
  431. package/node_modules/mime-db/package.json +60 -0
  432. package/node_modules/mime-types/HISTORY.md +397 -0
  433. package/node_modules/mime-types/LICENSE +23 -0
  434. package/node_modules/mime-types/README.md +113 -0
  435. package/node_modules/mime-types/index.js +188 -0
  436. package/node_modules/mime-types/package.json +44 -0
  437. package/node_modules/proxy-from-env/LICENSE +20 -0
  438. package/node_modules/proxy-from-env/README.md +163 -0
  439. package/node_modules/proxy-from-env/index.cjs +105 -0
  440. package/node_modules/proxy-from-env/index.js +103 -0
  441. package/node_modules/proxy-from-env/package.json +43 -0
  442. package/package.json +2 -1
  443. package/dist/web/assets/Assistant-DSQAdSiW.js +0 -1
  444. package/dist/web/assets/index-BXpTaAUz.js +0 -66
@@ -0,0 +1,1563 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var eventemitter3 = require('eventemitter3');
6
+ var crypto = require('crypto');
7
+ var axios = require('axios');
8
+ var WebSocket = require('ws');
9
+ var crypto$1 = require('node:crypto');
10
+
11
+ /**
12
+ * 通用基础类型定义
13
+ */
14
+ /**
15
+ * 认证失败重试次数用尽错误
16
+ *
17
+ * 当 WebSocket 认证连续失败次数达到 maxAuthFailureAttempts 时抛出。
18
+ * 通常表示 botId/secret 配置错误,重试无法恢复。
19
+ */
20
+ class WSAuthFailureError extends Error {
21
+ constructor(maxAttempts) {
22
+ super(`Max auth failure attempts exceeded (${maxAttempts})`);
23
+ this.code = 'WS_AUTH_FAILURE_EXHAUSTED';
24
+ this.name = 'WSAuthFailureError';
25
+ }
26
+ }
27
+ /**
28
+ * 连接断开重连次数用尽错误
29
+ *
30
+ * 当 WebSocket 连接断开后重连次数达到 maxReconnectAttempts 时抛出。
31
+ * 通常表示网络或服务端持续不可用。
32
+ */
33
+ class WSReconnectExhaustedError extends Error {
34
+ constructor(maxAttempts) {
35
+ super(`Max reconnect attempts exceeded (${maxAttempts})`);
36
+ this.code = 'WS_RECONNECT_EXHAUSTED';
37
+ this.name = 'WSReconnectExhaustedError';
38
+ }
39
+ }
40
+
41
+ /**
42
+ * 消息相关类型定义
43
+ * 按照企业微信智能机器人接收消息协议定义
44
+ */
45
+ /** 消息类型枚举 */
46
+ exports.MessageType = void 0;
47
+ (function (MessageType) {
48
+ /** 文本消息 */
49
+ MessageType["Text"] = "text";
50
+ /** 图片消息 */
51
+ MessageType["Image"] = "image";
52
+ /** 图文混排消息 */
53
+ MessageType["Mixed"] = "mixed";
54
+ /** 语音消息 */
55
+ MessageType["Voice"] = "voice";
56
+ /** 文件消息 */
57
+ MessageType["File"] = "file";
58
+ /** 视频消息 */
59
+ MessageType["Video"] = "video";
60
+ })(exports.MessageType || (exports.MessageType = {}));
61
+
62
+ /**
63
+ * API 相关类型定义
64
+ */
65
+ /** WebSocket 命令类型常量 */
66
+ const WsCmd = {
67
+ // ========== 开发者 → 企业微信 ==========
68
+ /** 认证订阅 */
69
+ SUBSCRIBE: 'aibot_subscribe',
70
+ /** 心跳 */
71
+ HEARTBEAT: 'ping',
72
+ /** 回复消息 */
73
+ RESPONSE: 'aibot_respond_msg',
74
+ /** 回复欢迎语 */
75
+ RESPONSE_WELCOME: 'aibot_respond_welcome_msg',
76
+ /** 更新模板卡片 */
77
+ RESPONSE_UPDATE: 'aibot_respond_update_msg',
78
+ /** 主动发送消息 */
79
+ SEND_MSG: 'aibot_send_msg',
80
+ /** 上传临时素材 - 初始化 */
81
+ UPLOAD_MEDIA_INIT: 'aibot_upload_media_init',
82
+ /** 上传临时素材 - 分片上传 */
83
+ UPLOAD_MEDIA_CHUNK: 'aibot_upload_media_chunk',
84
+ /** 上传临时素材 - 完成上传 */
85
+ UPLOAD_MEDIA_FINISH: 'aibot_upload_media_finish',
86
+ // ========== 企业微信 → 开发者 ==========
87
+ /** 消息推送回调 */
88
+ CALLBACK: 'aibot_msg_callback',
89
+ /** 事件推送回调 */
90
+ EVENT_CALLBACK: 'aibot_event_callback',
91
+ };
92
+ // ========== 模板卡片结构体及子结构体 ==========
93
+ /** 卡片类型枚举 */
94
+ exports.TemplateCardType = void 0;
95
+ (function (TemplateCardType) {
96
+ /** 文本通知模版卡片 */
97
+ TemplateCardType["TextNotice"] = "text_notice";
98
+ /** 图文展示模版卡片 */
99
+ TemplateCardType["NewsNotice"] = "news_notice";
100
+ /** 按钮交互模版卡片 */
101
+ TemplateCardType["ButtonInteraction"] = "button_interaction";
102
+ /** 投票选择模版卡片 */
103
+ TemplateCardType["VoteInteraction"] = "vote_interaction";
104
+ /** 多项选择模版卡片 */
105
+ TemplateCardType["MultipleInteraction"] = "multiple_interaction";
106
+ })(exports.TemplateCardType || (exports.TemplateCardType = {}));
107
+
108
+ /**
109
+ * 事件相关类型定义
110
+ */
111
+ /** 事件类型枚举 */
112
+ exports.EventType = void 0;
113
+ (function (EventType) {
114
+ /** 进入会话事件:用户当天首次进入机器人单聊会话 */
115
+ EventType["EnterChat"] = "enter_chat";
116
+ /** 模板卡片事件:用户点击模板卡片按钮 */
117
+ EventType["TemplateCardEvent"] = "template_card_event";
118
+ /** 用户反馈事件:用户对机器人回复进行反馈 */
119
+ EventType["FeedbackEvent"] = "feedback_event";
120
+ /** 连接断开事件:有新连接建立时,服务端向旧连接发送此事件并主动断开 */
121
+ EventType["Disconnected"] = "disconnected_event";
122
+ })(exports.EventType || (exports.EventType = {}));
123
+
124
+ /**
125
+ * 企业微信 API 客户端
126
+ * 仅负责文件下载等 HTTP 辅助功能,消息收发均走 WebSocket 通道
127
+ */
128
+ class WeComApiClient {
129
+ constructor(logger, timeout = 10000) {
130
+ this.logger = logger;
131
+ this.httpClient = axios.create({
132
+ timeout,
133
+ headers: {
134
+ 'Content-Type': 'application/json',
135
+ },
136
+ });
137
+ }
138
+ /**
139
+ * 下载文件(返回原始 Buffer 及文件名)
140
+ */
141
+ async downloadFileRaw(url) {
142
+ this.logger.info('Downloading file...');
143
+ try {
144
+ const response = await this.httpClient.get(url, {
145
+ responseType: 'arraybuffer',
146
+ });
147
+ // 从 Content-Disposition 头中解析文件名
148
+ const contentDisposition = response.headers['content-disposition'];
149
+ let filename;
150
+ if (contentDisposition) {
151
+ // 优先匹配 filename*=UTF-8''xxx 格式(RFC 5987)
152
+ const utf8Match = contentDisposition.match(/filename\*=UTF-8''([^;\s]+)/i);
153
+ if (utf8Match) {
154
+ filename = decodeURIComponent(utf8Match[1]);
155
+ }
156
+ else {
157
+ // 匹配 filename="xxx" 或 filename=xxx 格式
158
+ const match = contentDisposition.match(/filename="?([^";\s]+)"?/i);
159
+ if (match) {
160
+ filename = decodeURIComponent(match[1]);
161
+ }
162
+ }
163
+ }
164
+ this.logger.info('File downloaded successfully');
165
+ return { buffer: Buffer.from(response.data), filename };
166
+ }
167
+ catch (error) {
168
+ this.logger.error('File download failed:', error.message);
169
+ throw error;
170
+ }
171
+ }
172
+ }
173
+
174
+ /**
175
+ * 通用工具方法
176
+ */
177
+ /**
178
+ * 生成随机字符串
179
+ *
180
+ * @param length - 随机字符串长度,默认 8
181
+ * @returns 随机字符串
182
+ */
183
+ function generateRandomString(length = 8) {
184
+ return crypto.randomBytes(Math.ceil(length / 2)).toString('hex').substring(0, length);
185
+ }
186
+ /**
187
+ * 生成唯一请求 ID
188
+ *
189
+ * 格式:`{prefix}_{timestamp}_{random}`
190
+ *
191
+ * @param prefix - 前缀,通常为 cmd 名称
192
+ * @returns 唯一请求 ID
193
+ */
194
+ function generateReqId(prefix) {
195
+ const timestamp = Date.now();
196
+ const random = generateRandomString();
197
+ return `${prefix}_${timestamp}_${random}`;
198
+ }
199
+
200
+ /** SDK 内置默认 WebSocket 连接地址 */
201
+ const DEFAULT_WS_URL = 'wss://openws.work.weixin.qq.com';
202
+ class WsConnectionManager {
203
+ constructor(logger, heartbeatInterval = 30000, reconnectBaseDelay = 1000, maxReconnectAttempts = 10, wsUrl, wsOptions, maxReplyQueueSize, maxAuthFailureAttempts) {
204
+ this.ws = null;
205
+ this.heartbeatTimer = null;
206
+ this.reconnectAttempts = 0;
207
+ this.authFailureAttempts = 0;
208
+ this.isManualClose = false;
209
+ /** 标记最近一次连接关闭是否因认证失败触发(用于 scheduleReconnect 区分重连类型) */
210
+ this.lastCloseWasAuthFailure = false;
211
+ /** 认证凭证 */
212
+ this.botId = '';
213
+ this.botSecret = '';
214
+ /** 额外的认证参数(如 scene、plug_version 等),会展开到认证帧 body 中 */
215
+ this.extraAuthParams = {};
216
+ /** Number of consecutive missed heartbeat acks (pong) */
217
+ this.missedPongCount = 0;
218
+ /** Max missed pong before treating connection as dead */
219
+ this.maxMissedPong = 2;
220
+ /** Base delay (ms) for exponential back-off reconnection */
221
+ this.reconnectBaseDelay = 1000;
222
+ /** Upper cap (ms) for reconnect delay */
223
+ this.reconnectMaxDelay = 30000;
224
+ /** 重连定时器引用,用于在 disconnect/connect 时取消挂起的重连 */
225
+ this.reconnectTimer = null;
226
+ /** 按 req_id 分组的回复发送队列,保证同一 req_id 的消息串行发送 */
227
+ this.replyQueues = new Map();
228
+ /** 正在等待回执的 req_id 集合,value 包含超时定时器、resolve/reject 和序列号 */
229
+ this.pendingAcks = new Map();
230
+ /** 自增序列号,用于区分同一 reqId 的不同 pending,防止超时与 ack 竞态 */
231
+ this.pendingAckSeq = 0;
232
+ /** 回执超时时间(毫秒) */
233
+ this.replyAckTimeout = 5000;
234
+ /** 单个 req_id 的回复队列最大长度,超过后新消息将被拒绝 */
235
+ this.maxReplyQueueSize = 500;
236
+ /** 连接建立回调(WebSocket open 事件,认证尚未完成) */
237
+ this.onConnected = null;
238
+ /** 认证成功回调 */
239
+ this.onAuthenticated = null;
240
+ /** 连接断开回调 */
241
+ this.onDisconnected = null;
242
+ /** 收到消息回调 */
243
+ this.onMessage = null;
244
+ /** 重连回调 */
245
+ this.onReconnecting = null;
246
+ /** 错误回调 */
247
+ this.onError = null;
248
+ /** 服务端主动断开回调(新连接建立导致旧连接被断开) */
249
+ this.onServerDisconnect = null;
250
+ this.logger = logger;
251
+ this.heartbeatInterval = heartbeatInterval;
252
+ this.reconnectBaseDelay = reconnectBaseDelay;
253
+ this.maxReconnectAttempts = maxReconnectAttempts;
254
+ this.maxAuthFailureAttempts = maxAuthFailureAttempts ?? 5;
255
+ this.wsUrl = wsUrl || DEFAULT_WS_URL;
256
+ this.wsOptions = wsOptions || {};
257
+ if (maxReplyQueueSize !== undefined) {
258
+ this.maxReplyQueueSize = maxReplyQueueSize;
259
+ }
260
+ }
261
+ /**
262
+ * 设置认证凭证
263
+ */
264
+ setCredentials(botId, botSecret, extraAuthParams) {
265
+ this.botId = botId;
266
+ this.botSecret = botSecret;
267
+ this.extraAuthParams = extraAuthParams || {};
268
+ }
269
+ /**
270
+ * 建立 WebSocket 连接(使用 SDK 内置默认地址)
271
+ */
272
+ connect() {
273
+ this.isManualClose = false;
274
+ // 取消挂起的重连定时器,防止与当前 connect 竞态
275
+ if (this.reconnectTimer) {
276
+ clearTimeout(this.reconnectTimer);
277
+ this.reconnectTimer = null;
278
+ }
279
+ // 清理可能未完全关闭的旧连接
280
+ if (this.ws) {
281
+ this.ws.removeAllListeners();
282
+ this.ws.terminate();
283
+ this.ws = null;
284
+ }
285
+ this.logger.info(`Connecting to WebSocket: ${this.wsUrl}...`);
286
+ try {
287
+ this.ws = new WebSocket(this.wsUrl, this.wsOptions);
288
+ this.setupEventHandlers();
289
+ }
290
+ catch (error) {
291
+ this.logger.error('Failed to create WebSocket connection:', error.message);
292
+ this.onError?.(error);
293
+ this.scheduleReconnect();
294
+ }
295
+ }
296
+ /**
297
+ * 设置 WebSocket 事件处理器
298
+ */
299
+ setupEventHandlers() {
300
+ if (!this.ws)
301
+ return;
302
+ this.ws.on('open', () => {
303
+ this.logger.info('WebSocket connection established, sending auth...');
304
+ // 注意:不在 open 时重置 reconnectAttempts,因为 TCP 连接成功不代表认证会成功。
305
+ // 只有在认证成功后才重置所有计数器(见 handleFrame 中的认证响应处理)。
306
+ this.missedPongCount = 0;
307
+ this.lastCloseWasAuthFailure = false;
308
+ // 连接建立后立即发送认证帧
309
+ this.sendAuth();
310
+ this.onConnected?.();
311
+ });
312
+ this.ws.on('message', (data) => {
313
+ try {
314
+ const raw = data.toString().replace(/[\x00-\x08\x0B-\x0D\x0E-\x1F]/g, "");
315
+ const frame = JSON.parse(raw);
316
+ this.handleFrame(frame);
317
+ }
318
+ catch (error) {
319
+ this.logger.error('Failed to parse WebSocket message:', error.message);
320
+ }
321
+ });
322
+ this.ws.on('close', (code, reason) => {
323
+ const reasonStr = reason.toString() || `code: ${code}`;
324
+ this.logger.warn(`WebSocket connection closed: ${reasonStr}`);
325
+ this.stopHeartbeat();
326
+ this.clearPendingMessages(`WebSocket connection closed (${reasonStr})`);
327
+ this.onDisconnected?.(reasonStr);
328
+ // 释放旧 WebSocket 实例引用,便于 GC 回收
329
+ this.ws = null;
330
+ if (!this.isManualClose) {
331
+ this.scheduleReconnect();
332
+ }
333
+ });
334
+ this.ws.on('error', (error) => {
335
+ this.logger.error('WebSocket error:', error.message);
336
+ this.onError?.(error);
337
+ });
338
+ this.ws.on('ping', () => {
339
+ this.ws?.pong();
340
+ });
341
+ }
342
+ /**
343
+ * 发送认证帧
344
+ *
345
+ * 格式:{ cmd: "aibot_subscribe", headers: { req_id }, body: { secret, bot_id } }
346
+ */
347
+ sendAuth() {
348
+ try {
349
+ this.send({
350
+ cmd: WsCmd.SUBSCRIBE,
351
+ headers: { req_id: generateReqId(WsCmd.SUBSCRIBE) },
352
+ body: {
353
+ bot_id: this.botId,
354
+ secret: this.botSecret,
355
+ ...this.extraAuthParams,
356
+ },
357
+ });
358
+ this.logger.info('Auth frame sent');
359
+ }
360
+ catch (error) {
361
+ this.logger.error('Failed to send auth frame:', error.message);
362
+ }
363
+ }
364
+ /**
365
+ * 处理收到的帧数据
366
+ *
367
+ * 接收帧结构:
368
+ * - 消息推送:{ cmd: "aibot_msg_callback", headers: { req_id }, body: { ... } }
369
+ * - 认证/心跳响应:{ headers: { req_id }, errcode: 0, errmsg: "ok" }
370
+ */
371
+ handleFrame(frame) {
372
+ const cmd = frame.cmd || '';
373
+ const reqId = frame.headers?.req_id || '';
374
+ // 消息推送:cmd 为 "aibot_msg_callback"
375
+ if (frame.cmd === WsCmd.CALLBACK) {
376
+ this.logger.debug(`[server -> plugin] cmd=${cmd}, reqId=${reqId}, body=${JSON.stringify(frame.body)}`);
377
+ this.onMessage?.(frame);
378
+ return;
379
+ }
380
+ // 事件推送:cmd 为 "aibot_event_callback"
381
+ if (frame.cmd === WsCmd.EVENT_CALLBACK) {
382
+ this.logger.debug(`[server -> plugin] cmd=${cmd}, reqId=${reqId}, body=${JSON.stringify(frame.body)}`);
383
+ // 检测 disconnected_event:有新连接建立,服务端通知旧连接即将被断开
384
+ if (frame.body?.event?.eventtype === 'disconnected_event') {
385
+ this.logger.warn('Received disconnected_event: a new connection has been established, this connection will be closed by server');
386
+ // 先分发事件给上层(让用户可以监听 event.disconnected_event)
387
+ this.onMessage?.(frame);
388
+ // 停止心跳、清理待处理消息
389
+ this.stopHeartbeat();
390
+ this.clearPendingMessages('Server disconnected due to new connection');
391
+ // 标记为非手动断开但阻止自动重连(服务端正常行为,重连也会被再次断开)
392
+ this.isManualClose = true;
393
+ // 通知上层服务端主动断开
394
+ this.onServerDisconnect?.('New connection established, server disconnected this connection');
395
+ // 主动关闭 socket,避免连接处于僵尸状态
396
+ if (this.ws) {
397
+ this.ws.removeAllListeners();
398
+ this.ws.terminate();
399
+ this.ws = null;
400
+ }
401
+ return;
402
+ }
403
+ this.onMessage?.(frame);
404
+ return;
405
+ }
406
+ // 无 cmd 的帧:认证响应、心跳响应或回复消息回执,通过 req_id 前缀区分类型,再判断 errcode
407
+ const actualReqId = frame.headers?.req_id || '';
408
+ // 认证响应(优先于 pendingAcks 检查,避免误判)
409
+ if (actualReqId.startsWith(WsCmd.SUBSCRIBE)) {
410
+ if (frame.errcode !== 0) {
411
+ this.logger.error(`Authentication failed: errcode=${frame.errcode}, errmsg=${frame.errmsg}`);
412
+ this.onError?.(new Error(`Authentication failed: ${frame.errmsg} (code: ${frame.errcode})`));
413
+ // 标记为认证失败,close 事件中 scheduleReconnect 会据此使用 authFailureAttempts 计数器
414
+ this.lastCloseWasAuthFailure = true;
415
+ // 认证失败,主动关闭连接,触发 close 事件进而执行 scheduleReconnect 重连逻辑
416
+ if (this.ws) {
417
+ this.ws.terminate();
418
+ }
419
+ return;
420
+ }
421
+ this.logger.info('Authentication successful');
422
+ // 认证成功:重置所有重连计数器
423
+ this.reconnectAttempts = 0;
424
+ this.authFailureAttempts = 0;
425
+ this.startHeartbeat();
426
+ this.onAuthenticated?.();
427
+ return;
428
+ }
429
+ // 心跳响应(优先于 pendingAcks 检查,避免误判)
430
+ if (actualReqId.startsWith(WsCmd.HEARTBEAT)) {
431
+ if (frame.errcode !== 0) {
432
+ this.logger.warn(`Heartbeat ack error: errcode=${frame.errcode}, errmsg=${frame.errmsg}`);
433
+ return;
434
+ }
435
+ this.missedPongCount = 0;
436
+ return;
437
+ }
438
+ // 检查是否是回复消息的回执(req_id 存在于 pendingAcks 中)
439
+ if (this.pendingAcks.has(actualReqId)) {
440
+ this.handleReplyAck(actualReqId, frame);
441
+ return;
442
+ }
443
+ // 未知帧类型 — 只记录警告,不传给 onMessage(避免 body=undefined 导致下游误处理)
444
+ this.logger.warn('Received unknown frame (ignored):', JSON.stringify(frame));
445
+ }
446
+ /**
447
+ * 启动心跳定时器
448
+ */
449
+ startHeartbeat() {
450
+ this.stopHeartbeat();
451
+ this.heartbeatTimer = setInterval(() => {
452
+ this.sendHeartbeat();
453
+ }, this.heartbeatInterval);
454
+ this.logger.debug(`Heartbeat timer started, interval: ${this.heartbeatInterval}ms`);
455
+ }
456
+ /**
457
+ * 停止心跳定时器
458
+ */
459
+ stopHeartbeat() {
460
+ if (this.heartbeatTimer) {
461
+ clearInterval(this.heartbeatTimer);
462
+ this.heartbeatTimer = null;
463
+ this.logger.debug('Heartbeat timer stopped');
464
+ }
465
+ }
466
+ /**
467
+ * 发送心跳
468
+ * If consecutive missed pong count reaches the threshold, treat the
469
+ * connection as dead and trigger reconnection.
470
+ *
471
+ * 格式:{ cmd: "ping", headers: { req_id } }
472
+ */
473
+ sendHeartbeat() {
474
+ // Check missed pong BEFORE sending the next heartbeat
475
+ if (this.missedPongCount >= this.maxMissedPong) {
476
+ this.logger.warn(`No heartbeat ack received for ${this.missedPongCount} consecutive pings, connection considered dead`);
477
+ this.stopHeartbeat();
478
+ // Force-close the underlying socket so the 'close' handler fires
479
+ if (this.ws) {
480
+ this.ws.terminate();
481
+ }
482
+ return;
483
+ }
484
+ this.missedPongCount++;
485
+ try {
486
+ this.send({
487
+ cmd: WsCmd.HEARTBEAT,
488
+ headers: { req_id: generateReqId(WsCmd.HEARTBEAT) },
489
+ });
490
+ }
491
+ catch (error) {
492
+ this.logger.error('Failed to send heartbeat:', error.message);
493
+ }
494
+ }
495
+ /**
496
+ * 安排重连
497
+ *
498
+ * 区分两种重连场景,使用独立的计数器和最大重试次数:
499
+ * - 认证失败(lastCloseWasAuthFailure=true):使用 authFailureAttempts / maxAuthFailureAttempts
500
+ * - 连接断开(lastCloseWasAuthFailure=false):使用 reconnectAttempts / maxReconnectAttempts
501
+ *
502
+ * disconnected_event(被踢下线)不会触发重连,因为 isManualClose 已被设为 true。
503
+ */
504
+ scheduleReconnect() {
505
+ if (this.lastCloseWasAuthFailure) {
506
+ // 认证失败场景
507
+ if (this.maxAuthFailureAttempts !== -1 && this.authFailureAttempts >= this.maxAuthFailureAttempts) {
508
+ this.logger.error(`Max auth failure attempts reached (${this.maxAuthFailureAttempts}), giving up`);
509
+ this.onError?.(new WSAuthFailureError(this.maxAuthFailureAttempts));
510
+ return;
511
+ }
512
+ this.authFailureAttempts++;
513
+ const delay = Math.min(this.reconnectBaseDelay * Math.pow(2, this.authFailureAttempts - 1), this.reconnectMaxDelay);
514
+ this.logger.info(`Auth failed, reconnecting in ${delay}ms (auth attempt ${this.authFailureAttempts}/${this.maxAuthFailureAttempts})...`);
515
+ this.onReconnecting?.(this.authFailureAttempts);
516
+ this.reconnectTimer = setTimeout(() => {
517
+ this.reconnectTimer = null;
518
+ if (this.isManualClose)
519
+ return;
520
+ this.connect();
521
+ }, delay);
522
+ }
523
+ else {
524
+ // 连接断开场景(网络异常、心跳超时等)
525
+ if (this.maxReconnectAttempts !== -1 && this.reconnectAttempts >= this.maxReconnectAttempts) {
526
+ this.logger.error(`Max reconnect attempts reached (${this.maxReconnectAttempts}), giving up`);
527
+ this.onError?.(new WSReconnectExhaustedError(this.maxReconnectAttempts));
528
+ return;
529
+ }
530
+ this.reconnectAttempts++;
531
+ const delay = Math.min(this.reconnectBaseDelay * Math.pow(2, this.reconnectAttempts - 1), this.reconnectMaxDelay);
532
+ this.logger.info(`Connection lost, reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
533
+ this.onReconnecting?.(this.reconnectAttempts);
534
+ this.reconnectTimer = setTimeout(() => {
535
+ this.reconnectTimer = null;
536
+ if (this.isManualClose)
537
+ return;
538
+ this.connect();
539
+ }, delay);
540
+ }
541
+ }
542
+ /**
543
+ * 发送数据帧
544
+ *
545
+ * 统一格式:{ cmd, headers: { req_id }, body }
546
+ */
547
+ send(frame) {
548
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
549
+ const data = JSON.stringify(frame);
550
+ this.ws.send(data);
551
+ }
552
+ else {
553
+ throw new Error('WebSocket not connected, unable to send data');
554
+ }
555
+ }
556
+ /**
557
+ * 通过 WebSocket 通道发送回复消息(串行队列版本)
558
+ *
559
+ * 同一个 req_id 的消息会被放入队列中串行发送:
560
+ * 发送一条后等待服务端回执,收到回执或超时后才发送下一条。
561
+ *
562
+ * 格式:{ cmd: "aibot_respond_msg", headers: { req_id }, body: { ... } }
563
+ *
564
+ * @param reqId - 透传回调中的 req_id
565
+ * @param body - 回复消息体(如 StreamReplyBody)
566
+ * @param cmd - 发送的命令类型,默认 WsCmd.RESPONSE
567
+ * @returns Promise,收到回执时 resolve(回执帧),超时或errcode非0时 reject(Error)
568
+ */
569
+ sendReply(reqId, body, cmd = WsCmd.RESPONSE) {
570
+ // // 日志中截断 base64_data,避免分片上传时日志过大
571
+ // const logBody = body?.base64_data
572
+ // ? { ...body, base64_data: `<${body.base64_data.length} chars>` }
573
+ // : body;
574
+ // this.logger.debug(`[ws] sendReply: reqId=${reqId}, cmd=${cmd}, body=${JSON.stringify(logBody)}`);
575
+ return new Promise((resolve, reject) => {
576
+ const frame = {
577
+ cmd,
578
+ headers: { req_id: reqId },
579
+ body,
580
+ };
581
+ const item = { frame, resolve, reject };
582
+ // 入队
583
+ if (!this.replyQueues.has(reqId)) {
584
+ this.replyQueues.set(reqId, []);
585
+ }
586
+ const queue = this.replyQueues.get(reqId);
587
+ // 防止队列无限增长导致内存泄漏
588
+ if (queue.length >= this.maxReplyQueueSize) {
589
+ this.logger.warn(`Reply queue for reqId ${reqId} exceeds max size (${this.maxReplyQueueSize}), rejecting new message`);
590
+ reject(new Error(`Reply queue for reqId ${reqId} exceeds max size (${this.maxReplyQueueSize})`));
591
+ return;
592
+ }
593
+ queue.push(item);
594
+ // 如果队列中只有这一条,说明当前空闲,立即开始处理
595
+ if (queue.length === 1) {
596
+ this.processReplyQueue(reqId);
597
+ }
598
+ });
599
+ }
600
+ /**
601
+ * 处理指定 req_id 的回复队列
602
+ * 取出队列头部的消息发送,并设置回执超时
603
+ */
604
+ processReplyQueue(reqId) {
605
+ const queue = this.replyQueues.get(reqId);
606
+ if (!queue || queue.length === 0) {
607
+ // 队列为空,清理
608
+ this.replyQueues.delete(reqId);
609
+ return;
610
+ }
611
+ const item = queue[0];
612
+ try {
613
+ // 发送帧
614
+ this.send(item.frame);
615
+ this.logger.debug(`Reply message sent via WebSocket, reqId: ${reqId}, queue length: ${queue.length}`);
616
+ }
617
+ catch (error) {
618
+ this.logger.error(`Failed to send reply for reqId ${reqId}:`, error.message);
619
+ // 发送失败:reject 当前项,用 queueMicrotask 异步继续处理下一条,避免同步递归栈溢出
620
+ queue.shift();
621
+ item.reject(error);
622
+ queueMicrotask(() => this.processReplyQueue(reqId));
623
+ return;
624
+ }
625
+ // 分配唯一序列号,用于超时回调中校验是否是当前 pending
626
+ const seq = ++this.pendingAckSeq;
627
+ // 设置回执超时定时器
628
+ const timer = setTimeout(() => {
629
+ // 校验 seq:如果不匹配,说明这是过期的超时回调(当前 pending 已被正常 ack 处理过),直接忽略
630
+ const currentPending = this.pendingAcks.get(reqId);
631
+ if (!currentPending || currentPending.seq !== seq) {
632
+ return;
633
+ }
634
+ this.logger.warn(`Reply ack timeout (${this.replyAckTimeout}ms) for reqId: ${reqId}`);
635
+ this.pendingAcks.delete(reqId);
636
+ // 超时:reject 当前项,然后继续处理队列中的下一条
637
+ queue.shift();
638
+ item.reject(new Error(`Reply ack timeout (${this.replyAckTimeout}ms) for reqId: ${reqId}`));
639
+ this.processReplyQueue(reqId);
640
+ }, this.replyAckTimeout);
641
+ // 注册到待回执 Map
642
+ this.pendingAcks.set(reqId, {
643
+ resolve: item.resolve,
644
+ reject: item.reject,
645
+ timer,
646
+ seq,
647
+ });
648
+ }
649
+ /**
650
+ * 处理回复消息的回执
651
+ * 收到回执后释放队列锁,继续处理下一条
652
+ */
653
+ handleReplyAck(reqId, frame) {
654
+ const pending = this.pendingAcks.get(reqId);
655
+ if (!pending)
656
+ return;
657
+ // 清除超时定时器
658
+ clearTimeout(pending.timer);
659
+ this.pendingAcks.delete(reqId);
660
+ const queue = this.replyQueues.get(reqId);
661
+ if (frame.errcode !== 0) {
662
+ this.logger.warn(`Reply ack error: reqId=${reqId}, errcode=${frame.errcode}, errmsg=${frame.errmsg}`);
663
+ // 失败:reject 当前项
664
+ if (queue) {
665
+ queue.shift();
666
+ }
667
+ pending.reject(frame);
668
+ }
669
+ else {
670
+ this.logger.debug(`Reply ack received for reqId: ${reqId}`);
671
+ // 成功:resolve 当前项,传入完整回执帧
672
+ if (queue) {
673
+ queue.shift();
674
+ }
675
+ pending.resolve(frame);
676
+ }
677
+ // 继续处理队列中的下一条
678
+ this.processReplyQueue(reqId);
679
+ }
680
+ /**
681
+ * 主动断开连接
682
+ */
683
+ /**
684
+ * 清理所有待处理的消息和回执
685
+ * @param reason - 清理原因,用于 reject 的错误信息
686
+ */
687
+ clearPendingMessages(reason) {
688
+ // 收集所有已在 pendingAcks 中的 reject 函数引用,用于后续去重
689
+ const pendingRejects = new Set();
690
+ // 先清理 pendingAcks:清除定时器并 reject 正在等待回执的消息
691
+ for (const [reqId, pending] of this.pendingAcks) {
692
+ clearTimeout(pending.timer);
693
+ pendingRejects.add(pending.reject);
694
+ pending.reject(new Error(`${reason}, reply for reqId: ${reqId} cancelled`));
695
+ }
696
+ this.pendingAcks.clear();
697
+ // 再清理 replyQueues:跳过已经在 pendingAcks 中被 reject 过的队首 item,避免双重 reject
698
+ for (const [reqId, queue] of this.replyQueues) {
699
+ for (const item of queue) {
700
+ if (pendingRejects.has(item.reject)) {
701
+ continue; // 已在 pendingAcks 中被 reject 过,跳过
702
+ }
703
+ item.reject(new Error(`${reason}, reply for reqId: ${reqId} cancelled`));
704
+ }
705
+ }
706
+ this.replyQueues.clear();
707
+ }
708
+ disconnect() {
709
+ this.isManualClose = true;
710
+ this.stopHeartbeat();
711
+ this.clearPendingMessages('Connection manually closed');
712
+ // 取消挂起的重连定时器
713
+ if (this.reconnectTimer) {
714
+ clearTimeout(this.reconnectTimer);
715
+ this.reconnectTimer = null;
716
+ }
717
+ if (this.ws) {
718
+ this.ws.terminate();
719
+ this.ws = null;
720
+ }
721
+ this.logger.info('WebSocket connection manually closed');
722
+ }
723
+ /**
724
+ * 检查指定 reqId 是否有待回执的消息(即上一条消息还未收到 ack)
725
+ *
726
+ * 用于流式场景:调用方可据此决定是否跳过当前帧,避免排队积压。
727
+ *
728
+ * @param reqId - 要检查的 req_id
729
+ * @returns true 表示有消息正在等待 ack
730
+ */
731
+ hasPendingAck(reqId) {
732
+ return this.pendingAcks.has(reqId);
733
+ }
734
+ /**
735
+ * 获取当前连接状态
736
+ */
737
+ get isConnected() {
738
+ return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
739
+ }
740
+ }
741
+
742
+ /**
743
+ * 消息处理器
744
+ * 负责解析 WebSocket 帧并分发为具体的消息事件和事件回调
745
+ */
746
+ class MessageHandler {
747
+ constructor(logger) {
748
+ this.logger = logger;
749
+ }
750
+ /**
751
+ * 处理收到的 WebSocket 帧,解析并触发对应的消息/事件
752
+ *
753
+ * WebSocket 推送帧结构:
754
+ * - 消息推送:{ cmd: "aibot_msg_callback", headers: { req_id: "xxx" }, body: { msgid, msgtype, ... } }
755
+ * - 事件推送:{ cmd: "aibot_event_callback", headers: { req_id: "xxx" }, body: { msgid, msgtype: "event", event: { ... } } }
756
+ *
757
+ * @param frame - WebSocket 接收帧
758
+ * @param emitter - WSClient 实例,用于触发事件
759
+ */
760
+ handleFrame(frame, emitter) {
761
+ try {
762
+ const body = frame.body;
763
+ if (!body || !body.msgtype) {
764
+ this.logger.warn('Received invalid message format:', JSON.stringify(frame).substring(0, 200));
765
+ return;
766
+ }
767
+ // 事件推送回调处理
768
+ if (frame.cmd === WsCmd.EVENT_CALLBACK) {
769
+ this.handleEventCallback(frame, emitter);
770
+ return;
771
+ }
772
+ // 消息推送回调处理
773
+ this.handleMessageCallback(frame, emitter);
774
+ }
775
+ catch (error) {
776
+ this.logger.error('Failed to handle message:', error.message);
777
+ }
778
+ }
779
+ /**
780
+ * 处理消息推送回调 (aibot_msg_callback)
781
+ */
782
+ handleMessageCallback(frame, emitter) {
783
+ const body = frame.body;
784
+ // 触发通用消息事件
785
+ emitter.emit('message', frame);
786
+ // 根据 body 中的消息类型触发特定事件
787
+ switch (body.msgtype) {
788
+ case exports.MessageType.Text:
789
+ emitter.emit('message.text', frame);
790
+ break;
791
+ case exports.MessageType.Image:
792
+ emitter.emit('message.image', frame);
793
+ break;
794
+ case exports.MessageType.Mixed:
795
+ emitter.emit('message.mixed', frame);
796
+ break;
797
+ case exports.MessageType.Voice:
798
+ emitter.emit('message.voice', frame);
799
+ break;
800
+ case exports.MessageType.File:
801
+ emitter.emit('message.file', frame);
802
+ break;
803
+ case exports.MessageType.Video:
804
+ emitter.emit('message.video', frame);
805
+ break;
806
+ default:
807
+ this.logger.debug(`Received unhandled message type: ${body.msgtype}`);
808
+ break;
809
+ }
810
+ }
811
+ /**
812
+ * 处理事件推送回调 (aibot_event_callback)
813
+ */
814
+ handleEventCallback(frame, emitter) {
815
+ const body = frame.body;
816
+ // 触发通用事件
817
+ emitter.emit('event', frame);
818
+ // 根据事件类型触发特定事件
819
+ const eventType = body.event?.eventtype;
820
+ if (eventType) {
821
+ const eventKey = `event.${eventType}`;
822
+ emitter.emit(eventKey, frame);
823
+ }
824
+ else {
825
+ this.logger.debug('Received event callback without eventtype:', JSON.stringify(body).substring(0, 200));
826
+ }
827
+ }
828
+ }
829
+
830
+ /**
831
+ * 加解密工具模块
832
+ * 提供文件加解密相关的功能函数
833
+ */
834
+ /**
835
+ * 使用 AES-256-CBC 解密文件
836
+ *
837
+ * @param encryptedBuffer - 加密的文件数据
838
+ * @param aesKey - Base64 编码的 AES-256 密钥
839
+ * @returns 解密后的文件 Buffer
840
+ */
841
+ function decryptFile(encryptedBuffer, aesKey) {
842
+ // 参数验证
843
+ if (!encryptedBuffer || encryptedBuffer.length === 0) {
844
+ throw new Error('decryptFile: encryptedBuffer is empty or not provided');
845
+ }
846
+ if (!aesKey || typeof aesKey !== 'string') {
847
+ throw new Error('decryptFile: aesKey must be a non-empty string');
848
+ }
849
+ // 将 Base64 编码的 aesKey 解码为 Buffer
850
+ const key = Buffer.from(aesKey, 'base64');
851
+ // IV 取 aesKey 解码后的前 16 字节
852
+ const iv = key.subarray(0, 16);
853
+ try {
854
+ const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
855
+ // 关闭自动 padding,因为文档要求 PKCS#7 填充至 32 字节的倍数,
856
+ // 而 Node.js 默认按 16 字节 block 去除 padding,会导致 bad decrypt 错误
857
+ decipher.setAutoPadding(false);
858
+ const decrypted = Buffer.concat([
859
+ decipher.update(encryptedBuffer),
860
+ decipher.final(),
861
+ ]);
862
+ // 手动去除 PKCS#7 填充(支持 32 字节 block)
863
+ const padLen = decrypted[decrypted.length - 1];
864
+ if (padLen < 1 || padLen > 32 || padLen > decrypted.length) {
865
+ throw new Error(`Invalid PKCS#7 padding value: ${padLen}`);
866
+ }
867
+ // 验证所有 padding 字节是否一致
868
+ for (let i = decrypted.length - padLen; i < decrypted.length; i++) {
869
+ if (decrypted[i] !== padLen) {
870
+ throw new Error('Invalid PKCS#7 padding: padding bytes mismatch');
871
+ }
872
+ }
873
+ return decrypted.subarray(0, decrypted.length - padLen);
874
+ }
875
+ catch (error) {
876
+ throw new Error(`decryptFile: Decryption failed - ${error.message}. This may indicate corrupted data or an incorrect aesKey.`);
877
+ }
878
+ }
879
+
880
+ /**
881
+ * 默认日志实现
882
+ * 带有日志级别和时间戳的控制台日志
883
+ */
884
+ class DefaultLogger {
885
+ constructor(prefix = 'AiBotSDK') {
886
+ this.prefix = prefix;
887
+ }
888
+ formatTime() {
889
+ return new Date().toISOString();
890
+ }
891
+ debug(message, ...args) {
892
+ console.debug(`[${this.formatTime()}] [${this.prefix}] [DEBUG] ${message}`, ...args);
893
+ }
894
+ info(message, ...args) {
895
+ console.info(`[${this.formatTime()}] [${this.prefix}] [INFO] ${message}`, ...args);
896
+ }
897
+ warn(message, ...args) {
898
+ console.warn(`[${this.formatTime()}] [${this.prefix}] [WARN] ${message}`, ...args);
899
+ }
900
+ error(message, ...args) {
901
+ console.error(`[${this.formatTime()}] [${this.prefix}] [ERROR] ${message}`, ...args);
902
+ }
903
+ }
904
+
905
+ class WSClient extends eventemitter3.EventEmitter {
906
+ constructor(options) {
907
+ super();
908
+ this.started = false;
909
+ // 合并默认选项
910
+ this.options = {
911
+ reconnectInterval: 1000,
912
+ maxReconnectAttempts: 10,
913
+ maxAuthFailureAttempts: 5,
914
+ heartbeatInterval: 30000,
915
+ requestTimeout: 10000,
916
+ wsUrl: '',
917
+ wsOptions: {},
918
+ maxReplyQueueSize: 500,
919
+ logger: new DefaultLogger(),
920
+ ...options,
921
+ };
922
+ this.logger = this.options.logger;
923
+ // 初始化 API 客户端(仅用于文件下载)
924
+ this.apiClient = new WeComApiClient(this.logger, this.options.requestTimeout);
925
+ // 初始化 WebSocket 管理器
926
+ this.wsManager = new WsConnectionManager(this.logger, this.options.heartbeatInterval, this.options.reconnectInterval, this.options.maxReconnectAttempts, this.options.wsUrl || undefined, this.options.wsOptions, this.options.maxReplyQueueSize, this.options.maxAuthFailureAttempts);
927
+ // 设置认证凭证
928
+ this.wsManager.setCredentials(this.options.botId, this.options.secret, {
929
+ ...(this.options.scene !== undefined && { scene: this.options.scene }),
930
+ ...(this.options.plug_version !== undefined && { plug_version: this.options.plug_version }),
931
+ });
932
+ // 初始化消息处理器
933
+ this.messageHandler = new MessageHandler(this.logger);
934
+ // 绑定 WebSocket 事件
935
+ this.setupWsEvents();
936
+ }
937
+ /**
938
+ * 设置 WebSocket 事件处理
939
+ */
940
+ setupWsEvents() {
941
+ this.wsManager.onConnected = () => {
942
+ this.emit('connected');
943
+ };
944
+ // 认证成功
945
+ this.wsManager.onAuthenticated = () => {
946
+ this.logger.info('Authenticated');
947
+ this.emit('authenticated');
948
+ };
949
+ this.wsManager.onDisconnected = (reason) => {
950
+ this.emit('disconnected', reason);
951
+ };
952
+ // 服务端因新连接建立而主动断开旧连接
953
+ this.wsManager.onServerDisconnect = (reason) => {
954
+ this.logger.warn(`Server disconnected this connection: ${reason}`);
955
+ this.started = false;
956
+ this.emit('disconnected', reason);
957
+ };
958
+ this.wsManager.onReconnecting = (attempt) => {
959
+ this.emit('reconnecting', attempt);
960
+ };
961
+ this.wsManager.onError = (error) => {
962
+ this.emit('error', error);
963
+ };
964
+ this.wsManager.onMessage = (frame) => {
965
+ this.messageHandler.handleFrame(frame, this);
966
+ };
967
+ }
968
+ /**
969
+ * 建立 WebSocket 长连接
970
+ * SDK 使用内置默认地址建立连接,连接成功后自动发送认证帧(botId + secret)。
971
+ * 支持链式调用:wsClient.connect().on('message', handler)
972
+ *
973
+ * @returns 返回 this,支持链式调用
974
+ */
975
+ connect() {
976
+ if (this.started) {
977
+ this.logger.warn('Client already connected');
978
+ return this;
979
+ }
980
+ this.logger.info('Establishing WebSocket connection...');
981
+ this.started = true;
982
+ // 直接使用内置默认地址建立连接,连接成功后自动认证
983
+ this.wsManager.connect();
984
+ return this;
985
+ }
986
+ /**
987
+ * 断开 WebSocket 连接
988
+ */
989
+ disconnect() {
990
+ if (!this.started) {
991
+ this.logger.warn('Client not connected');
992
+ return;
993
+ }
994
+ this.logger.info('Disconnecting...');
995
+ this.started = false;
996
+ this.wsManager.disconnect();
997
+ this.logger.info('Disconnected');
998
+ }
999
+ /**
1000
+ * 通过 WebSocket 通道发送回复消息(通用方法)
1001
+ *
1002
+ * @param frame - 收到的原始 WebSocket 帧,透传 headers.req_id
1003
+ * @param body - 回复消息体
1004
+ * @param cmd
1005
+ */
1006
+ reply(frame, body, cmd) {
1007
+ const reqId = frame.headers?.req_id || '';
1008
+ return this.wsManager.sendReply(reqId, body, cmd);
1009
+ }
1010
+ /**
1011
+ * 发送流式文本回复(便捷方法)
1012
+ *
1013
+ * @param frame - 收到的原始 WebSocket 帧,透传 headers.req_id
1014
+ * @param streamId - 流式消息 ID
1015
+ * @param content - 回复内容(支持 Markdown)
1016
+ * @param finish - 是否结束流式消息,默认 false
1017
+ * @param msgItem - 图文混排项(仅在 finish=true 时有效),用于在结束时附带图片内容
1018
+ * @param feedback - 反馈信息(仅在首次回复时设置)
1019
+ */
1020
+ replyStream(frame, streamId, content, finish = false, msgItem, feedback) {
1021
+ const stream = {
1022
+ id: streamId,
1023
+ finish,
1024
+ content,
1025
+ };
1026
+ // msg_item 仅在 finish=true 时支持
1027
+ if (finish && msgItem && msgItem.length > 0) {
1028
+ stream.msg_item = msgItem;
1029
+ }
1030
+ // feedback 仅在首次回复时设置
1031
+ if (feedback) {
1032
+ stream.feedback = feedback;
1033
+ }
1034
+ return this.reply(frame, {
1035
+ msgtype: 'stream',
1036
+ stream,
1037
+ });
1038
+ }
1039
+ /**
1040
+ * 发送欢迎语回复
1041
+ *
1042
+ * 注意:此方法需要使用对应事件(如 enter_chat)的 req_id 才能调用,
1043
+ * 即 frame 参数应来自触发欢迎语的事件帧。
1044
+ * 收到事件回调后需在 5 秒内发送回复,超时将无法发送欢迎语。
1045
+ *
1046
+ * @param frame - 对应事件的 WebSocket 帧(需包含该事件的 req_id)
1047
+ * @param body - 欢迎语消息体(支持文本或模板卡片格式)
1048
+ */
1049
+ replyWelcome(frame, body) {
1050
+ return this.reply(frame, body, WsCmd.RESPONSE_WELCOME);
1051
+ }
1052
+ /**
1053
+ * 回复模板卡片消息
1054
+ *
1055
+ * 收到消息回调或进入会话事件后,可使用此方法回复模板卡片消息。
1056
+ *
1057
+ * @param frame - 收到的原始 WebSocket 帧,透传 headers.req_id
1058
+ * @param templateCard - 模板卡片内容
1059
+ * @param feedback - 反馈信息
1060
+ */
1061
+ replyTemplateCard(frame, templateCard, feedback) {
1062
+ const card = feedback ? { ...templateCard, feedback } : templateCard;
1063
+ const body = {
1064
+ msgtype: 'template_card',
1065
+ template_card: card,
1066
+ };
1067
+ return this.reply(frame, body);
1068
+ }
1069
+ /**
1070
+ * 发送流式消息 + 模板卡片组合回复
1071
+ *
1072
+ * 首次回复时必须返回 stream 的 id。
1073
+ * template_card 可首次回复,也可在后续回复中发送,但同一个消息只能回复一次。
1074
+ *
1075
+ * @param frame - 收到的原始 WebSocket 帧,透传 headers.req_id
1076
+ * @param streamId - 流式消息 ID
1077
+ * @param content - 回复内容(支持 Markdown)
1078
+ * @param finish - 是否结束流式消息,默认 false
1079
+ * @param options - 可选项
1080
+ * @param options.msgItem - 图文混排项(仅在 finish=true 时有效)
1081
+ * @param options.streamFeedback - 流式消息反馈信息(首次回复时设置)
1082
+ * @param options.templateCard - 模板卡片内容(同一消息只能回复一次)
1083
+ * @param options.cardFeedback - 模板卡片反馈信息
1084
+ */
1085
+ replyStreamWithCard(frame, streamId, content, finish = false, options) {
1086
+ const stream = {
1087
+ id: streamId,
1088
+ finish,
1089
+ content,
1090
+ };
1091
+ if (finish && options?.msgItem && options.msgItem.length > 0) {
1092
+ stream.msg_item = options.msgItem;
1093
+ }
1094
+ if (options?.streamFeedback) {
1095
+ stream.feedback = options.streamFeedback;
1096
+ }
1097
+ const body = {
1098
+ msgtype: 'stream_with_template_card',
1099
+ stream,
1100
+ };
1101
+ if (options?.templateCard) {
1102
+ body.template_card = options.cardFeedback
1103
+ ? { ...options.templateCard, feedback: options.cardFeedback }
1104
+ : options.templateCard;
1105
+ }
1106
+ return this.reply(frame, body);
1107
+ }
1108
+ /**
1109
+ * 更新模板卡片
1110
+ *
1111
+ * 注意:此方法需要使用对应事件(template_card_event)的 req_id 才能调用,
1112
+ * 即 frame 参数应来自触发更新的事件帧。
1113
+ * 收到事件回调后需在 5 秒内发送回复,超时将无法更新卡片。
1114
+ *
1115
+ * @param frame - 对应事件的 WebSocket 帧(需包含该事件的 req_id)
1116
+ * @param templateCard - 模板卡片内容(task_id 需跟回调收到的 task_id 一致)
1117
+ * @param userids - 要替换模版卡片消息的 userid 列表,若不填则替换所有用户
1118
+ */
1119
+ updateTemplateCard(frame, templateCard, userids) {
1120
+ const body = {
1121
+ response_type: 'update_template_card',
1122
+ template_card: templateCard,
1123
+ };
1124
+ if (userids && userids.length > 0) {
1125
+ body.userids = userids;
1126
+ }
1127
+ return this.reply(frame, body, WsCmd.RESPONSE_UPDATE);
1128
+ }
1129
+ /**
1130
+ * 主动发送消息
1131
+ *
1132
+ * 向指定会话(单聊或群聊)主动推送消息,无需依赖收到的回调帧。
1133
+ *
1134
+ * @param chatid - 会话 ID,单聊填用户的 userid,群聊填对应群聊的 chatid
1135
+ * @param body - 消息体(支持 markdown 或 template_card 格式)
1136
+ * @returns Promise,收到回执时 resolve(回执帧)
1137
+ *
1138
+ * @example
1139
+ * ```ts
1140
+ * // 发送 markdown 消息
1141
+ * await wsClient.sendMessage('CHATID', {
1142
+ * msgtype: 'markdown',
1143
+ * markdown: { content: '这是一条**主动推送**的消息' },
1144
+ * });
1145
+ *
1146
+ * // 发送模板卡片消息
1147
+ * await wsClient.sendMessage('CHATID', {
1148
+ * msgtype: 'template_card',
1149
+ * template_card: { card_type: 'text_notice', ... },
1150
+ * });
1151
+ * ```
1152
+ */
1153
+ sendMessage(chatid, body) {
1154
+ const reqId = generateReqId(WsCmd.SEND_MSG);
1155
+ const fullBody = {
1156
+ chatid,
1157
+ ...body,
1158
+ };
1159
+ return this.wsManager.sendReply(reqId, fullBody, WsCmd.SEND_MSG);
1160
+ }
1161
+ /**
1162
+ * 上传临时素材(三步分片上传)
1163
+ *
1164
+ * 通过 WebSocket 长连接执行分片上传:init → chunk × N → finish
1165
+ * 单个分片不超过 512KB(Base64 编码前),最多 100 个分片。
1166
+ *
1167
+ * @param fileBuffer - 文件 Buffer
1168
+ * @param options - 上传选项(类型、文件名)
1169
+ * @returns 上传结果,包含 media_id
1170
+ */
1171
+ async uploadMedia(fileBuffer, options) {
1172
+ const { type, filename } = options;
1173
+ const totalSize = fileBuffer.length;
1174
+ // 分片大小:512KB(Base64 编码前)
1175
+ const CHUNK_SIZE = 512 * 1024;
1176
+ const totalChunks = Math.ceil(totalSize / CHUNK_SIZE);
1177
+ if (totalChunks > 100) {
1178
+ throw new Error(`File too large: ${totalChunks} chunks exceeds maximum of 100 chunks (max ~50MB)`);
1179
+ }
1180
+ // 计算文件 MD5
1181
+ const md5 = crypto.createHash('md5').update(fileBuffer).digest('hex');
1182
+ this.logger.info(`Uploading media: type=${type}, filename=${filename}, size=${totalSize}, chunks=${totalChunks}`);
1183
+ // Step 1: 初始化上传
1184
+ const initReqId = generateReqId(WsCmd.UPLOAD_MEDIA_INIT);
1185
+ const initResult = await this.wsManager.sendReply(initReqId, { type, filename, total_size: totalSize, total_chunks: totalChunks, md5 }, WsCmd.UPLOAD_MEDIA_INIT);
1186
+ const uploadId = initResult.body?.upload_id;
1187
+ if (!uploadId) {
1188
+ throw new Error(`Upload init failed: no upload_id returned. Response: ${JSON.stringify(initResult)}`);
1189
+ }
1190
+ this.logger.info(`Upload init success: upload_id=${uploadId}`);
1191
+ // Step 2: 分片上传(带重试,根据分片数动态调整并发)
1192
+ /** 单分片最大重试次数 */
1193
+ const MAX_CHUNK_RETRIES = 2;
1194
+ /**
1195
+ * 动态计算并发数:
1196
+ * - 1~4 分片(≤2MB):全部并发
1197
+ * - 5~10 分片(2~5MB):并发 3
1198
+ * - >10 分片(>5MB):并发 2(企微后端对大量并发 chunk 会返回 system error)
1199
+ */
1200
+ const MAX_CONCURRENCY = totalChunks <= 4 ? totalChunks : totalChunks <= 10 ? 3 : 2;
1201
+ const uploadChunk = async (chunkIndex) => {
1202
+ const start = chunkIndex * CHUNK_SIZE;
1203
+ const end = Math.min(start + CHUNK_SIZE, totalSize);
1204
+ const chunk = fileBuffer.subarray(start, end);
1205
+ const base64Data = chunk.toString('base64');
1206
+ let lastError;
1207
+ for (let attempt = 0; attempt <= MAX_CHUNK_RETRIES; attempt++) {
1208
+ try {
1209
+ const chunkReqId = generateReqId(WsCmd.UPLOAD_MEDIA_CHUNK);
1210
+ await this.wsManager.sendReply(chunkReqId, { upload_id: uploadId, chunk_index: chunkIndex, base64_data: base64Data }, WsCmd.UPLOAD_MEDIA_CHUNK);
1211
+ this.logger.debug(`Uploaded chunk ${chunkIndex + 1}/${totalChunks} (${chunk.length} bytes)`);
1212
+ return;
1213
+ }
1214
+ catch (err) {
1215
+ lastError = err;
1216
+ if (attempt < MAX_CHUNK_RETRIES) {
1217
+ const delay = 500 * (attempt + 1);
1218
+ this.logger.warn(`Chunk ${chunkIndex} upload failed (attempt ${attempt + 1}/${MAX_CHUNK_RETRIES + 1}), ` +
1219
+ `retrying in ${delay}ms... error: ${err instanceof Error ? err.message : JSON.stringify(err)}`);
1220
+ await new Promise(r => setTimeout(r, delay));
1221
+ }
1222
+ }
1223
+ }
1224
+ // 所有重试都失败
1225
+ const errMsg = lastError instanceof Error
1226
+ ? lastError.message
1227
+ : JSON.stringify(lastError);
1228
+ throw new Error(`Chunk ${chunkIndex} upload failed after ${MAX_CHUNK_RETRIES + 1} attempts: ${errMsg}`);
1229
+ };
1230
+ this.logger.debug(`Upload concurrency: ${MAX_CONCURRENCY} workers for ${totalChunks} chunks`);
1231
+ if (totalChunks <= 1) {
1232
+ // 单分片直接上传
1233
+ await uploadChunk(0);
1234
+ }
1235
+ else {
1236
+ // 多分片并发上传:动态并发数
1237
+ let nextIndex = 0;
1238
+ const errors = [];
1239
+ const runWorker = async () => {
1240
+ while (nextIndex < totalChunks) {
1241
+ const idx = nextIndex++;
1242
+ try {
1243
+ await uploadChunk(idx);
1244
+ }
1245
+ catch (err) {
1246
+ errors.push(err instanceof Error ? err : new Error(String(err)));
1247
+ }
1248
+ }
1249
+ };
1250
+ const workerCount = Math.min(MAX_CONCURRENCY, totalChunks);
1251
+ await Promise.all(Array.from({ length: workerCount }, () => runWorker()));
1252
+ if (errors.length > 0) {
1253
+ throw new Error(`Upload failed: ${errors.length} chunk(s) failed. First error: ${errors[0].message}`);
1254
+ }
1255
+ }
1256
+ this.logger.info(`All ${totalChunks} chunks uploaded, finishing...`);
1257
+ // Step 3: 完成上传
1258
+ const finishReqId = generateReqId(WsCmd.UPLOAD_MEDIA_FINISH);
1259
+ const finishResult = await this.wsManager.sendReply(finishReqId, { upload_id: uploadId }, WsCmd.UPLOAD_MEDIA_FINISH);
1260
+ const mediaId = finishResult.body?.media_id;
1261
+ if (!mediaId) {
1262
+ throw new Error(`Upload finish failed: no media_id returned. Response: ${JSON.stringify(finishResult)}`);
1263
+ }
1264
+ this.logger.info(`Upload complete: media_id=${mediaId}, type=${finishResult.body?.type}`);
1265
+ return {
1266
+ type: finishResult.body?.type ?? type,
1267
+ media_id: mediaId,
1268
+ created_at: finishResult.body?.created_at ?? new Date().toISOString(),
1269
+ };
1270
+ }
1271
+ /**
1272
+ * 被动回复媒体消息(便捷方法)
1273
+ *
1274
+ * 通过 aibot_respond_msg 被动回复通道发送媒体消息(file/image/voice/video)
1275
+ *
1276
+ * @param frame - 收到的原始 WebSocket 帧,透传 headers.req_id
1277
+ * @param mediaType - 媒体类型
1278
+ * @param mediaId - 临时素材 media_id
1279
+ * @param videoOptions - 视频消息可选参数(仅 mediaType='video' 时生效)
1280
+ */
1281
+ replyMedia(frame, mediaType, mediaId, videoOptions) {
1282
+ const mediaContent = { media_id: mediaId };
1283
+ if (mediaType === 'video' && videoOptions) {
1284
+ if (videoOptions.title)
1285
+ mediaContent.title = videoOptions.title;
1286
+ if (videoOptions.description)
1287
+ mediaContent.description = videoOptions.description;
1288
+ }
1289
+ const body = {
1290
+ msgtype: mediaType,
1291
+ [mediaType]: mediaContent,
1292
+ };
1293
+ return this.reply(frame, body);
1294
+ }
1295
+ /**
1296
+ * 主动发送媒体消息(便捷方法)
1297
+ *
1298
+ * 通过 aibot_send_msg 主动推送通道发送媒体消息
1299
+ *
1300
+ * @param chatid - 会话 ID
1301
+ * @param mediaType - 媒体类型
1302
+ * @param mediaId - 临时素材 media_id
1303
+ * @param videoOptions - 视频消息可选参数(仅 mediaType='video' 时生效)
1304
+ */
1305
+ sendMediaMessage(chatid, mediaType, mediaId, videoOptions) {
1306
+ const mediaContent = { media_id: mediaId };
1307
+ if (mediaType === 'video' && videoOptions) {
1308
+ if (videoOptions.title)
1309
+ mediaContent.title = videoOptions.title;
1310
+ if (videoOptions.description)
1311
+ mediaContent.description = videoOptions.description;
1312
+ }
1313
+ const body = {
1314
+ msgtype: mediaType,
1315
+ [mediaType]: mediaContent,
1316
+ };
1317
+ return this.sendMessage(chatid, body);
1318
+ }
1319
+ /**
1320
+ * 下载文件并使用 AES 密钥解密
1321
+ *
1322
+ * @param url - 文件下载地址
1323
+ * @param aesKey - AES 解密密钥(Base64 编码),取自消息中 image.aeskey 或 file.aeskey 字段
1324
+ * @returns 解密后的文件 Buffer 及文件名
1325
+ *
1326
+ * @example
1327
+ * ```ts
1328
+ * // aesKey 来自消息体中的 image.aeskey 或 file.aeskey
1329
+ * const { buffer, filename } = await wsClient.downloadFile(imageUrl, body.image?.aeskey);
1330
+ * ```
1331
+ */
1332
+ async downloadFile(url, aesKey) {
1333
+ this.logger.debug(`[plugin] downloadFile: url=${url}, hasAesKey=${!!aesKey}`);
1334
+ this.logger.info('Downloading and decrypting file...');
1335
+ try {
1336
+ // 下载加密的文件数据
1337
+ const { buffer: encryptedBuffer, filename } = await this.apiClient.downloadFileRaw(url);
1338
+ // 如果没有提供 aesKey,直接返回原始数据
1339
+ if (!aesKey) {
1340
+ this.logger.warn('No aesKey provided, returning raw file data');
1341
+ return { buffer: encryptedBuffer, filename };
1342
+ }
1343
+ // 使用独立的解密模块进行 AES-256-CBC 解密
1344
+ const decryptedBuffer = decryptFile(encryptedBuffer, aesKey);
1345
+ this.logger.info('File downloaded and decrypted successfully');
1346
+ return { buffer: decryptedBuffer, filename };
1347
+ }
1348
+ catch (error) {
1349
+ this.logger.error('File download/decrypt failed:', error.message);
1350
+ throw error;
1351
+ }
1352
+ }
1353
+ /**
1354
+ * 检查指定消息帧是否有未完成的 ack(即上一条消息还未收到回执)
1355
+ *
1356
+ * 用于流式场景:调用方可据此决定是否跳过当前中间帧,避免排队积压。
1357
+ *
1358
+ * @param frame - 收到的原始 WebSocket 帧
1359
+ * @returns true 表示有消息正在等待 ack
1360
+ */
1361
+ hasPendingReplyAck(frame) {
1362
+ const reqId = frame.headers?.req_id || '';
1363
+ return this.wsManager.hasPendingAck(reqId);
1364
+ }
1365
+ /**
1366
+ * 非阻塞流式文本回复
1367
+ *
1368
+ * 如果上一条同 reqId 的消息尚未收到 ack,则跳过本次发送(返回 'skipped'),
1369
+ * 避免流式中间帧排队积压导致延迟。
1370
+ *
1371
+ * 注意:finish=true 的最终帧不受此限制,始终保证发送(走正常队列)。
1372
+ *
1373
+ * @param frame - 收到的原始 WebSocket 帧
1374
+ * @param streamId - 流式消息 ID
1375
+ * @param content - 回复内容
1376
+ * @param finish - 是否结束流式消息
1377
+ * @param msgItem - 图文混排项(仅在 finish=true 时有效),用于在结束时附带图片内容
1378
+ * @param feedback - 反馈信息(仅在首次回复时设置)
1379
+ * @returns Promise<WsFrame> 正常发送时返回回执帧,跳过时返回 'skipped'
1380
+ */
1381
+ replyStreamNonBlocking(frame, streamId, content, finish = false, msgItem, feedback) {
1382
+ // finish=true 的最终帧必须发送,不做跳过判断
1383
+ if (!finish && this.hasPendingReplyAck(frame)) {
1384
+ return Promise.resolve('skipped');
1385
+ }
1386
+ return this.replyStream(frame, streamId, content, finish, msgItem, feedback);
1387
+ }
1388
+ /**
1389
+ * 获取当前连接状态
1390
+ */
1391
+ get isConnected() {
1392
+ return this.wsManager.isConnected;
1393
+ }
1394
+ /**
1395
+ * 获取 API 客户端实例(供高级用途使用,如文件下载)
1396
+ */
1397
+ get api() {
1398
+ return this.apiClient;
1399
+ }
1400
+ }
1401
+
1402
+ /**
1403
+ * WeCom 加解密通用核心
1404
+ * 独立于 Webhook、WebSocket、Agent 的具体协议形态,统一提供基于 AES-256-CBC
1405
+ * 的加解密与 SHA1 签名计算能力。
1406
+ */
1407
+ const CRYPTO_CONSTANTS = {
1408
+ /** PKCS#7 块大小 */
1409
+ PKCS7_BLOCK_SIZE: 32,
1410
+ /** AES Key 长度 */
1411
+ AES_KEY_LENGTH: 32,
1412
+ };
1413
+ /**
1414
+ * 解码企业微信提供的 Base64 encodingAESKey
1415
+ */
1416
+ function decodeEncodingAESKey(encodingAESKey) {
1417
+ const trimmed = encodingAESKey.trim();
1418
+ if (!trimmed)
1419
+ throw new Error("encodingAESKey missing");
1420
+ const withPadding = trimmed.endsWith("=") ? trimmed : `${trimmed}=`;
1421
+ const key = Buffer.from(withPadding, "base64");
1422
+ if (key.length !== CRYPTO_CONSTANTS.AES_KEY_LENGTH) {
1423
+ throw new Error(`invalid encodingAESKey (expected ${CRYPTO_CONSTANTS.AES_KEY_LENGTH} bytes, got ${key.length})`);
1424
+ }
1425
+ return key;
1426
+ }
1427
+ /**
1428
+ * PKCS#7 填充
1429
+ */
1430
+ function pkcs7Pad(buf, blockSize) {
1431
+ const mod = buf.length % blockSize;
1432
+ const pad = mod === 0 ? blockSize : blockSize - mod;
1433
+ const padByte = Buffer.alloc(1, pad);
1434
+ return Buffer.concat([buf, Buffer.alloc(pad, padByte[0])]);
1435
+ }
1436
+ /**
1437
+ * PKCS#7 解除填充
1438
+ */
1439
+ function pkcs7Unpad(buf, blockSize) {
1440
+ if (buf.length === 0)
1441
+ throw new Error("invalid pkcs7 payload");
1442
+ const pad = buf[buf.length - 1];
1443
+ if (pad < 1 || pad > blockSize) {
1444
+ throw new Error("invalid pkcs7 padding value");
1445
+ }
1446
+ if (pad > buf.length) {
1447
+ throw new Error("invalid pkcs7 payload length");
1448
+ }
1449
+ for (let i = 0; i < pad; i += 1) {
1450
+ if (buf[buf.length - 1 - i] !== pad) {
1451
+ throw new Error("invalid pkcs7 padding byte");
1452
+ }
1453
+ }
1454
+ return buf.subarray(0, buf.length - pad);
1455
+ }
1456
+ /**
1457
+ * 计算 SHA1 哈希
1458
+ */
1459
+ function sha1Hex(input) {
1460
+ return crypto$1.createHash("sha1").update(input).digest("hex");
1461
+ }
1462
+ class WecomCrypto {
1463
+ constructor(token, encodingAESKey, receiveId // 对应企业微信的 corpId 或 botId (用于校验与追加)
1464
+ ) {
1465
+ this.token = token;
1466
+ this.encodingAESKey = encodingAESKey;
1467
+ this.receiveId = receiveId;
1468
+ if (!token)
1469
+ throw new Error("token is required");
1470
+ this.aesKey = decodeEncodingAESKey(encodingAESKey);
1471
+ this.iv = this.aesKey.subarray(0, 16);
1472
+ }
1473
+ /**
1474
+ * 计算 WeCom 消息签名
1475
+ */
1476
+ computeSignature(timestamp, nonce, encrypt) {
1477
+ const parts = [this.token, timestamp, nonce, encrypt]
1478
+ .map((v) => String(v ?? ""))
1479
+ .sort();
1480
+ return sha1Hex(parts.join(""));
1481
+ }
1482
+ /**
1483
+ * 验证 WeCom 消息签名
1484
+ */
1485
+ verifySignature(signature, timestamp, nonce, encrypt) {
1486
+ const expected = this.computeSignature(timestamp, nonce, encrypt);
1487
+ return expected === signature;
1488
+ }
1489
+ /**
1490
+ * 消息解密
1491
+ * 返回纯文本字符串(XML 或 JSON 根据上层业务而定)
1492
+ */
1493
+ decrypt(encryptText) {
1494
+ const decipher = crypto$1.createDecipheriv("aes-256-cbc", this.aesKey, this.iv);
1495
+ decipher.setAutoPadding(false);
1496
+ const decryptedPadded = Buffer.concat([
1497
+ decipher.update(Buffer.from(encryptText, "base64")),
1498
+ decipher.final(),
1499
+ ]);
1500
+ const decrypted = pkcs7Unpad(decryptedPadded, CRYPTO_CONSTANTS.PKCS7_BLOCK_SIZE);
1501
+ if (decrypted.length < 20) {
1502
+ throw new Error(`invalid payload (expected >=20 bytes, got ${decrypted.length})`);
1503
+ }
1504
+ // 16 bytes random + 4 bytes length + msg + receiveId
1505
+ const msgLen = decrypted.readUInt32BE(16);
1506
+ const msgStart = 20;
1507
+ const msgEnd = msgStart + msgLen;
1508
+ if (msgEnd > decrypted.length) {
1509
+ throw new Error(`invalid msg length (msgEnd=${msgEnd}, total=${decrypted.length})`);
1510
+ }
1511
+ const msg = decrypted.subarray(msgStart, msgEnd).toString("utf8");
1512
+ const receiveId = this.receiveId ?? "";
1513
+ if (receiveId) {
1514
+ const trailing = decrypted.subarray(msgEnd).toString("utf8");
1515
+ if (trailing !== receiveId) {
1516
+ throw new Error(`receiveId mismatch (expected "${receiveId}", got "${trailing}")`);
1517
+ }
1518
+ }
1519
+ return msg;
1520
+ }
1521
+ /**
1522
+ * 消息加密
1523
+ * 加密明文并返回 base64 格式密文与对应的新签名
1524
+ */
1525
+ encrypt(plainText, timestamp, nonce) {
1526
+ const random16 = crypto$1.randomBytes(16);
1527
+ const msgBuf = Buffer.from(plainText ?? "", "utf8");
1528
+ const msgLen = Buffer.alloc(4);
1529
+ msgLen.writeUInt32BE(msgBuf.length, 0);
1530
+ const receiveIdBuf = Buffer.from(this.receiveId ?? "", "utf8");
1531
+ const raw = Buffer.concat([random16, msgLen, msgBuf, receiveIdBuf]);
1532
+ const padded = pkcs7Pad(raw, CRYPTO_CONSTANTS.PKCS7_BLOCK_SIZE);
1533
+ const cipher = crypto$1.createCipheriv("aes-256-cbc", this.aesKey, this.iv);
1534
+ cipher.setAutoPadding(false);
1535
+ const encryptedBuf = Buffer.concat([cipher.update(padded), cipher.final()]);
1536
+ const encryptBase64 = encryptedBuf.toString("base64");
1537
+ const signature = this.computeSignature(timestamp, nonce, encryptBase64);
1538
+ return { encrypt: encryptBase64, signature };
1539
+ }
1540
+ }
1541
+
1542
+ /** 默认导出 AiBot 命名空间 */
1543
+ const AiBot = {
1544
+ WSClient,
1545
+ };
1546
+
1547
+ exports.DefaultLogger = DefaultLogger;
1548
+ exports.MessageHandler = MessageHandler;
1549
+ exports.WSAuthFailureError = WSAuthFailureError;
1550
+ exports.WSClient = WSClient;
1551
+ exports.WSReconnectExhaustedError = WSReconnectExhaustedError;
1552
+ exports.WeComApiClient = WeComApiClient;
1553
+ exports.WecomCrypto = WecomCrypto;
1554
+ exports.WsCmd = WsCmd;
1555
+ exports.WsConnectionManager = WsConnectionManager;
1556
+ exports.decodeEncodingAESKey = decodeEncodingAESKey;
1557
+ exports.decryptFile = decryptFile;
1558
+ exports.default = AiBot;
1559
+ exports.generateRandomString = generateRandomString;
1560
+ exports.generateReqId = generateReqId;
1561
+ exports.pkcs7Pad = pkcs7Pad;
1562
+ exports.pkcs7Unpad = pkcs7Unpad;
1563
+ //# sourceMappingURL=index.cjs.js.map