weflow-cli 1.5.0 → 1.6.1

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 (332) hide show
  1. package/ARCHITECTURE.md +105 -0
  2. package/CHANGELOG.md +121 -0
  3. package/CONTRIBUTING.md +45 -0
  4. package/LICENSE +21 -21
  5. package/OPERATIONS.md +272 -0
  6. package/README.en.md +281 -0
  7. package/README.md +334 -149
  8. package/SECURITY.md +47 -0
  9. package/bin/weflow-cli.ts +4050 -581
  10. package/dist/bin/weflow-cli.js +4310 -588
  11. package/dist/bin/weflow-cli.js.map +1 -1
  12. package/dist/mcp-server/index.js +178 -236
  13. package/dist/mcp-server/index.js.map +1 -1
  14. package/dist/src/core/dbPathService.d.ts +15 -1
  15. package/dist/src/core/dbPathService.d.ts.map +1 -1
  16. package/dist/src/core/dbPathService.js +177 -19
  17. package/dist/src/core/dbPathService.js.map +1 -1
  18. package/dist/src/core/keyService.d.ts +8 -0
  19. package/dist/src/core/keyService.d.ts.map +1 -1
  20. package/dist/src/core/keyService.js +127 -22
  21. package/dist/src/core/keyService.js.map +1 -1
  22. package/dist/src/core/ntCore.d.ts +59 -1
  23. package/dist/src/core/ntCore.d.ts.map +1 -1
  24. package/dist/src/core/ntCore.js +201 -65
  25. package/dist/src/core/ntCore.js.map +1 -1
  26. package/dist/src/core/sqlcipherCore.d.ts.map +1 -1
  27. package/dist/src/core/sqlcipherCore.js +18 -1
  28. package/dist/src/core/sqlcipherCore.js.map +1 -1
  29. package/dist/src/core/wcdbCore.d.ts.map +1 -1
  30. package/dist/src/core/wcdbCore.js +3 -6
  31. package/dist/src/core/wcdbCore.js.map +1 -1
  32. package/dist/src/services/assistantDaemon.d.ts +19 -0
  33. package/dist/src/services/assistantDaemon.d.ts.map +1 -0
  34. package/dist/src/services/assistantDaemon.js +117 -0
  35. package/dist/src/services/assistantDaemon.js.map +1 -0
  36. package/dist/src/services/assistantMemory.d.ts +36 -0
  37. package/dist/src/services/assistantMemory.d.ts.map +1 -0
  38. package/dist/src/services/assistantMemory.js +182 -0
  39. package/dist/src/services/assistantMemory.js.map +1 -0
  40. package/dist/src/services/assistantPrivacy.d.ts +25 -0
  41. package/dist/src/services/assistantPrivacy.d.ts.map +1 -0
  42. package/dist/src/services/assistantPrivacy.js +77 -0
  43. package/dist/src/services/assistantPrivacy.js.map +1 -0
  44. package/dist/src/services/assistantRouting.d.ts +18 -0
  45. package/dist/src/services/assistantRouting.d.ts.map +1 -0
  46. package/dist/src/services/assistantRouting.js +68 -0
  47. package/dist/src/services/assistantRouting.js.map +1 -0
  48. package/dist/src/services/assistantService.d.ts +37 -0
  49. package/dist/src/services/assistantService.d.ts.map +1 -0
  50. package/dist/src/services/assistantService.js +284 -0
  51. package/dist/src/services/assistantService.js.map +1 -0
  52. package/dist/src/services/assistantTools.d.ts +24 -0
  53. package/dist/src/services/assistantTools.d.ts.map +1 -0
  54. package/dist/src/services/assistantTools.js +582 -0
  55. package/dist/src/services/assistantTools.js.map +1 -0
  56. package/dist/src/services/chatService.d.ts +44 -0
  57. package/dist/src/services/chatService.d.ts.map +1 -1
  58. package/dist/src/services/chatService.js +105 -4
  59. package/dist/src/services/chatService.js.map +1 -1
  60. package/dist/src/services/configService.d.ts +55 -0
  61. package/dist/src/services/configService.d.ts.map +1 -1
  62. package/dist/src/services/configService.js +147 -6
  63. package/dist/src/services/configService.js.map +1 -1
  64. package/dist/src/services/evidenceService.d.ts +30 -0
  65. package/dist/src/services/evidenceService.d.ts.map +1 -0
  66. package/dist/src/services/evidenceService.js +98 -0
  67. package/dist/src/services/evidenceService.js.map +1 -0
  68. package/dist/src/services/exportService.d.ts +13 -20
  69. package/dist/src/services/exportService.d.ts.map +1 -1
  70. package/dist/src/services/exportService.js +113 -47
  71. package/dist/src/services/exportService.js.map +1 -1
  72. package/dist/src/services/initKeyService.d.ts +19 -0
  73. package/dist/src/services/initKeyService.d.ts.map +1 -0
  74. package/dist/src/services/initKeyService.js +80 -0
  75. package/dist/src/services/initKeyService.js.map +1 -0
  76. package/dist/src/services/messageContract.d.ts +23 -0
  77. package/dist/src/services/messageContract.d.ts.map +1 -0
  78. package/dist/src/services/messageContract.js +49 -0
  79. package/dist/src/services/messageContract.js.map +1 -0
  80. package/dist/src/services/messageQuery.d.ts +7 -0
  81. package/dist/src/services/messageQuery.d.ts.map +1 -0
  82. package/dist/src/services/messageQuery.js +30 -0
  83. package/dist/src/services/messageQuery.js.map +1 -0
  84. package/dist/src/services/wechat-formatter.js +14 -14
  85. package/dist/src/services/wechatMessageService.d.ts.map +1 -1
  86. package/dist/src/services/wechatMessageService.js +31 -3
  87. package/dist/src/services/wechatMessageService.js.map +1 -1
  88. package/dist/src/services/whitelistService.d.ts +52 -4
  89. package/dist/src/services/whitelistService.d.ts.map +1 -1
  90. package/dist/src/services/whitelistService.js +183 -17
  91. package/dist/src/services/whitelistService.js.map +1 -1
  92. package/dist/src/types.d.ts +20 -0
  93. package/dist/src/types.d.ts.map +1 -1
  94. package/dist/src/utils/dateRange.d.ts +15 -0
  95. package/dist/src/utils/dateRange.d.ts.map +1 -0
  96. package/dist/src/utils/dateRange.js +48 -0
  97. package/dist/src/utils/dateRange.js.map +1 -0
  98. package/dist/src/utils/mcpSecurity.d.ts +5 -0
  99. package/dist/src/utils/mcpSecurity.d.ts.map +1 -0
  100. package/dist/src/utils/mcpSecurity.js +39 -0
  101. package/dist/src/utils/mcpSecurity.js.map +1 -0
  102. package/dist/src/utils/packageRoot.d.ts +2 -0
  103. package/dist/src/utils/packageRoot.d.ts.map +1 -0
  104. package/dist/src/utils/packageRoot.js +16 -0
  105. package/dist/src/utils/packageRoot.js.map +1 -0
  106. package/dist/src/utils/python.d.ts +2 -0
  107. package/dist/src/utils/python.d.ts.map +1 -0
  108. package/dist/src/utils/python.js +72 -0
  109. package/dist/src/utils/python.js.map +1 -0
  110. package/dist/src/utils/pythonProcessEnv.d.ts +3 -0
  111. package/dist/src/utils/pythonProcessEnv.d.ts.map +1 -0
  112. package/dist/src/utils/pythonProcessEnv.js +46 -0
  113. package/dist/src/utils/pythonProcessEnv.js.map +1 -0
  114. package/dist/src/utils/talkerUtils.d.ts +4 -1
  115. package/dist/src/utils/talkerUtils.d.ts.map +1 -1
  116. package/dist/src/utils/talkerUtils.js +9 -9
  117. package/dist/src/utils/talkerUtils.js.map +1 -1
  118. package/dist/src/utils/wechatEmoji.d.ts +5 -0
  119. package/dist/src/utils/wechatEmoji.d.ts.map +1 -0
  120. package/dist/src/utils/wechatEmoji.js +35 -0
  121. package/dist/src/utils/wechatEmoji.js.map +1 -0
  122. package/docs/AI_INTERFACE.md +189 -0
  123. package/docs/BRANCHES.md +87 -0
  124. package/docs/DATA_CONTRACT.md +74 -0
  125. package/docs/DECISIONS.md +270 -0
  126. package/docs/DEEPSEEK_V4_FLASH/346/226/275/345/267/245/346/226/207/346/241/243.md +234 -0
  127. package/docs/EVIDENCE_GUIDE.md +96 -0
  128. package/docs/MCP.md +95 -0
  129. package/docs/PARTNERS.md +41 -0
  130. package/docs/PROJECT_STATE.md +96 -0
  131. package/docs/RELEASING.md +124 -0
  132. package/docs/ROADMAP.md +191 -0
  133. package/docs/SETUP.md +103 -0
  134. package/docs/images/weflow-architecture-gpt-image-2.png +0 -0
  135. package/docs/images/weflow-architecture.png +0 -0
  136. package/docs/images/weflow-architecture.svg +65 -0
  137. package/mcp-server/index.ts +169 -269
  138. package/package.json +81 -72
  139. package/requirements-3x.txt +4 -0
  140. package/requirements-voice.txt +15 -0
  141. package/requirements.txt +9 -0
  142. package/resources/js/marked.min.js +69 -0
  143. package/resources/wechat-emoji/666.png +0 -0
  144. package/resources/wechat-emoji/Awesome.png +0 -0
  145. package/resources/wechat-emoji/Concerned.png +0 -0
  146. package/resources/wechat-emoji/Cry.png +0 -0
  147. package/resources/wechat-emoji/Emm.png +0 -0
  148. package/resources/wechat-emoji/Facepalm.png +0 -0
  149. package/resources/wechat-emoji/Grin.png +0 -0
  150. package/resources/wechat-emoji/OK.png +0 -0
  151. package/resources/wechat-emoji/Respect.png +0 -0
  152. package/resources/wechat-emoji/Sick.png +0 -0
  153. package/resources/wechat-emoji/Sleep.png +0 -0
  154. package/resources/wechat-emoji/Smile.png +0 -0
  155. package/resources/wechat-emoji//344/272/262/344/272/262.png +0 -0
  156. package/resources/wechat-emoji//344/276/277/344/276/277.png +0 -0
  157. package/resources/wechat-emoji//345/201/267/347/254/221.png +0 -0
  158. package/resources/wechat-emoji//345/202/262/346/205/242.png +0 -0
  159. package/resources/wechat-emoji//345/206/215/350/247/201.png +0 -0
  160. package/resources/wechat-emoji//345/207/213/350/260/242.png +0 -0
  161. package/resources/wechat-emoji//345/212/240/346/262/271.png +0 -0
  162. package/resources/wechat-emoji//345/213/276/345/274/225.png +0 -0
  163. package/resources/wechat-emoji//345/217/221/345/221/206.png +0 -0
  164. package/resources/wechat-emoji//345/217/221/346/200/222.png +0 -0
  165. package/resources/wechat-emoji//345/217/221/346/212/226.png +0 -0
  166. package/resources/wechat-emoji//345/217/257/346/200/234.png +0 -0
  167. package/resources/wechat-emoji//345/217/263/345/223/274/345/223/274.png +0 -0
  168. package/resources/wechat-emoji//345/217/271/346/260/224.png +0 -0
  169. package/resources/wechat-emoji//345/220/203/347/223/234.png +0 -0
  170. package/resources/wechat-emoji//345/220/210/345/215/201.png +0 -0
  171. package/resources/wechat-emoji//345/220/220.png +0 -0
  172. package/resources/wechat-emoji//345/221/262/347/211/231.png +0 -0
  173. package/resources/wechat-emoji//345/222/222/351/252/202.png +0 -0
  174. package/resources/wechat-emoji//345/222/226/345/225/241.png +0 -0
  175. package/resources/wechat-emoji//345/223/207.png +0 -0
  176. package/resources/wechat-emoji//345/225/244/351/205/222.png +0 -0
  177. package/resources/wechat-emoji//345/230/230.png +0 -0
  178. package/resources/wechat-emoji//345/230/264/345/224/207.png +0 -0
  179. package/resources/wechat-emoji//345/230/277/345/223/210.png +0 -0
  180. package/resources/wechat-emoji//345/233/247.png +0 -0
  181. package/resources/wechat-emoji//345/233/260.png +0 -0
  182. package/resources/wechat-emoji//345/235/217/347/254/221.png +0 -0
  183. package/resources/wechat-emoji//345/244/247/345/223/255.png +0 -0
  184. package/resources/wechat-emoji//345/244/251/345/225/212.png +0 -0
  185. package/resources/wechat-emoji//345/244/252/351/230/263.png +0 -0
  186. package/resources/wechat-emoji//345/244/261/346/234/233.png +0 -0
  187. package/resources/wechat-emoji//345/245/270/347/254/221.png +0 -0
  188. package/resources/wechat-emoji//345/245/275/347/232/204.png +0 -0
  189. package/resources/wechat-emoji//345/247/224/345/261/210.png +0 -0
  190. package/resources/wechat-emoji//345/256/263/347/276/236.png +0 -0
  191. package/resources/wechat-emoji//345/260/264/345/260/254.png +0 -0
  192. package/resources/wechat-emoji//345/272/206/347/245/235.png +0 -0
  193. package/resources/wechat-emoji//345/274/261.png +0 -0
  194. package/resources/wechat-emoji//345/274/272.png +0 -0
  195. package/resources/wechat-emoji//345/276/227/346/204/217.png +0 -0
  196. package/resources/wechat-emoji//345/276/256/347/254/221.png +0 -0
  197. package/resources/wechat-emoji//345/277/203/347/242/216.png +0 -0
  198. package/resources/wechat-emoji//345/277/253/345/223/255/344/272/206.png +0 -0
  199. package/resources/wechat-emoji//346/201/220/346/203/247.png +0 -0
  200. package/resources/wechat-emoji//346/202/240/351/227/262.png +0 -0
  201. package/resources/wechat-emoji//346/203/212/346/201/220.png +0 -0
  202. package/resources/wechat-emoji//346/203/212/350/256/266.png +0 -0
  203. package/resources/wechat-emoji//346/204/211/345/277/253.png +0 -0
  204. package/resources/wechat-emoji//346/206/250/347/254/221.png +0 -0
  205. package/resources/wechat-emoji//346/211/223/350/204/270.png +0 -0
  206. package/resources/wechat-emoji//346/212/223/347/213/202.png +0 -0
  207. package/resources/wechat-emoji//346/212/240/351/274/273.png +0 -0
  208. package/resources/wechat-emoji//346/212/261/346/213/263.png +0 -0
  209. package/resources/wechat-emoji//346/213/245/346/212/261.png +0 -0
  210. package/resources/wechat-emoji//346/213/263/345/244/264.png +0 -0
  211. package/resources/wechat-emoji//346/215/202/350/204/270.png +0 -0
  212. package/resources/wechat-emoji//346/217/241/346/211/213.png +0 -0
  213. package/resources/wechat-emoji//346/222/207/345/230/264.png +0 -0
  214. package/resources/wechat-emoji//346/223/246/346/261/227.png +0 -0
  215. package/resources/wechat-emoji//346/225/262/346/211/223.png +0 -0
  216. package/resources/wechat-emoji//346/227/240/350/257/255.png +0 -0
  217. package/resources/wechat-emoji//346/227/272/346/237/264.png +0 -0
  218. package/resources/wechat-emoji//346/231/225.png +0 -0
  219. package/resources/wechat-emoji//346/234/210/344/272/256.png +0 -0
  220. package/resources/wechat-emoji//346/234/272/346/231/272.png +0 -0
  221. package/resources/wechat-emoji//346/261/227.png +0 -0
  222. package/resources/wechat-emoji//346/265/201/346/263/252.png +0 -0
  223. package/resources/wechat-emoji//347/202/270/345/274/271.png +0 -0
  224. package/resources/wechat-emoji//347/203/237/350/212/261.png +0 -0
  225. package/resources/wechat-emoji//347/210/206/347/253/271.png +0 -0
  226. package/resources/wechat-emoji//347/210/261/345/277/203.png +0 -0
  227. package/resources/wechat-emoji//347/214/252/345/244/264.png +0 -0
  228. package/resources/wechat-emoji//347/216/253/347/221/260.png +0 -0
  229. package/resources/wechat-emoji//347/224/237/347/227/205.png +0 -0
  230. package/resources/wechat-emoji//347/226/221/351/227/256.png +0 -0
  231. package/resources/wechat-emoji//347/231/274.png +0 -0
  232. package/resources/wechat-emoji//347/231/275/347/234/274.png +0 -0
  233. package/resources/wechat-emoji//347/232/261/347/234/211.png +0 -0
  234. package/resources/wechat-emoji//347/235/241.png +0 -0
  235. package/resources/wechat-emoji//347/240/264/346/266/225/344/270/272/347/254/221.png +0 -0
  236. package/resources/wechat-emoji//347/244/274/347/211/251.png +0 -0
  237. package/resources/wechat-emoji//347/244/276/344/274/232/347/244/276/344/274/232.png +0 -0
  238. package/resources/wechat-emoji//347/246/217.png +0 -0
  239. package/resources/wechat-emoji//347/254/221/350/204/270.png +0 -0
  240. package/resources/wechat-emoji//347/272/242/345/214/205.png +0 -0
  241. package/resources/wechat-emoji//347/277/273/347/231/275/347/234/274.png +0 -0
  242. package/resources/wechat-emoji//350/200/266.png +0 -0
  243. package/resources/wechat-emoji//350/203/234/345/210/251.png +0 -0
  244. package/resources/wechat-emoji//350/204/270/347/272/242.png +0 -0
  245. package/resources/wechat-emoji//350/211/262.png +0 -0
  246. package/resources/wechat-emoji//350/213/246/346/266/251.png +0 -0
  247. package/resources/wechat-emoji//350/217/234/345/210/200.png +0 -0
  248. package/resources/wechat-emoji//350/233/213/347/263/225.png +0 -0
  249. package/resources/wechat-emoji//350/241/260.png +0 -0
  250. package/resources/wechat-emoji//350/243/202/345/274/200.png +0 -0
  251. package/resources/wechat-emoji//350/256/251/346/210/221/347/234/213/347/234/213.png +0 -0
  252. package/resources/wechat-emoji//350/260/203/347/232/256.png +0 -0
  253. package/resources/wechat-emoji//350/267/263/350/267/263.png +0 -0
  254. package/resources/wechat-emoji//350/275/254/345/234/210.png +0 -0
  255. package/resources/wechat-emoji//351/204/231/350/247/206.png +0 -0
  256. package/resources/wechat-emoji//351/227/255/345/230/264.png +0 -0
  257. package/resources/wechat-emoji//351/230/264/351/231/251.png +0 -0
  258. package/resources/wechat-emoji//351/232/276/350/277/207.png +0 -0
  259. package/resources/wechat-emoji//351/252/267/351/253/205.png +0 -0
  260. package/resources/wechat-emoji//351/274/223/346/216/214.png +0 -0
  261. package/scripts/_batch_link_sources.py +73 -73
  262. package/scripts/_utils.py +29 -0
  263. package/scripts/annual_report.py +796 -796
  264. package/scripts/auto_tag.py +128 -128
  265. package/scripts/biz_daily.py +210 -53
  266. package/scripts/chat_report.py +10 -0
  267. package/scripts/chat_stats.py +702 -702
  268. package/scripts/classify_daily.py +17 -11
  269. package/scripts/create_reading_notes.py +370 -370
  270. package/scripts/daily_stats.py +132 -0
  271. package/scripts/enrich_backlinks.py +163 -163
  272. package/scripts/export_chat_html.py +1704 -107
  273. package/scripts/extract_todos.py +445 -404
  274. package/scripts/fav_server.py +119 -12
  275. package/scripts/fix_topics.py +111 -111
  276. package/scripts/generate_ai_report.py +1 -1
  277. package/scripts/generate_html.py +597 -277
  278. package/scripts/inspection_records.py +354 -0
  279. package/scripts/mcp_bridge.py +398 -398
  280. package/scripts/nt_decrypt.py +702 -48
  281. package/scripts/pipeline.py +27 -19
  282. package/scripts/promote_all.py +312 -310
  283. package/scripts/promote_ideas.py +299 -297
  284. package/scripts/rag_chat.py +194 -191
  285. package/scripts/semantic_search.py +435 -431
  286. package/scripts/sync_fav.py +201 -146
  287. package/scripts/sync_weread.py +192 -192
  288. package/scripts/vault_rag.py +140 -139
  289. package/scripts/vault_search.py +141 -141
  290. package/scripts/wechat_emoji.py +132 -0
  291. package/scripts/wechat_emoticon.py +433 -0
  292. package/scripts/wechat_image.py +258 -0
  293. package/scripts/wechat_voice.py +327 -0
  294. package/src/core/dbPathService.ts +163 -18
  295. package/src/core/keyService.ts +137 -25
  296. package/src/core/ntCore.ts +215 -62
  297. package/src/core/sqlcipherCore.ts +17 -1
  298. package/src/core/wcdbCore.ts +3 -7
  299. package/src/services/assistantDaemon.ts +111 -0
  300. package/src/services/assistantMemory.ts +193 -0
  301. package/src/services/assistantPrivacy.ts +87 -0
  302. package/src/services/assistantRouting.ts +92 -0
  303. package/src/services/assistantService.ts +295 -0
  304. package/src/services/assistantTools.ts +549 -0
  305. package/src/services/chatService.ts +133 -4
  306. package/src/services/configService.ts +194 -6
  307. package/src/services/evidenceService.ts +129 -0
  308. package/src/services/exportService.ts +113 -48
  309. package/src/services/initKeyService.ts +83 -0
  310. package/src/services/messageContract.ts +77 -0
  311. package/src/services/messageQuery.ts +38 -0
  312. package/src/services/wechat-formatter.ts +348 -348
  313. package/src/services/wechatMessageService.ts +25 -3
  314. package/src/services/wereadService.ts +308 -308
  315. package/src/services/whitelistService.ts +209 -22
  316. package/src/types.ts +20 -0
  317. package/src/utils/dateRange.ts +58 -0
  318. package/src/utils/mcpSecurity.ts +39 -0
  319. package/src/utils/packageRoot.ts +14 -0
  320. package/src/utils/python.ts +73 -0
  321. package/src/utils/pythonProcessEnv.ts +45 -0
  322. package/src/utils/talkerUtils.ts +14 -2
  323. package/dist/src/utils/errors.d.ts +0 -25
  324. package/dist/src/utils/errors.d.ts.map +0 -1
  325. package/dist/src/utils/errors.js +0 -43
  326. package/dist/src/utils/errors.js.map +0 -1
  327. package/dist/src/utils/pythonRunner.d.ts +0 -27
  328. package/dist/src/utils/pythonRunner.d.ts.map +0 -1
  329. package/dist/src/utils/pythonRunner.js +0 -62
  330. package/dist/src/utils/pythonRunner.js.map +0 -1
  331. package/src/utils/errors.ts +0 -42
  332. package/src/utils/pythonRunner.ts +0 -81
