@jackwener/opencli 1.6.0 → 1.6.2

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 (390) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +27 -45
  4. package/README.zh-CN.md +32 -34
  5. package/autoresearch/browse-tasks.json +18 -20
  6. package/autoresearch/commands/debug.ts +163 -0
  7. package/autoresearch/commands/fix.ts +145 -0
  8. package/autoresearch/commands/plan.ts +88 -0
  9. package/autoresearch/commands/run.ts +138 -0
  10. package/autoresearch/config.ts +82 -0
  11. package/autoresearch/engine.ts +359 -0
  12. package/autoresearch/eval-all.ts +127 -0
  13. package/autoresearch/eval-browse.ts +1 -1
  14. package/autoresearch/eval-publish.ts +238 -0
  15. package/autoresearch/eval-save.ts +249 -0
  16. package/autoresearch/eval-skill.ts +14 -8
  17. package/autoresearch/eval-v2ex.ts +220 -0
  18. package/autoresearch/eval-zhihu.ts +230 -0
  19. package/autoresearch/logger.ts +69 -0
  20. package/autoresearch/presets/combined-reliability.ts +27 -0
  21. package/autoresearch/presets/index.ts +23 -0
  22. package/autoresearch/presets/operate-reliability.ts +24 -0
  23. package/autoresearch/presets/save-reliability.ts +26 -0
  24. package/autoresearch/presets/skill-quality.ts +20 -0
  25. package/autoresearch/presets/v2ex-reliability.ts +24 -0
  26. package/autoresearch/presets/zhihu-reliability.ts +25 -0
  27. package/autoresearch/publish-tasks.json +345 -0
  28. package/autoresearch/run-save.sh +11 -0
  29. package/autoresearch/save-adapters/xhs-explore-deep.ts +64 -0
  30. package/autoresearch/save-adapters/xhs-note-comments.ts +61 -0
  31. package/autoresearch/save-adapters/xhs-search-full.ts +62 -0
  32. package/autoresearch/save-adapters/zhihu-hot-detail.ts +52 -0
  33. package/autoresearch/save-adapters/zhihu-question-full.ts +57 -0
  34. package/autoresearch/save-adapters/zhihu-search-detail.ts +53 -0
  35. package/autoresearch/save-tasks.json +281 -0
  36. package/autoresearch/v2ex-tasks.json +899 -0
  37. package/autoresearch/zhihu-tasks.json +848 -0
  38. package/bun.lock +615 -0
  39. package/dist/browser/base-page.d.ts +4 -2
  40. package/dist/browser/base-page.js +37 -4
  41. package/dist/browser/bridge.js +10 -8
  42. package/dist/browser/cdp.js +2 -6
  43. package/dist/browser/daemon-client.d.ts +11 -1
  44. package/dist/browser/daemon-client.js +3 -0
  45. package/dist/browser/dom-helpers.d.ts +4 -2
  46. package/dist/browser/dom-helpers.js +42 -31
  47. package/dist/browser/dom-snapshot.js +23 -1
  48. package/dist/browser/page.d.ts +7 -2
  49. package/dist/browser/page.js +112 -30
  50. package/dist/browser.test.js +1 -1
  51. package/dist/build-manifest.d.ts +1 -0
  52. package/dist/build-manifest.js +1 -0
  53. package/dist/cli-manifest.json +1133 -182
  54. package/dist/cli.d.ts +2 -0
  55. package/dist/cli.js +48 -7
  56. package/dist/cli.test.d.ts +1 -0
  57. package/dist/cli.test.js +88 -0
  58. package/dist/clis/1688/item.d.ts +70 -0
  59. package/dist/clis/1688/item.js +187 -0
  60. package/dist/clis/1688/item.test.d.ts +1 -0
  61. package/dist/clis/1688/item.test.js +67 -0
  62. package/dist/clis/1688/search.d.ts +56 -0
  63. package/dist/clis/1688/search.js +309 -0
  64. package/dist/clis/1688/search.test.d.ts +1 -0
  65. package/dist/clis/1688/search.test.js +75 -0
  66. package/dist/clis/1688/shared.d.ts +112 -0
  67. package/dist/clis/1688/shared.js +514 -0
  68. package/dist/clis/1688/shared.test.d.ts +1 -0
  69. package/dist/clis/1688/shared.test.js +57 -0
  70. package/dist/clis/1688/store.d.ts +45 -0
  71. package/dist/clis/1688/store.js +226 -0
  72. package/dist/clis/1688/store.test.d.ts +1 -0
  73. package/dist/clis/1688/store.test.js +62 -0
  74. package/dist/clis/amazon/bestsellers.d.ts +0 -20
  75. package/dist/clis/amazon/bestsellers.js +6 -129
  76. package/dist/clis/amazon/bestsellers.test.js +12 -3
  77. package/dist/clis/amazon/movers-shakers.d.ts +1 -0
  78. package/dist/clis/amazon/movers-shakers.js +7 -0
  79. package/dist/clis/amazon/new-releases.d.ts +1 -0
  80. package/dist/clis/amazon/new-releases.js +7 -0
  81. package/dist/clis/amazon/rankings.d.ts +59 -0
  82. package/dist/clis/amazon/rankings.js +226 -0
  83. package/dist/clis/amazon/rankings.test.d.ts +1 -0
  84. package/dist/clis/amazon/rankings.test.js +41 -0
  85. package/dist/clis/amazon/shared.d.ts +11 -0
  86. package/dist/clis/amazon/shared.js +121 -11
  87. package/dist/clis/amazon/shared.test.js +11 -0
  88. package/dist/clis/bilibili/comments.js +2 -2
  89. package/dist/clis/bilibili/comments.test.js +3 -2
  90. package/dist/clis/bilibili/download.js +2 -1
  91. package/dist/clis/bilibili/subtitle.js +4 -3
  92. package/dist/clis/bilibili/subtitle.test.js +2 -1
  93. package/dist/clis/bilibili/utils.d.ts +5 -0
  94. package/dist/clis/bilibili/utils.js +30 -0
  95. package/dist/clis/bilibili/utils.test.d.ts +1 -0
  96. package/dist/clis/bilibili/utils.test.js +17 -0
  97. package/dist/clis/douban/marks.js +1 -1
  98. package/dist/clis/douban/subject.yaml +50 -19
  99. package/dist/clis/doubao/utils.js +32 -12
  100. package/dist/clis/douyin/_shared/browser-fetch.test.js +0 -1
  101. package/dist/clis/douyin/_shared/transcode.test.js +0 -2
  102. package/dist/clis/douyin/draft.test.js +0 -2
  103. package/dist/clis/facebook/search.test.js +0 -2
  104. package/dist/clis/gemini/ask.js +9 -3
  105. package/dist/clis/gemini/ask.test.d.ts +1 -0
  106. package/dist/clis/gemini/ask.test.js +100 -0
  107. package/dist/clis/gemini/reply-state.test.d.ts +1 -0
  108. package/dist/clis/gemini/reply-state.test.js +641 -0
  109. package/dist/clis/gemini/utils.d.ts +44 -1
  110. package/dist/clis/gemini/utils.js +528 -61
  111. package/dist/clis/gemini/utils.test.js +149 -2
  112. package/dist/clis/hupu/detail.d.ts +1 -0
  113. package/dist/clis/hupu/detail.js +72 -0
  114. package/dist/clis/hupu/hot.yaml +43 -0
  115. package/dist/clis/hupu/like.d.ts +1 -0
  116. package/dist/clis/hupu/like.js +75 -0
  117. package/dist/clis/hupu/reply.d.ts +1 -0
  118. package/dist/clis/hupu/reply.js +71 -0
  119. package/dist/clis/hupu/search.d.ts +1 -0
  120. package/dist/clis/hupu/search.js +59 -0
  121. package/dist/clis/hupu/unlike.d.ts +1 -0
  122. package/dist/clis/hupu/unlike.js +75 -0
  123. package/dist/clis/hupu/utils.d.ts +20 -0
  124. package/dist/clis/hupu/utils.js +319 -0
  125. package/dist/clis/instagram/_shared/private-publish.d.ts +138 -0
  126. package/dist/clis/instagram/_shared/private-publish.js +1030 -0
  127. package/dist/clis/instagram/_shared/private-publish.test.d.ts +1 -0
  128. package/dist/clis/instagram/_shared/private-publish.test.js +705 -0
  129. package/dist/clis/instagram/_shared/protocol-capture.d.ts +26 -0
  130. package/dist/clis/instagram/_shared/protocol-capture.js +282 -0
  131. package/dist/clis/instagram/_shared/protocol-capture.test.d.ts +1 -0
  132. package/dist/clis/instagram/_shared/protocol-capture.test.js +114 -0
  133. package/dist/clis/instagram/_shared/runtime-info.d.ts +9 -0
  134. package/dist/clis/instagram/_shared/runtime-info.js +81 -0
  135. package/dist/clis/instagram/note.d.ts +1 -0
  136. package/dist/clis/instagram/note.js +222 -0
  137. package/dist/clis/instagram/note.test.d.ts +1 -0
  138. package/dist/clis/instagram/note.test.js +81 -0
  139. package/dist/clis/instagram/post.d.ts +4 -0
  140. package/dist/clis/instagram/post.js +1496 -0
  141. package/dist/clis/instagram/post.test.d.ts +1 -0
  142. package/dist/clis/instagram/post.test.js +1647 -0
  143. package/dist/clis/instagram/reel.d.ts +1 -0
  144. package/dist/clis/instagram/reel.js +826 -0
  145. package/dist/clis/instagram/reel.test.d.ts +1 -0
  146. package/dist/clis/instagram/reel.test.js +167 -0
  147. package/dist/clis/instagram/story.d.ts +1 -0
  148. package/dist/clis/instagram/story.js +115 -0
  149. package/dist/clis/instagram/story.test.d.ts +1 -0
  150. package/dist/clis/instagram/story.test.js +167 -0
  151. package/dist/clis/sinafinance/stock-rank.d.ts +4 -0
  152. package/dist/clis/sinafinance/stock-rank.js +65 -0
  153. package/dist/clis/substack/utils.test.js +0 -2
  154. package/dist/clis/twitter/post.js +72 -45
  155. package/dist/clis/twitter/post.test.d.ts +1 -0
  156. package/dist/clis/twitter/post.test.js +116 -0
  157. package/dist/clis/twitter/reply.d.ts +12 -0
  158. package/dist/clis/twitter/reply.js +257 -35
  159. package/dist/clis/twitter/reply.test.d.ts +1 -0
  160. package/dist/clis/twitter/reply.test.js +151 -0
  161. package/dist/clis/twitter/search.js +67 -5
  162. package/dist/clis/twitter/search.test.js +83 -5
  163. package/dist/clis/xianyu/chat.d.ts +7 -0
  164. package/dist/clis/xianyu/chat.js +146 -0
  165. package/dist/clis/xianyu/chat.test.d.ts +1 -0
  166. package/dist/clis/xianyu/chat.test.js +15 -0
  167. package/dist/clis/xianyu/item.d.ts +7 -0
  168. package/dist/clis/xianyu/item.js +152 -0
  169. package/dist/clis/xianyu/item.test.d.ts +1 -0
  170. package/dist/clis/xianyu/item.test.js +56 -0
  171. package/dist/clis/xianyu/search.d.ts +10 -0
  172. package/dist/clis/xianyu/search.js +134 -0
  173. package/dist/clis/xianyu/search.test.d.ts +1 -0
  174. package/dist/clis/xianyu/search.test.js +17 -0
  175. package/dist/clis/xianyu/utils.d.ts +1 -0
  176. package/dist/clis/xianyu/utils.js +8 -0
  177. package/dist/clis/xiaoe/catalog.yaml +129 -0
  178. package/dist/clis/xiaoe/content.yaml +43 -0
  179. package/dist/clis/xiaoe/courses.yaml +73 -0
  180. package/dist/clis/xiaoe/detail.yaml +39 -0
  181. package/dist/clis/xiaoe/play-url.yaml +124 -0
  182. package/dist/clis/xiaohongshu/comments.test.js +0 -2
  183. package/dist/clis/xiaohongshu/creator-note-detail.test.js +0 -2
  184. package/dist/clis/xiaohongshu/creator-notes.test.js +0 -2
  185. package/dist/clis/xiaohongshu/download.test.js +0 -2
  186. package/dist/clis/xiaohongshu/note.test.js +0 -2
  187. package/dist/clis/xiaohongshu/publish.test.js +0 -2
  188. package/dist/clis/xiaohongshu/search.js +29 -20
  189. package/dist/clis/xiaohongshu/search.test.js +56 -48
  190. package/dist/clis/yuanbao/ask.d.ts +21 -0
  191. package/dist/clis/yuanbao/ask.js +427 -0
  192. package/dist/clis/yuanbao/ask.test.d.ts +1 -0
  193. package/dist/clis/yuanbao/ask.test.js +124 -0
  194. package/dist/clis/yuanbao/new.d.ts +1 -0
  195. package/dist/clis/yuanbao/new.js +70 -0
  196. package/dist/clis/yuanbao/new.test.d.ts +1 -0
  197. package/dist/clis/yuanbao/new.test.js +30 -0
  198. package/dist/clis/yuanbao/shared.d.ts +13 -0
  199. package/dist/clis/yuanbao/shared.js +49 -0
  200. package/dist/clis/zhihu/question.js +30 -19
  201. package/dist/clis/zhihu/question.test.js +34 -16
  202. package/dist/commanderAdapter.js +8 -4
  203. package/dist/commanderAdapter.test.js +42 -0
  204. package/dist/completion.js +3 -1
  205. package/dist/completion.test.d.ts +1 -0
  206. package/dist/completion.test.js +23 -0
  207. package/dist/doctor.js +1 -1
  208. package/dist/electron-apps.d.ts +2 -0
  209. package/dist/electron-apps.js +7 -1
  210. package/dist/errors.js +1 -1
  211. package/dist/execution.js +25 -35
  212. package/dist/explore.js +1 -1
  213. package/dist/launcher.d.ts +4 -0
  214. package/dist/launcher.js +64 -8
  215. package/dist/launcher.test.js +88 -7
  216. package/dist/output.d.ts +2 -0
  217. package/dist/output.js +10 -1
  218. package/dist/output.test.d.ts +0 -3
  219. package/dist/output.test.js +59 -92
  220. package/dist/pipeline/executor.test.js +0 -2
  221. package/dist/pipeline/steps/download.test.js +0 -2
  222. package/dist/registry.d.ts +2 -0
  223. package/dist/serialization.d.ts +1 -0
  224. package/dist/serialization.js +1 -0
  225. package/dist/types.d.ts +9 -2
  226. package/docs/.vitepress/config.mts +4 -0
  227. package/docs/adapters/browser/1688.md +52 -0
  228. package/docs/adapters/browser/36kr.md +2 -1
  229. package/docs/adapters/browser/doubao.md +5 -1
  230. package/docs/adapters/browser/hupu.md +53 -0
  231. package/docs/adapters/browser/sinafinance.md +32 -2
  232. package/docs/adapters/browser/weibo.md +6 -1
  233. package/docs/adapters/browser/wikipedia.md +2 -0
  234. package/docs/adapters/browser/xianyu.md +42 -0
  235. package/docs/adapters/browser/xiaoe.md +44 -0
  236. package/docs/adapters/browser/yuanbao.md +64 -0
  237. package/docs/adapters/index.md +14 -5
  238. package/docs/comparison.md +1 -1
  239. package/docs/developer/ai-workflow.md +2 -2
  240. package/docs/developer/contributing.md +1 -1
  241. package/docs/developer/testing.md +2 -0
  242. package/docs/guide/plugins.md +1 -0
  243. package/docs/guide/troubleshooting.md +11 -0
  244. package/docs/superpowers/specs/2026-04-03-v2ex-autoresearch-design.md +41 -0
  245. package/docs/zh/guide/plugins.md +1 -0
  246. package/extension/dist/background.js +1127 -0
  247. package/extension/src/background.test.ts +39 -0
  248. package/extension/src/background.ts +223 -34
  249. package/extension/src/cdp.ts +194 -4
  250. package/extension/src/protocol.ts +22 -1
  251. package/package.json +3 -2
  252. package/scripts/postinstall.js +1 -1
  253. package/skills/opencli-explorer/SKILL.md +1 -1
  254. package/skills/opencli-oneshot/SKILL.md +2 -2
  255. package/skills/opencli-operate/SKILL.md +120 -27
  256. package/skills/opencli-usage/SKILL.md +31 -20
  257. package/skills/opencli-usage/browser.md +114 -16
  258. package/skills/opencli-usage/public-api.md +32 -3
  259. package/skills/smart-search/SKILL.md +156 -0
  260. package/skills/smart-search/references/sources-ai.md +74 -0
  261. package/skills/smart-search/references/sources-info.md +43 -0
  262. package/skills/smart-search/references/sources-media.md +50 -0
  263. package/skills/smart-search/references/sources-other.md +42 -0
  264. package/skills/smart-search/references/sources-shopping.md +31 -0
  265. package/skills/smart-search/references/sources-social.md +51 -0
  266. package/skills/smart-search/references/sources-tech.md +42 -0
  267. package/skills/smart-search/references/sources-travel.md +20 -0
  268. package/src/browser/base-page.ts +41 -6
  269. package/src/browser/bridge.ts +11 -8
  270. package/src/browser/cdp.ts +1 -8
  271. package/src/browser/daemon-client.ts +11 -1
  272. package/src/browser/dom-helpers.ts +43 -31
  273. package/src/browser/dom-snapshot.ts +23 -1
  274. package/src/browser/page.ts +115 -31
  275. package/src/browser.test.ts +1 -1
  276. package/src/build-manifest.ts +2 -0
  277. package/src/cli.test.ts +133 -0
  278. package/src/cli.ts +73 -11
  279. package/src/clis/1688/item.test.ts +69 -0
  280. package/src/clis/1688/item.ts +282 -0
  281. package/src/clis/1688/search.test.ts +81 -0
  282. package/src/clis/1688/search.ts +402 -0
  283. package/src/clis/1688/shared.test.ts +75 -0
  284. package/src/clis/1688/shared.ts +623 -0
  285. package/src/clis/1688/store.test.ts +69 -0
  286. package/src/clis/1688/store.ts +300 -0
  287. package/src/clis/amazon/bestsellers.test.ts +12 -3
  288. package/src/clis/amazon/bestsellers.ts +6 -178
  289. package/src/clis/amazon/movers-shakers.ts +8 -0
  290. package/src/clis/amazon/new-releases.ts +8 -0
  291. package/src/clis/amazon/rankings.test.ts +47 -0
  292. package/src/clis/amazon/rankings.ts +312 -0
  293. package/src/clis/amazon/shared.test.ts +16 -0
  294. package/src/clis/amazon/shared.ts +134 -12
  295. package/src/clis/bilibili/comments.test.ts +4 -3
  296. package/src/clis/bilibili/comments.ts +2 -2
  297. package/src/clis/bilibili/download.ts +2 -1
  298. package/src/clis/bilibili/subtitle.test.ts +2 -1
  299. package/src/clis/bilibili/subtitle.ts +4 -3
  300. package/src/clis/bilibili/utils.test.ts +21 -0
  301. package/src/clis/bilibili/utils.ts +27 -0
  302. package/src/clis/douban/marks.ts +1 -1
  303. package/src/clis/douban/subject.yaml +50 -19
  304. package/src/clis/doubao/utils.ts +32 -12
  305. package/src/clis/douyin/_shared/browser-fetch.test.ts +0 -1
  306. package/src/clis/douyin/_shared/transcode.test.ts +0 -2
  307. package/src/clis/douyin/draft.test.ts +0 -2
  308. package/src/clis/facebook/search.test.ts +0 -2
  309. package/src/clis/gemini/ask.test.ts +116 -0
  310. package/src/clis/gemini/ask.ts +10 -3
  311. package/src/clis/gemini/reply-state.test.ts +708 -0
  312. package/src/clis/gemini/utils.test.ts +184 -2
  313. package/src/clis/gemini/utils.ts +588 -60
  314. package/src/clis/hupu/detail.ts +126 -0
  315. package/src/clis/hupu/hot.yaml +43 -0
  316. package/src/clis/hupu/like.ts +76 -0
  317. package/src/clis/hupu/reply.ts +76 -0
  318. package/src/clis/hupu/search.ts +95 -0
  319. package/src/clis/hupu/unlike.ts +76 -0
  320. package/src/clis/hupu/utils.ts +381 -0
  321. package/src/clis/instagram/_shared/private-publish.test.ts +827 -0
  322. package/src/clis/instagram/_shared/private-publish.ts +1303 -0
  323. package/src/clis/instagram/_shared/protocol-capture.test.ts +148 -0
  324. package/src/clis/instagram/_shared/protocol-capture.ts +321 -0
  325. package/src/clis/instagram/_shared/runtime-info.ts +91 -0
  326. package/src/clis/instagram/note.test.ts +96 -0
  327. package/src/clis/instagram/note.ts +254 -0
  328. package/src/clis/instagram/post.test.ts +1716 -0
  329. package/src/clis/instagram/post.ts +1620 -0
  330. package/src/clis/instagram/reel.test.ts +191 -0
  331. package/src/clis/instagram/reel.ts +886 -0
  332. package/src/clis/instagram/story.test.ts +191 -0
  333. package/src/clis/instagram/story.ts +151 -0
  334. package/src/clis/sinafinance/stock-rank.ts +68 -0
  335. package/src/clis/substack/utils.test.ts +0 -2
  336. package/src/clis/twitter/post.test.ts +157 -0
  337. package/src/clis/twitter/post.ts +82 -48
  338. package/src/clis/twitter/reply.test.ts +177 -0
  339. package/src/clis/twitter/reply.ts +285 -39
  340. package/src/clis/twitter/search.test.ts +88 -5
  341. package/src/clis/twitter/search.ts +68 -5
  342. package/src/clis/xianyu/chat.test.ts +20 -0
  343. package/src/clis/xianyu/chat.ts +175 -0
  344. package/src/clis/xianyu/item.test.ts +67 -0
  345. package/src/clis/xianyu/item.ts +172 -0
  346. package/src/clis/xianyu/search.test.ts +22 -0
  347. package/src/clis/xianyu/search.ts +151 -0
  348. package/src/clis/xianyu/utils.ts +9 -0
  349. package/src/clis/xiaoe/catalog.yaml +129 -0
  350. package/src/clis/xiaoe/content.yaml +43 -0
  351. package/src/clis/xiaoe/courses.yaml +73 -0
  352. package/src/clis/xiaoe/detail.yaml +39 -0
  353. package/src/clis/xiaoe/play-url.yaml +124 -0
  354. package/src/clis/xiaohongshu/comments.test.ts +0 -2
  355. package/src/clis/xiaohongshu/creator-note-detail.test.ts +0 -2
  356. package/src/clis/xiaohongshu/creator-notes.test.ts +0 -2
  357. package/src/clis/xiaohongshu/download.test.ts +0 -2
  358. package/src/clis/xiaohongshu/note.test.ts +0 -2
  359. package/src/clis/xiaohongshu/publish.test.ts +0 -2
  360. package/src/clis/xiaohongshu/search.test.ts +59 -48
  361. package/src/clis/xiaohongshu/search.ts +31 -21
  362. package/src/clis/yuanbao/ask.test.ts +156 -0
  363. package/src/clis/yuanbao/ask.ts +522 -0
  364. package/src/clis/yuanbao/new.test.ts +36 -0
  365. package/src/clis/yuanbao/new.ts +81 -0
  366. package/src/clis/yuanbao/shared.ts +57 -0
  367. package/src/clis/zhihu/question.test.ts +42 -17
  368. package/src/clis/zhihu/question.ts +31 -26
  369. package/src/commanderAdapter.test.ts +51 -0
  370. package/src/commanderAdapter.ts +8 -4
  371. package/src/completion.test.ts +30 -0
  372. package/src/completion.ts +3 -1
  373. package/src/doctor.ts +1 -1
  374. package/src/electron-apps.ts +9 -1
  375. package/src/errors.ts +1 -1
  376. package/src/execution.ts +26 -30
  377. package/src/explore.ts +1 -1
  378. package/src/launcher.test.ts +121 -7
  379. package/src/launcher.ts +87 -9
  380. package/src/output.test.ts +50 -90
  381. package/src/output.ts +10 -1
  382. package/src/pipeline/executor.test.ts +0 -2
  383. package/src/pipeline/steps/download.test.ts +0 -2
  384. package/src/registry.ts +2 -0
  385. package/src/serialization.ts +2 -0
  386. package/src/types.ts +9 -2
  387. package/tests/e2e/browser-auth.test.ts +9 -0
  388. package/CLI-EXPLORER.md +0 -724
  389. package/CLI-ONESHOT.md +0 -216
  390. package/SKILL.md +0 -59