@@ -1,34 +1,82 @@
1
1
  /**
2
- * 白名单服务控制消息收发权限,仅白名单内 wxid 可收发消息。
2
+ * 白名单/黑名单服务控制消息收发权限。
3
+ * - 白名单: 允许收发的 wxid 列表
4
+ * - 黑名单: 绝对禁止收发的 wxid 列表 (优先级高于白名单)
5
+ * - 审计日志: 记录每次发送尝试, 便于事后追溯
3
6
  */
4
- import { configService } from './configService.js'
7
+ import { join } from 'path'
8
+ import { homedir } from 'os'
9
+ import { appendFileSync, mkdirSync, existsSync, readFileSync, statSync } from 'fs'
10
+ import { configService, type ListEntry } from './configService.js'
5
11
  import { resolveTalker } from '../utils/talkerUtils.js'
6
12
 
7
- export interface WhitelistEntry {
8
- wxid: string
9
- displayName: string
13
+ const AUDIT_DIR = join(homedir(), '.weflow-cli')
14
+ const AUDIT_FILE = join(AUDIT_DIR, 'audit-send.log')
15
+
16
+ /** 单条文本消息最大字符数 (微信单条上限约 5000, 这里更保守) */
17
+ export const MAX_TEXT_LENGTH = 2000
18
+ /** 默认速率限制: 窗口内最大发送条数 */
19
+ export const DEFAULT_RATE_WINDOW_MS = 60_000
20
+ export const DEFAULT_RATE_MAX = 10
21
+
22
+ export interface SendAuditEntry {
23
+ timestamp: number
24
+ action: 'send'
25
+ targetWxid: string
26
+ targetName?: string
27
+ kind: 'text' | 'image' | 'file'
28
+ success: boolean
29
+ /** 消息预览 (前 80 字符) */
30
+ preview?: string
31
+ /** 失败原因 */
32
+ error?: string
10
33
  }
11
34
 
12
35
  export class WhitelistService {
36
+ // ====== 白名单 ======
37
+
13
38
  getList(): string[] {
14
39
  return configService.getWhitelist()
15
40
  }
16
41
 
42
+ /** 带昵称的白名单条目 */
43
+ getWhitelistEntries(): ListEntry[] {
44
+ return configService.getWhitelistEntries()
45
+ }
46
+
47
+ /** 查 wxid 对应的 displayName, 没有则返回 wxid */
48
+ lookupName(wxid: string): string {
49
+ const e = configService.getWhitelistEntries().find(x => x.wxid === wxid)
50
+ if (e?.displayName) return e.displayName
51
+ const b = configService.getBlacklistEntries().find(x => x.wxid === wxid)
52
+ return b?.displayName || wxid
53
+ }
54
+
17
55
  isAllowed(wxid: string): boolean {
56
+ // 黑名单优先: 即使在白名单也拒绝
57
+ if (this.isBlocked(wxid)) return false
18
58
  return configService.getWhitelist().includes(wxid)
19
59
  }
20
60
 
21
61
  /** 直接添加 wxid(不解析昵称),用于 CLI 确认后的最终写入 */
22
- addDirect(wxid: string): void {
23
- const list = configService.getWhitelist()
24
- if (!list.includes(wxid)) {
25
- list.push(wxid)
26
- configService.setWhitelist(list)
62
+ addDirect(wxid: string, displayName?: string): void {
63
+ const entries = configService.getWhitelistEntries()
64
+ if (!entries.some(e => e.wxid === wxid)) {
65
+ entries.push({ wxid, displayName, addedAt: Date.now() })
66
+ configService.setWhitelistEntries(entries)
67
+ } else if (displayName) {
68
+ // 已存在但补全 displayName
69
+ const idx = entries.findIndex(e => e.wxid === wxid)
70
+ if (!entries[idx].displayName) {
71
+ entries[idx].displayName = displayName
72
+ configService.setWhitelistEntries(entries)
73
+ }
27
74
  }
28
75
  }
29
76
 
30
- async add(target: string): Promise<{ success: boolean; error?: string; wxid?: string }> {
77
+ async add(target: string): Promise<{ success: boolean; error?: string; wxid?: string; displayName?: string }> {
31
78
  let wxid: string
79
+ let displayName: string | undefined
32
80
 
33
81
  // 已知格式直接使用,否则通过 resolveTalker 解析
34
82
  if (target.startsWith('wxid_') || target.includes('@chatroom') || target.includes('@openim')) {
@@ -41,31 +89,170 @@ export class WhitelistService {
41
89
  }
42
90
  }
43
91
 
44
- const list = configService.getWhitelist()
45
- if (list.includes(wxid)) {
92
+ // 黑名单中的目标禁止加入白名单
93
+ if (this.isBlocked(wxid)) {
94
+ return { success: false, error: `"${wxid}" 在黑名单中, 禁止加入白名单` }
95
+ }
96
+
97
+ const entries = configService.getWhitelistEntries()
98
+ if (entries.some(e => e.wxid === wxid)) {
46
99
  return { success: false, error: `"${wxid}" 已在白名单中` }
47
100
  }
48
101
 
49
- list.push(wxid)
50
- configService.setWhitelist(list)
51
- return { success: true, wxid }
102
+ entries.push({ wxid, displayName, addedAt: Date.now() })
103
+ configService.setWhitelistEntries(entries)
104
+ return { success: true, wxid, displayName }
52
105
  }
53
106
 
54
107
  remove(wxid: string): boolean {
55
- const list = configService.getWhitelist()
56
- const idx = list.indexOf(wxid)
108
+ const entries = configService.getWhitelistEntries()
109
+ const idx = entries.findIndex(e => e.wxid === wxid)
57
110
  if (idx < 0) return false
58
- list.splice(idx, 1)
59
- configService.setWhitelist(list)
111
+ entries.splice(idx, 1)
112
+ configService.setWhitelistEntries(entries)
60
113
  return true
61
114
  }
62
115
 
63
116
  clear(): void {
64
- configService.setWhitelist([])
117
+ configService.setWhitelistEntries([])
118
+ }
119
+
120
+ // ====== 黑名单 ======
121
+
122
+ getBlacklist(): string[] {
123
+ return configService.getBlacklist()
124
+ }
125
+
126
+ getBlacklistEntries(): ListEntry[] {
127
+ return configService.getBlacklistEntries()
128
+ }
129
+
130
+ isBlocked(wxid: string): boolean {
131
+ return configService.getBlacklist().includes(wxid)
132
+ }
133
+
134
+ /** 直接添加 wxid 到黑名单 */
135
+ blockDirect(wxid: string, displayName?: string, reason?: string): void {
136
+ const entries = configService.getBlacklistEntries()
137
+ if (!entries.some(e => e.wxid === wxid)) {
138
+ entries.push({ wxid, displayName, addedAt: Date.now(), reason })
139
+ configService.setBlacklistEntries(entries)
140
+ }
141
+ // 同步从白名单移除 (黑名单优先级最高)
142
+ if (configService.getWhitelist().includes(wxid)) {
143
+ this.remove(wxid)
144
+ }
145
+ }
146
+
147
+ async block(target: string, reason?: string): Promise<{ success: boolean; error?: string; wxid?: string; displayName?: string }> {
148
+ let wxid: string
149
+ let displayName: string | undefined
150
+ if (target.startsWith('wxid_') || target.includes('@chatroom') || target.includes('@openim')) {
151
+ wxid = target
152
+ } else {
153
+ try {
154
+ wxid = await resolveTalker(target)
155
+ } catch (e: any) {
156
+ return { success: false, error: `无法解析 "${target}": ${e.message}` }
157
+ }
158
+ }
159
+
160
+ const entries = configService.getBlacklistEntries()
161
+ if (entries.some(e => e.wxid === wxid)) {
162
+ return { success: false, error: `"${wxid}" 已在黑名单中` }
163
+ }
164
+
165
+ entries.push({ wxid, displayName, addedAt: Date.now(), reason })
166
+ configService.setBlacklistEntries(entries)
167
+ // 同步从白名单移除
168
+ if (configService.getWhitelist().includes(wxid)) {
169
+ this.remove(wxid)
170
+ }
171
+ return { success: true, wxid, displayName }
172
+ }
173
+
174
+ unblock(wxid: string): boolean {
175
+ const entries = configService.getBlacklistEntries()
176
+ const idx = entries.findIndex(e => e.wxid === wxid)
177
+ if (idx < 0) return false
178
+ entries.splice(idx, 1)
179
+ configService.setBlacklistEntries(entries)
180
+ return true
181
+ }
182
+
183
+ clearBlacklist(): void {
184
+ configService.setBlacklistEntries([])
185
+ }
186
+
187
+ // ====== 审计日志 ======
188
+
189
+ /** 追加一条发送审计记录 (JSON Lines) */
190
+ auditSend(entry: SendAuditEntry): void {
191
+ try {
192
+ if (!existsSync(AUDIT_DIR)) {
193
+ mkdirSync(AUDIT_DIR, { recursive: true })
194
+ }
195
+ appendFileSync(AUDIT_FILE, JSON.stringify(entry) + '\n', 'utf8')
196
+ } catch {
197
+ // 审计失败不影响主流程
198
+ }
199
+ }
200
+
201
+ /** 读取审计日志, 返回最近 limit 条 (默认全部) */
202
+ readAudit(limit?: number, filter?: (e: SendAuditEntry) => boolean): SendAuditEntry[] {
203
+ if (!existsSync(AUDIT_FILE)) return []
204
+ try {
205
+ const raw = readFileSync(AUDIT_FILE, 'utf8')
206
+ const lines = raw.split('\n').filter(Boolean)
207
+ let entries: SendAuditEntry[] = []
208
+ for (const line of lines) {
209
+ try {
210
+ entries.push(JSON.parse(line))
211
+ } catch {
212
+ // 跳过损坏行
213
+ }
214
+ }
215
+ if (filter) entries = entries.filter(filter)
216
+ // 倒序 (最新在前)
217
+ entries.reverse()
218
+ if (limit && limit > 0) entries = entries.slice(0, limit)
219
+ return entries
220
+ } catch {
221
+ return []
222
+ }
223
+ }
224
+
225
+ /** 审计日志文件大小 (字节) */
226
+ auditSize(): number {
227
+ try {
228
+ return existsSync(AUDIT_FILE) ? statSync(AUDIT_FILE).size : 0
229
+ } catch {
230
+ return 0
231
+ }
232
+ }
233
+
234
+ /**
235
+ * 速率限制检查: 指定窗口内成功发送次数是否超阈值。
236
+ * @returns { allowed, count, windowMs, max }
237
+ */
238
+ checkRateLimit(windowMs: number = DEFAULT_RATE_WINDOW_MS, max: number = DEFAULT_RATE_MAX): {
239
+ allowed: boolean
240
+ count: number
241
+ windowMs: number
242
+ max: number
243
+ } {
244
+ const since = Date.now() - windowMs
245
+ const recent = this.readAudit(undefined, e => e.success && e.timestamp >= since)
246
+ return {
247
+ allowed: recent.length < max,
248
+ count: recent.length,
249
+ windowMs,
250
+ max,
251
+ }
65
252
  }
66
253
 
67
254
  reload(): void {
68
- // configService 每次 getWhitelist 都从内存读取,无需主动 reload
255
+ // configService 每次读取都从内存读取,无需主动 reload
69
256
  }
70
257
  }
71
258
 
package/src/types.ts CHANGED
@@ -83,6 +83,12 @@ export interface WechatLoginSession {
83
83
  export interface WechatInboundMessage {
84
84
  messageId: string
85
85
  fromUserId: string
86
+ /** Reply destination. A group ID is used only when the upstream explicitly provides one. */
87
+ conversationId: string
88
+ conversationType: 'direct' | 'group'
89
+ /** Person who sent the message; group messages without this identifier are denied. */
90
+ senderId: string
91
+ mentionedBot: boolean
86
92
  senderNickname: string
87
93
  timestamp: number
88
94
  timestampMs: number
@@ -138,4 +144,18 @@ export interface ConfigData {
138
144
  contactKey?: string
139
145
  /** NT 格式: contact.db 32位十六进制盐值 */
140
146
  contactSalt?: string
147
+ /** NT 格式: sns.db 路径 */
148
+ snsDbPath?: string
149
+ /** NT 格式: sns.db 64位十六进制密钥 */
150
+ snsKey?: string
151
+ /** NT 格式: sns.db 32位十六进制盐值 */
152
+ snsSalt?: string
153
+ /** NT 格式: favorite.db 路径 */
154
+ favDbPath?: string
155
+ /** NT 格式: favorite.db 64位十六进制密钥 (PBKDF2 派生 raw key) */
156
+ favKey?: string
157
+ /** NT 格式: 4.x 全库共用 passphrase 密钥 (64位hex, 用于派生各库 raw key) */
158
+ favPassphrase?: string
159
+ /** 账号级 seed,用于解密本地缓存里的自定义表情包 */
160
+ emoticonSeed?: string
141
161
  }
@@ -0,0 +1,58 @@
1
+ export class DateRangeError extends Error {
2
+ constructor(public readonly code: string, message: string) {
3
+ super(message)
4
+ }
5
+ }
6
+
7
+ export function parseLocalDateOrIso(value: string | undefined, endOfDay = false): number | undefined {
8
+ if (!value) return undefined
9
+ let timestamp: number
10
+ const match = value.match(/^(\d{4})-(\d{2})-(\d{2})$/)
11
+ if (match) {
12
+ const [, year, month, day] = match
13
+ const date = new Date(
14
+ Number(year),
15
+ Number(month) - 1,
16
+ Number(day),
17
+ endOfDay ? 23 : 0,
18
+ endOfDay ? 59 : 0,
19
+ endOfDay ? 59 : 0,
20
+ endOfDay ? 999 : 0,
21
+ )
22
+ timestamp = date.getFullYear() === Number(year) &&
23
+ date.getMonth() === Number(month) - 1 &&
24
+ date.getDate() === Number(day)
25
+ ? date.getTime()
26
+ : Number.NaN
27
+ } else {
28
+ timestamp = Date.parse(value)
29
+ }
30
+ if (Number.isNaN(timestamp)) throw new DateRangeError('INVALID_DATE', '导出日期无效')
31
+ return Math.floor(timestamp / 1000)
32
+ }
33
+
34
+ export function resolveExportDateRange(options: {
35
+ date?: string
36
+ from?: string
37
+ to?: string
38
+ preserveDateForRichHtml?: boolean
39
+ }): { from?: number; to?: number } {
40
+ if (options.date && (options.from || options.to)) {
41
+ throw new DateRangeError('CONFLICTING_DATE_FILTERS', '--date 不能与 --from/--to 同时使用')
42
+ }
43
+
44
+ let from = parseLocalDateOrIso(options.from)
45
+ let to = parseLocalDateOrIso(options.to, true)
46
+ if (options.date) {
47
+ const dateStart = parseLocalDateOrIso(options.date)
48
+ const dateEnd = parseLocalDateOrIso(options.date, true)
49
+ if (!options.preserveDateForRichHtml) {
50
+ from = dateStart
51
+ to = dateEnd
52
+ }
53
+ }
54
+ if (from !== undefined && to !== undefined && from > to) {
55
+ throw new DateRangeError('INVALID_DATE_RANGE', '起始日期不能晚于结束日期')
56
+ }
57
+ return { from, to }
58
+ }
@@ -0,0 +1,39 @@
1
+ import { readFileSync, statSync } from 'fs'
2
+ import { isAbsolute, relative, resolve } from 'path'
3
+
4
+ export function safeChildPath(root: string, child: string): string | null {
5
+ const candidate = resolve(root, child)
6
+ const rel = relative(resolve(root), candidate)
7
+ return rel && !rel.startsWith('..') && !isAbsolute(rel) ? candidate : null
8
+ }
9
+
10
+ export function safeDate(value: unknown): string | null {
11
+ const date = String(value || '')
12
+ return /^\d{4}-\d{2}-\d{2}$/.test(date) ? date : null
13
+ }
14
+
15
+ export function isAllowedWeChatArticleUrl(value: unknown): boolean {
16
+ try {
17
+ const url = new URL(String(value || ''))
18
+ return url.protocol === 'https:' &&
19
+ url.hostname === 'mp.weixin.qq.com' &&
20
+ !url.username &&
21
+ !url.password
22
+ } catch {
23
+ return false
24
+ }
25
+ }
26
+
27
+ export function isCoverImage(path: string): boolean {
28
+ try {
29
+ const stat = statSync(path)
30
+ if (!stat.isFile() || stat.size > 10 * 1024 * 1024) return false
31
+ const head = readFileSync(path).subarray(0, 12)
32
+ return (head.length >= 8 && head.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) ||
33
+ (head.length >= 3 && head.subarray(0, 3).equals(Buffer.from([255, 216, 255]))) ||
34
+ (head.length >= 6 && (head.subarray(0, 6).toString() === 'GIF87a' || head.subarray(0, 6).toString() === 'GIF89a')) ||
35
+ (head.length >= 12 && head.subarray(0, 4).toString() === 'RIFF' && head.subarray(8, 12).toString() === 'WEBP')
36
+ } catch {
37
+ return false
38
+ }
39
+ }
@@ -0,0 +1,14 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { dirname, join } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ export function resolvePackageRoot(moduleUrl: string): string {
6
+ let current = dirname(fileURLToPath(moduleUrl))
7
+ for (let depth = 0; depth < 6; depth++) {
8
+ if (existsSync(join(current, 'package.json')) && existsSync(join(current, 'scripts'))) return current
9
+ const parent = dirname(current)
10
+ if (parent === current) break
11
+ current = parent
12
+ }
13
+ throw new Error('Unable to locate the WeFlow CLI package root')
14
+ }
@@ -0,0 +1,73 @@
1
+ import { execFileSync } from 'child_process'
2
+ import { delimiter, join } from 'path'
3
+ import { accessSync, constants } from 'fs'
4
+
5
+ let cached: string | null = null
6
+
7
+ const EXE_NAMES = process.platform === 'win32'
8
+ ? ['python.exe', 'python3.exe', 'py.exe']
9
+ : ['python3', 'python']
10
+
11
+ function isExecutable(path: string): boolean {
12
+ try {
13
+ accessSync(path, constants.X_OK)
14
+ return true
15
+ } catch {
16
+ return false
17
+ }
18
+ }
19
+
20
+ /** 按顺序返回 PATH 中所有可用的 Python 解释器(含命令名候选) */
21
+ function candidates(): string[] {
22
+ const found: string[] = []
23
+ const dirs = (process.env.PATH || '').split(delimiter).filter(Boolean)
24
+ for (const dir of dirs) {
25
+ for (const name of EXE_NAMES) {
26
+ const full = join(dir, name)
27
+ if (isExecutable(full) && !found.includes(full)) {
28
+ found.push(full)
29
+ }
30
+ }
31
+ }
32
+ // 兜底:直接用命令名(依赖 PATH 解析,可能命中与上面相同的解释器)
33
+ for (const name of process.platform === 'win32' ? ['python', 'python3', 'py'] : ['python3', 'python']) {
34
+ if (!found.includes(name)) found.push(name)
35
+ }
36
+ return found
37
+ }
38
+
39
+ function canRun(cmd: string): boolean {
40
+ try {
41
+ execFileSync(cmd, ['--version'], { stdio: 'ignore', timeout: 5000 })
42
+ return true
43
+ } catch {
44
+ return false
45
+ }
46
+ }
47
+
48
+ function hasSqlcipher(cmd: string): boolean {
49
+ try {
50
+ execFileSync(cmd, ['-c', 'import sqlcipher3'], { stdio: 'ignore', timeout: 10000 })
51
+ return true
52
+ } catch {
53
+ return false
54
+ }
55
+ }
56
+
57
+ function detect(): string {
58
+ // 优先选择能 import sqlcipher3 的解释器(NT 解密核心依赖),
59
+ // 避免命中 PATH 中仅有基础库的裸 Python 环境(如被 IDE 注入的 VM Python 抢占)。
60
+ const list = candidates()
61
+ for (const cmd of list) {
62
+ if (canRun(cmd) && hasSqlcipher(cmd)) return cmd
63
+ }
64
+ for (const cmd of list) {
65
+ if (canRun(cmd)) return cmd
66
+ }
67
+ return 'python'
68
+ }
69
+
70
+ export function getPythonCommand(): string {
71
+ cached ??= detect()
72
+ return cached
73
+ }
@@ -0,0 +1,45 @@
1
+ const INTERNAL_SECRET_KEYS = [
2
+ 'WEFLOW_DB_PATH',
3
+ 'WEFLOW_NT_KEY',
4
+ 'WEFLOW_NT_SALT',
5
+ 'WEFLOW_CONTACT_DB',
6
+ 'WEFLOW_CONTACT_KEY',
7
+ 'WEFLOW_CONTACT_SALT',
8
+ 'WEFLOW_TALKER',
9
+ 'WEFLOW_OWN_WXID',
10
+ 'WEFLOW_NT_PASSPHRASE',
11
+ 'WEFLOW_VAULT_QUESTION',
12
+ 'WEFLOW_SEARCH_QUERY',
13
+ 'WEFLOW_RAG_QUESTION',
14
+ 'WEFLOW_RAG_TALKER',
15
+ 'WEFLOW_REPORT_TALKERS',
16
+ 'WEFLOW_SCAN_ROOT',
17
+ 'WEFLOW_QUERY_KEYWORD',
18
+ 'WEFLOW_QUERY_USERNAMES',
19
+ 'WEFLOW_EXPORT_NAME',
20
+ 'WEFLOW_EXPORT_OUTPUT',
21
+ 'WEFLOW_EXPORT_CACHE_DIR',
22
+ 'WEFLOW_EXPORT_ACCOUNT_DIR',
23
+ 'WEFLOW_EXPORT_DATE',
24
+ ] as const
25
+
26
+ export function createPythonProcessEnv(
27
+ overrides: Record<string, string | undefined> = {},
28
+ ): NodeJS.ProcessEnv {
29
+ const env: NodeJS.ProcessEnv = { ...process.env, PYTHONIOENCODING: 'utf-8' }
30
+ for (const key of INTERNAL_SECRET_KEYS) delete env[key]
31
+ for (const [key, value] of Object.entries(overrides)) {
32
+ if (value) env[key] = value
33
+ }
34
+ return env
35
+ }
36
+
37
+ export function safeSubprocessError(error: unknown, label = '子进程执行失败'): string {
38
+ const value = error as { code?: unknown; killed?: boolean; signal?: unknown }
39
+ if (value?.code === 'ENOENT') return `${label}: 运行时或命令不可用`
40
+ if (value?.code === 'ETIMEDOUT' || value?.killed || value?.signal) return `${label}: 执行超时或被终止`
41
+ if (typeof value?.code === 'number' || /^\d+$/.test(String(value?.code || ''))) {
42
+ return `${label} (exit ${String(value.code)})`
43
+ }
44
+ return label
45
+ }
@@ -13,7 +13,11 @@ import type { ChatSession } from '../types.js'
13
13
  *
14
14
  * @throws {Error} 当输入无法解析到任何会话时
15
15
  */
16
- export async function resolveTalker(input: string): Promise<string> {
16
+ export interface ResolveTalkerOptions {
17
+ interactive?: boolean
18
+ }
19
+
20
+ export async function resolveTalker(input: string, options: ResolveTalkerOptions = {}): Promise<string> {
17
21
  // 1. 已知格式直接返回
18
22
  if (input.startsWith('wxid_') || input.includes('@chatroom') || input.includes('@openim')) {
19
23
  return input
@@ -48,7 +52,15 @@ export async function resolveTalker(input: string): Promise<string> {
48
52
  return sessions[0].username
49
53
  }
50
54
 
51
- // 多个匹配 交互选择
55
+ if (options.interactive === false) {
56
+ const candidates = sessions.slice(0, 20).map(session => ({
57
+ username: session.username,
58
+ displayName: session.displayName || '',
59
+ }))
60
+ throw new Error(`会话名称不唯一,请使用会话 ID。候选: ${JSON.stringify(candidates)}`)
61
+ }
62
+
63
+ // 多个匹配 - 交互选择
52
64
  const choices = sessions.slice(0, 20).map(s => ({
53
65
  name: `${s.displayName} (${s.username})`,
54
66
  value: s.username,
@@ -1,25 +0,0 @@
1
- /**
2
- * WeflowError - weflow-cli 统一错误类
3
- *
4
- * 提供结构化的错误信息,包含:
5
- * - message: 人类可读的错误描述
6
- * - code: 错误代码(用于程序化判断)
7
- * - exitCode: 进程退出码
8
- */
9
- export declare class WeflowError extends Error {
10
- code: string;
11
- exitCode: number;
12
- constructor(message: string, code?: string, exitCode?: number);
13
- }
14
- /**
15
- * 统一错误处理函数。
16
- *
17
- * 根据错误类型输出对应的错误信息并以合适的退出码退出进程:
18
- * - WeflowError: 使用其自带的 exitCode
19
- * - 普通 Error: 视为未预期错误,退出码 1
20
- * - 其他: 视为未知错误,退出码 1
21
- *
22
- * @param e 捕获到的错误对象
23
- */
24
- export declare function handleError(e: unknown): void;
25
- //# sourceMappingURL=errors.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qBAAa,WAAY,SAAQ,KAAK;IAG3B,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,MAAM;gBAFvB,OAAO,EAAE,MAAM,EACR,IAAI,GAAE,MAAkB,EACxB,QAAQ,GAAE,MAAU;CAK9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAW5C"}
@@ -1,43 +0,0 @@
1
- /**
2
- * WeflowError - weflow-cli 统一错误类
3
- *
4
- * 提供结构化的错误信息,包含:
5
- * - message: 人类可读的错误描述
6
- * - code: 错误代码(用于程序化判断)
7
- * - exitCode: 进程退出码
8
- */
9
- export class WeflowError extends Error {
10
- code;
11
- exitCode;
12
- constructor(message, code = 'UNKNOWN', exitCode = 1) {
13
- super(message);
14
- this.code = code;
15
- this.exitCode = exitCode;
16
- this.name = 'WeflowError';
17
- }
18
- }
19
- /**
20
- * 统一错误处理函数。
21
- *
22
- * 根据错误类型输出对应的错误信息并以合适的退出码退出进程:
23
- * - WeflowError: 使用其自带的 exitCode
24
- * - 普通 Error: 视为未预期错误,退出码 1
25
- * - 其他: 视为未知错误,退出码 1
26
- *
27
- * @param e 捕获到的错误对象
28
- */
29
- export function handleError(e) {
30
- if (e instanceof WeflowError) {
31
- console.error(`[weflow-cli] ${e.message}`);
32
- process.exit(e.exitCode);
33
- }
34
- else if (e instanceof Error) {
35
- console.error(`[weflow-cli] 未预期的错误: ${e.message}`);
36
- process.exit(1);
37
- }
38
- else {
39
- console.error('[weflow-cli] 未知错误');
40
- process.exit(1);
41
- }
42
- }
43
- //# sourceMappingURL=errors.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,OAAO,WAAY,SAAQ,KAAK;IAG3B;IACA;IAHT,YACE,OAAe,EACR,OAAe,SAAS,EACxB,WAAmB,CAAC;QAE3B,KAAK,CAAC,OAAO,CAAC,CAAA;QAHP,SAAI,GAAJ,IAAI,CAAoB;QACxB,aAAQ,GAAR,QAAQ,CAAY;QAG3B,IAAI,CAAC,IAAI,GAAG,aAAa,CAAA;IAC3B,CAAC;CACF;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,CAAU;IACpC,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;QAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;IAC1B,CAAC;SAAM,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;QAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;QAClC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC"}
@@ -1,27 +0,0 @@
1
- export interface PythonScriptResult {
2
- stdout: string;
3
- stderr: string;
4
- exitCode: number;
5
- }
6
- export interface PythonScriptError extends Error {
7
- script: string;
8
- stderr: string;
9
- exitCode: number;
10
- }
11
- /** 获取项目根目录 */
12
- export declare function getProjectRoot(): string;
13
- /**
14
- * 运行 Python 脚本,统一封装路径解析、参数序列化、错误处理。
15
- *
16
- * @param scriptName 脚本文件名(如 'biz_daily.py'),相对于 scripts/ 目录
17
- * @param args 命令行参数数组
18
- * @param options 可选配置
19
- * @returns 脚本执行结果
20
- * @throws PythonScriptError 当脚本执行失败时
21
- */
22
- export declare function runPythonScript(scriptName: string, args?: string[], options?: {
23
- cwd?: string;
24
- timeout?: number;
25
- env?: Record<string, string>;
26
- }): Promise<PythonScriptResult>;
27
- //# sourceMappingURL=pythonRunner.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pythonRunner.d.ts","sourceRoot":"","sources":["../../../src/utils/pythonRunner.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,iBAAkB,SAAQ,KAAK;IAC9C,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,cAAc;AACd,wBAAgB,cAAc,IAAI,MAAM,CAcvC;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,MAAM,EAAO,EACnB,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAO,GAC7E,OAAO,CAAC,kBAAkB,CAAC,CAwB7B"}