@@ -0,0 +1,381 @@
1
+ import { AuthRequiredError, CommandExecutionError } from '../../errors.js';
2
+ import type { IPage } from '../../types.js';
3
+
4
+ export interface HupuApiResponse {
5
+ code?: number;
6
+ msg?: string;
7
+ message?: string;
8
+ }
9
+
10
+ interface BrowserFetchResult {
11
+ ok?: boolean;
12
+ status?: number;
13
+ data?: HupuApiResponse | null;
14
+ error?: string;
15
+ }
16
+
17
+ interface BrowserDataResult<T> {
18
+ ok?: boolean;
19
+ data?: T;
20
+ error?: string;
21
+ }
22
+
23
+ export function stripHtml(html: string): string {
24
+ if (!html) return '';
25
+ const decoded = html
26
+ .replace(/\\u003c/g, '<')
27
+ .replace(/\\u003e/g, '>')
28
+ .replace(/\\n/g, '\n')
29
+ .replace(/\\r/g, '');
30
+ return decoded.replace(/<[^>]+>/g, '').trim();
31
+ }
32
+
33
+ export function decodeHtmlEntities(html: string): string {
34
+ if (!html) return '';
35
+ return html.replace(/&nbsp;/g, ' ')
36
+ .replace(/&lt;/g, '<')
37
+ .replace(/&gt;/g, '>')
38
+ .replace(/&amp;/g, '&')
39
+ .replace(/&quot;/g, '"')
40
+ .replace(/&#x27;/g, "'");
41
+ }
42
+
43
+ export function getHupuThreadUrl(tid: unknown): string {
44
+ return `https://bbs.hupu.com/${encodeURIComponent(String(tid))}-1.html`;
45
+ }
46
+
47
+ export function getHupuSearchUrl(query: unknown, page: unknown, forum?: unknown, sort?: unknown): string {
48
+ const searchParams = new URLSearchParams();
49
+ searchParams.append('q', String(query));
50
+ searchParams.append('page', String(page));
51
+
52
+ if (forum) {
53
+ searchParams.append('topicId', String(forum));
54
+ }
55
+
56
+ if (sort) {
57
+ searchParams.append('sortby', String(sort));
58
+ }
59
+
60
+ return `https://bbs.hupu.com/search?${searchParams.toString()}`;
61
+ }
62
+
63
+ export async function readHupuNextData<T>(
64
+ page: IPage,
65
+ url: string,
66
+ actionLabel: string,
67
+ options: {
68
+ expectedTid?: string;
69
+ timeoutMs?: number;
70
+ } = {},
71
+ ): Promise<T> {
72
+ await page.goto(url);
73
+
74
+ const result = await page.evaluate(`
75
+ (async () => {
76
+ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
77
+ const expectedTid = ${JSON.stringify(options.expectedTid || '')};
78
+ const timeoutMs = ${JSON.stringify(options.timeoutMs ?? 5000)};
79
+ let lastSeenTid = '';
80
+ let lastSeenHref = '';
81
+
82
+ const waitFor = async (predicate, limitMs = timeoutMs) => {
83
+ const start = Date.now();
84
+ while (Date.now() - start < limitMs) {
85
+ if (predicate()) return true;
86
+ await wait(100);
87
+ }
88
+ return false;
89
+ };
90
+
91
+ const ready = await waitFor(() => {
92
+ const script = document.getElementById('__NEXT_DATA__');
93
+ if (!script?.textContent) return false;
94
+
95
+ lastSeenHref = location.href;
96
+
97
+ try {
98
+ const parsed = JSON.parse(script.textContent);
99
+ const threadTid = parsed?.props?.pageProps?.detail?.thread?.tid;
100
+ lastSeenTid = typeof threadTid === 'string' ? threadTid : '';
101
+
102
+ if (!expectedTid) return true;
103
+ return threadTid === expectedTid;
104
+ } catch {
105
+ return false;
106
+ }
107
+ });
108
+ if (!ready) {
109
+ return {
110
+ ok: false,
111
+ error: expectedTid
112
+ ? \`帖子数据未就绪或tid不匹配(expected=\${expectedTid}, actual=\${lastSeenTid || 'unknown'}, href=\${lastSeenHref || location.href})\`
113
+ : '无法找到帖子数据'
114
+ };
115
+ }
116
+
117
+ try {
118
+ const text = document.getElementById('__NEXT_DATA__')?.textContent || '';
119
+ return {
120
+ ok: true,
121
+ data: JSON.parse(text)
122
+ };
123
+ } catch (error) {
124
+ return {
125
+ ok: false,
126
+ error: error instanceof Error ? error.message : String(error)
127
+ };
128
+ }
129
+ })()
130
+ `) as BrowserDataResult<T>;
131
+
132
+ if (!result || typeof result !== 'object' || !result.ok) {
133
+ throw new CommandExecutionError(`${actionLabel} failed: ${result?.error || 'invalid browser response'}`);
134
+ }
135
+
136
+ return result.data as T;
137
+ }
138
+
139
+ export async function readHupuSearchData<T>(page: IPage, url: string, actionLabel: string): Promise<T> {
140
+ await page.goto(url);
141
+
142
+ const result = await page.evaluate(`
143
+ (async () => {
144
+ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
145
+ const waitFor = async (predicate, timeoutMs = 5000) => {
146
+ const start = Date.now();
147
+ while (Date.now() - start < timeoutMs) {
148
+ if (predicate()) return true;
149
+ await wait(100);
150
+ }
151
+ return false;
152
+ };
153
+
154
+ const extractFromScript = () => {
155
+ const marker = 'window.$$data=';
156
+ for (const script of Array.from(document.scripts)) {
157
+ const text = script.textContent || '';
158
+ const dataIndex = text.indexOf(marker);
159
+ if (dataIndex === -1) continue;
160
+
161
+ const jsonStart = dataIndex + marker.length;
162
+ let braceCount = 0;
163
+ let jsonEnd = jsonStart;
164
+ let inString = false;
165
+ let escapeNext = false;
166
+
167
+ for (let i = jsonStart; i < text.length; i++) {
168
+ const char = text[i];
169
+
170
+ if (escapeNext) {
171
+ escapeNext = false;
172
+ continue;
173
+ }
174
+
175
+ if (char === '\\\\') {
176
+ escapeNext = true;
177
+ continue;
178
+ }
179
+
180
+ if (char === '"') {
181
+ inString = !inString;
182
+ continue;
183
+ }
184
+
185
+ if (!inString) {
186
+ if (char === '{') {
187
+ braceCount++;
188
+ } else if (char === '}') {
189
+ braceCount--;
190
+ if (braceCount === 0) {
191
+ jsonEnd = i;
192
+ break;
193
+ }
194
+ }
195
+ }
196
+ }
197
+
198
+ if (jsonEnd > jsonStart) {
199
+ return text.substring(jsonStart, jsonEnd + 1);
200
+ }
201
+ }
202
+ return '';
203
+ };
204
+
205
+ const ready = await waitFor(() => {
206
+ return typeof window.$$data !== 'undefined' || Boolean(extractFromScript());
207
+ });
208
+ if (!ready) {
209
+ return { ok: false, error: '无法找到搜索数据' };
210
+ }
211
+
212
+ try {
213
+ if (typeof window.$$data !== 'undefined') {
214
+ return {
215
+ ok: true,
216
+ data: JSON.parse(JSON.stringify(window.$$data))
217
+ };
218
+ }
219
+
220
+ const jsonString = extractFromScript();
221
+ return {
222
+ ok: true,
223
+ data: JSON.parse(jsonString)
224
+ };
225
+ } catch (error) {
226
+ return {
227
+ ok: false,
228
+ error: error instanceof Error ? error.message : String(error)
229
+ };
230
+ }
231
+ })()
232
+ `) as BrowserDataResult<T>;
233
+
234
+ if (!result || typeof result !== 'object' || !result.ok) {
235
+ throw new CommandExecutionError(`${actionLabel} failed: ${result?.error || 'invalid browser response'}`);
236
+ }
237
+
238
+ return result.data as T;
239
+ }
240
+
241
+ function buildBrowserJsonPostScript(
242
+ apiUrl: string,
243
+ body: Record<string, unknown>,
244
+ mode: 'default' | 'reply',
245
+ ): string {
246
+ return `
247
+ (async () => {
248
+ const url = ${JSON.stringify(apiUrl)};
249
+ const payload = ${JSON.stringify(body)};
250
+ const mode = ${JSON.stringify(mode)};
251
+ const getCookie = (name) => document.cookie
252
+ .split('; ')
253
+ .find((item) => item.startsWith(name + '='))
254
+ ?.slice(name.length + 1) || '';
255
+
256
+ const findThumbcacheValue = () => {
257
+ const rawEntry = document.cookie
258
+ .split('; ')
259
+ .find((item) => item.startsWith('.thumbcache_'));
260
+ if (rawEntry && rawEntry.includes('=')) {
261
+ const rawValue = rawEntry.slice(rawEntry.indexOf('=') + 1);
262
+ try {
263
+ return decodeURIComponent(rawValue);
264
+ } catch {
265
+ return rawValue;
266
+ }
267
+ }
268
+
269
+ const storageKey = Object.keys(localStorage).find((key) => key.startsWith('.thumbcache_'));
270
+ if (!storageKey) return '';
271
+ return localStorage.getItem(storageKey) || '';
272
+ };
273
+
274
+ const resolveDefaultPayload = (input) => {
275
+ const next = { ...input };
276
+ const sensorsRaw = decodeURIComponent(getCookie('sensorsdata2015jssdkcross') || '');
277
+ let deviceid = '';
278
+ try {
279
+ const sensors = JSON.parse(sensorsRaw);
280
+ deviceid = sensors?.props?.['$device_id'] || sensors?.distinct_id || '';
281
+ } catch {}
282
+
283
+ if ((next.puid === '' || next.puid == null) && getCookie('ua')) {
284
+ next.puid = getCookie('ua');
285
+ }
286
+ if ((next.shumei_id === '' || next.shumei_id == null) && getCookie('smidV2')) {
287
+ next.shumei_id = getCookie('smidV2');
288
+ }
289
+ if ((next.deviceid === '' || next.deviceid == null) && deviceid) {
290
+ next.deviceid = deviceid;
291
+ }
292
+ return next;
293
+ };
294
+
295
+ const resolveReplyPayload = (input) => {
296
+ const next = { ...input };
297
+ const thumbcache = findThumbcacheValue();
298
+ if ((next.shumeiId === '' || next.shumeiId == null) && thumbcache) {
299
+ next.shumeiId = thumbcache;
300
+ }
301
+ if ((next.deviceid === '' || next.deviceid == null) && thumbcache) {
302
+ next.deviceid = thumbcache;
303
+ }
304
+ return next;
305
+ };
306
+
307
+ const resolvedPayload = mode === 'reply'
308
+ ? resolveReplyPayload(payload)
309
+ : resolveDefaultPayload(payload);
310
+
311
+ try {
312
+ const response = await fetch(url, {
313
+ method: 'POST',
314
+ credentials: 'include',
315
+ headers: {
316
+ 'Content-Type': 'application/json'
317
+ },
318
+ body: JSON.stringify(resolvedPayload)
319
+ });
320
+
321
+ const text = await response.text();
322
+ let data = null;
323
+ try {
324
+ data = text ? JSON.parse(text) : null;
325
+ } catch {
326
+ data = text ? { message: text } : null;
327
+ }
328
+
329
+ return {
330
+ ok: response.ok,
331
+ status: response.status,
332
+ data
333
+ };
334
+ } catch (error) {
335
+ return {
336
+ ok: false,
337
+ error: error instanceof Error ? error.message : String(error)
338
+ };
339
+ }
340
+ })()
341
+ `;
342
+ }
343
+
344
+ /**
345
+ * Execute authenticated Hupu JSON requests inside the browser page so
346
+ * cookies and the thread referer come from the live logged-in session.
347
+ */
348
+ export async function postHupuJson(
349
+ page: IPage,
350
+ tid: unknown,
351
+ apiUrl: string,
352
+ body: Record<string, unknown>,
353
+ actionLabel: string,
354
+ mode: 'default' | 'reply' = 'default',
355
+ ): Promise<HupuApiResponse> {
356
+ const referer = getHupuThreadUrl(tid);
357
+ await page.goto(referer);
358
+
359
+ const result = await page.evaluate(
360
+ buildBrowserJsonPostScript(apiUrl, body, mode),
361
+ ) as BrowserFetchResult;
362
+
363
+ if (!result || typeof result !== 'object') {
364
+ throw new CommandExecutionError(`${actionLabel} failed: invalid browser response`);
365
+ }
366
+
367
+ if (result.status === 401 || result.status === 403) {
368
+ throw new AuthRequiredError('bbs.hupu.com', `${actionLabel} failed: please log in to Hupu first`);
369
+ }
370
+
371
+ if (result.error) {
372
+ throw new CommandExecutionError(`${actionLabel} failed: ${result.error}`);
373
+ }
374
+
375
+ if (!result.ok) {
376
+ const detail = result.data?.msg || result.data?.message || `HTTP ${result.status ?? 'unknown'}`;
377
+ throw new CommandExecutionError(`${actionLabel} failed: ${detail}`);
378
+ }
379
+
380
+ return result.data ?? {};
381
+ }