@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
@@ -20,8 +20,6 @@ function createPageMock(evaluateResults: any[]): IPage {
20
20
  getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
21
21
  wait: vi.fn().mockResolvedValue(undefined),
22
22
  tabs: vi.fn().mockResolvedValue([]),
23
- closeTab: vi.fn().mockResolvedValue(undefined),
24
- newTab: vi.fn().mockResolvedValue(undefined),
25
23
  selectTab: vi.fn().mockResolvedValue(undefined),
26
24
  networkRequests: vi.fn().mockResolvedValue([]),
27
25
  consoleMessages: vi.fn().mockResolvedValue([]),
@@ -41,8 +39,8 @@ describe('xiaohongshu search', () => {
41
39
  expect(cmd?.func).toBeTypeOf('function');
42
40
 
43
41
  const page = createPageMock([
44
- // First evaluate: early login-wall check (returns true)
45
- true,
42
+ // First evaluate: MutationObserver wait (login wall detected)
43
+ 'login_wall',
46
44
  ]);
47
45
 
48
46
  await expect(cmd!.func!(page, { query: '特斯拉', limit: 5 })).rejects.toThrow(
@@ -63,21 +61,18 @@ describe('xiaohongshu search', () => {
63
61
  'https://www.xiaohongshu.com/user/profile/635a9c720000000018028b40?xsec_token=user-token&xsec_source=pc_search';
64
62
 
65
63
  const page = createPageMock([
66
- // First evaluate: early login-wall check (returns false → no wall)
67
- false,
68
- // Second evaluate: main DOM extraction
69
- {
70
- loginWall: false,
71
- results: [
72
- {
73
- title: '某鱼买FSD被坑了4万',
74
- author: '随风',
75
- likes: '261',
76
- url: detailUrl,
77
- author_url: authorUrl,
78
- },
79
- ],
80
- },
64
+ // First evaluate: MutationObserver wait (content appeared)
65
+ 'content',
66
+ // Second evaluate: main DOM extraction (returns array directly)
67
+ [
68
+ {
69
+ title: '某鱼买FSD被坑了4万',
70
+ author: '随风',
71
+ likes: '261',
72
+ url: detailUrl,
73
+ author_url: authorUrl,
74
+ },
75
+ ],
81
76
  ]);
82
77
 
83
78
  const result = await cmd!.func!(page, { query: '特斯拉', limit: 1 });
@@ -103,35 +98,32 @@ describe('xiaohongshu search', () => {
103
98
  expect(cmd?.func).toBeTypeOf('function');
104
99
 
105
100
  const page = createPageMock([
106
- // First evaluate: early login-wall check (returns false → no wall)
107
- false,
108
- // Second evaluate: main DOM extraction
109
- {
110
- loginWall: false,
111
- results: [
112
- {
113
- title: 'Result A',
114
- author: 'UserA',
115
- likes: '10',
116
- url: 'https://www.xiaohongshu.com/search_result/aaa',
117
- author_url: '',
118
- },
119
- {
120
- title: '',
121
- author: 'UserB',
122
- likes: '5',
123
- url: 'https://www.xiaohongshu.com/search_result/bbb',
124
- author_url: '',
125
- },
126
- {
127
- title: 'Result C',
128
- author: 'UserC',
129
- likes: '3',
130
- url: 'https://www.xiaohongshu.com/search_result/ccc',
131
- author_url: '',
132
- },
133
- ],
134
- },
101
+ // First evaluate: MutationObserver wait (content appeared)
102
+ 'content',
103
+ // Second evaluate: main DOM extraction (returns array directly)
104
+ [
105
+ {
106
+ title: 'Result A',
107
+ author: 'UserA',
108
+ likes: '10',
109
+ url: 'https://www.xiaohongshu.com/search_result/aaa',
110
+ author_url: '',
111
+ },
112
+ {
113
+ title: '',
114
+ author: 'UserB',
115
+ likes: '5',
116
+ url: 'https://www.xiaohongshu.com/search_result/bbb',
117
+ author_url: '',
118
+ },
119
+ {
120
+ title: 'Result C',
121
+ author: 'UserC',
122
+ likes: '3',
123
+ url: 'https://www.xiaohongshu.com/search_result/ccc',
124
+ author_url: '',
125
+ },
126
+ ],
135
127
  ]);
136
128
 
137
129
  const result = (await cmd!.func!(page, { query: '测试', limit: 1 })) as any[];
@@ -140,6 +132,25 @@ describe('xiaohongshu search', () => {
140
132
  expect(result).toHaveLength(1);
141
133
  expect(result[0]).toMatchObject({ rank: 1, title: 'Result A' });
142
134
  });
135
+
136
+ it('waits for content via MutationObserver before extracting', async () => {
137
+ const cmd = getRegistry().get('xiaohongshu/search');
138
+ expect(cmd?.func).toBeTypeOf('function');
139
+
140
+ const page = createPageMock([
141
+ // First evaluate: MutationObserver wait (content appeared)
142
+ 'content',
143
+ // Second evaluate: extraction (returns empty array)
144
+ [],
145
+ ]);
146
+
147
+ const result = (await cmd!.func!(page, { query: '测试等待', limit: 5 })) as any[];
148
+ expect(result).toHaveLength(0);
149
+ // Only one navigation, no retry
150
+ expect(page.goto).toHaveBeenCalledTimes(1);
151
+ // Two evaluate calls: wait + extraction
152
+ expect(page.evaluate).toHaveBeenCalledTimes(2);
153
+ });
143
154
  });
144
155
 
145
156
  describe('noteIdToDate (ObjectID timestamp parsing)', () => {
@@ -9,6 +9,29 @@
9
9
  import { cli, Strategy } from '../../registry.js';
10
10
  import { AuthRequiredError } from '../../errors.js';
11
11
 
12
+ /**
13
+ * Wait for search results or login wall using MutationObserver (max 5s).
14
+ * Returns 'content' if note items appeared, 'login_wall' if login gate
15
+ * detected, or 'timeout' if neither appeared within the deadline.
16
+ */
17
+ const WAIT_FOR_CONTENT_JS = `
18
+ new Promise((resolve) => {
19
+ const detect = () => {
20
+ if (document.querySelector('section.note-item')) return 'content';
21
+ if (/登录后查看搜索结果/.test(document.body?.innerText || '')) return 'login_wall';
22
+ return null;
23
+ };
24
+ const found = detect();
25
+ if (found) return resolve(found);
26
+ const observer = new MutationObserver(() => {
27
+ const result = detect();
28
+ if (result) { observer.disconnect(); resolve(result); }
29
+ });
30
+ observer.observe(document.body, { childList: true, subtree: true });
31
+ setTimeout(() => { observer.disconnect(); resolve('timeout'); }, 5000);
32
+ })
33
+ `;
34
+
12
35
  /**
13
36
  * Extract approximate publish date from a Xiaohongshu note URL.
14
37
  * XHS note IDs follow MongoDB ObjectID format where the first 8 hex
@@ -42,15 +65,13 @@ cli({
42
65
  await page.goto(
43
66
  `https://www.xiaohongshu.com/search_result?keyword=${keyword}&source=web_search_result_notes`
44
67
  );
45
- await page.wait(3);
46
68
 
47
- // Early login-wall detection: XHS may show a login gate instead of
48
- // results. Check *before* autoScroll to avoid crashing on a page
49
- // that has no meaningful content to scroll through.
50
- const loginCheck = await page.evaluate(`
51
- (() => /登录后查看搜索结果/.test(document.body?.innerText || ''))()
52
- `);
53
- if (loginCheck) {
69
+ // Wait for search results to render (or login wall to appear).
70
+ // Uses MutationObserver to resolve as soon as content appears,
71
+ // instead of a fixed delay + blind retry.
72
+ const waitResult = await page.evaluate(WAIT_FOR_CONTENT_JS);
73
+
74
+ if (waitResult === 'login_wall') {
54
75
  throw new AuthRequiredError(
55
76
  'www.xiaohongshu.com',
56
77
  'Xiaohongshu search results are blocked behind a login wall',
@@ -62,8 +83,6 @@ cli({
62
83
 
63
84
  const payload = await page.evaluate(`
64
85
  (() => {
65
- const loginWall = /登录后查看搜索结果/.test(document.body.innerText || '');
66
-
67
86
  const normalizeUrl = (href) => {
68
87
  if (!href) return '';
69
88
  if (href.startsWith('http://') || href.startsWith('https://')) return href;
@@ -107,20 +126,11 @@ cli({
107
126
  });
108
127
  });
109
128
 
110
- return {
111
- loginWall,
112
- results,
113
- };
129
+ return results;
114
130
  })()
115
131
  `);
116
132
 
117
- if (!payload || typeof payload !== 'object') return [];
118
-
119
- if ((payload as any).loginWall) {
120
- throw new AuthRequiredError('www.xiaohongshu.com', 'Xiaohongshu search results are blocked behind a login wall');
121
- }
122
-
123
- const data: any[] = Array.isArray((payload as any).results) ? (payload as any).results : [];
133
+ const data: any[] = Array.isArray(payload) ? (payload as any[]) : [];
124
134
  return data
125
135
  .filter((item: any) => item.title)
126
136
  .slice(0, kwargs.limit)
@@ -0,0 +1,156 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import type { IPage } from '../../types.js';
3
+ import { AuthRequiredError, CommandExecutionError, TimeoutError } from '../../errors.js';
4
+ import { __test__ } from './ask.js';
5
+ import { askCommand } from './ask.js';
6
+
7
+ describe('yuanbao ask helpers', () => {
8
+ describe('isOnYuanbao', () => {
9
+ const fakePage = (url: string | Error): IPage =>
10
+ ({ evaluate: () => url instanceof Error ? Promise.reject(url) : Promise.resolve(url) }) as unknown as IPage;
11
+
12
+ it('returns true for yuanbao.tencent.com URLs', async () => {
13
+ expect(await __test__.isOnYuanbao(fakePage('https://yuanbao.tencent.com/'))).toBe(true);
14
+ expect(await __test__.isOnYuanbao(fakePage('https://yuanbao.tencent.com/chat/abc'))).toBe(true);
15
+ });
16
+
17
+ it('returns false for non-yuanbao domains', async () => {
18
+ expect(await __test__.isOnYuanbao(fakePage('https://example.com/?next=yuanbao.tencent.com'))).toBe(false);
19
+ expect(await __test__.isOnYuanbao(fakePage('about:blank'))).toBe(false);
20
+ });
21
+
22
+ it('returns false when evaluate throws', async () => {
23
+ expect(await __test__.isOnYuanbao(fakePage(new Error('detached')))).toBe(false);
24
+ });
25
+ });
26
+
27
+ it('removes echoed prompt prefixes from transcript additions', () => {
28
+ expect(__test__.sanitizeYuanbaoResponseText('你好\n你好,我是元宝。', '你好')).toBe('你好,我是元宝。');
29
+ });
30
+
31
+ it('filters transient in-progress assistant placeholders', () => {
32
+ expect(__test__.sanitizeYuanbaoResponseText('正在搜索资料', '张雪机车相关的股票有哪些?')).toBe('');
33
+ });
34
+
35
+ it('normalizes boolean flags with explicit defaults', () => {
36
+ expect(__test__.normalizeBooleanFlag(undefined, true)).toBe(true);
37
+ expect(__test__.normalizeBooleanFlag(undefined, false)).toBe(false);
38
+ expect(__test__.normalizeBooleanFlag('true', false)).toBe(true);
39
+ expect(__test__.normalizeBooleanFlag('1', false)).toBe(true);
40
+ expect(__test__.normalizeBooleanFlag('yes', false)).toBe(true);
41
+ expect(__test__.normalizeBooleanFlag('false', true)).toBe(false);
42
+ });
43
+
44
+ it('ignores baseline lines and echoed prompts when collecting additions', () => {
45
+ const response = __test__.collectYuanbaoTranscriptAdditions(
46
+ ['旧消息'],
47
+ ['旧消息', '你好', '你好\n你好,我是元宝。'],
48
+ '你好',
49
+ );
50
+
51
+ expect(response).toBe('你好,我是元宝。');
52
+ });
53
+
54
+ it('prefers fresh assistant messages over echoed prompts and older messages', () => {
55
+ const response = __test__.pickLatestYuanbaoAssistantCandidate(
56
+ ['旧回复', '你好', '你好!我是元宝,由腾讯推出的AI助手。'],
57
+ 1,
58
+ '你好',
59
+ );
60
+
61
+ expect(response).toBe('你好!我是元宝,由腾讯推出的AI助手。');
62
+ });
63
+
64
+ it('converts assistant html tables to markdown tables via turndown', () => {
65
+ const markdown = __test__.convertYuanbaoHtmlToMarkdown(`
66
+ <h3>核心产业链概念股一览</h3>
67
+ <table>
68
+ <thead>
69
+ <tr><th>细分赛道</th><th>核心标的</th></tr>
70
+ </thead>
71
+ <tbody>
72
+ <tr><td>光模块</td><td>中际旭创</td></tr>
73
+ </tbody>
74
+ </table>
75
+ `);
76
+
77
+ expect(markdown).toContain('### 核心产业链概念股一览');
78
+ expect(markdown).toContain('| 细分赛道 | 核心标的 |');
79
+ expect(markdown).toContain('| --- | --- |');
80
+ expect(markdown).toContain('| 光模块 | 中际旭创 |');
81
+ });
82
+
83
+ it('tracks stabilization by incrementing repeats and resetting on changes', () => {
84
+ expect(__test__.updateStableState('', 0, '第一段')).toEqual({
85
+ previousText: '第一段',
86
+ stableCount: 0,
87
+ });
88
+
89
+ expect(__test__.updateStableState('第一段', 0, '第一段')).toEqual({
90
+ previousText: '第一段',
91
+ stableCount: 1,
92
+ });
93
+
94
+ expect(__test__.updateStableState('第一段', 1, '第二段')).toEqual({
95
+ previousText: '第二段',
96
+ stableCount: 0,
97
+ });
98
+ });
99
+ });
100
+
101
+ function createAskPageMock(overrides: {
102
+ currentUrl?: string;
103
+ hasLoginGate?: boolean;
104
+ sendResult?: { ok?: boolean; reason?: string; detail?: string; action?: string };
105
+ } = {}): IPage {
106
+ const currentUrl = overrides.currentUrl ?? 'https://yuanbao.tencent.com/';
107
+ const hasLoginGate = overrides.hasLoginGate ?? false;
108
+ const sendResult = overrides.sendResult;
109
+
110
+ return {
111
+ goto: vi.fn().mockResolvedValue(undefined),
112
+ wait: vi.fn().mockResolvedValue(undefined),
113
+ evaluate: vi.fn().mockImplementation(async (script: string) => {
114
+ if (script === 'window.location.href') return currentUrl;
115
+ if (script.includes('微信扫码登录')) return hasLoginGate;
116
+ if (script.includes('[dt-button-id="internet_search"]')) return { found: false, enabled: false };
117
+ if (script.includes('[dt-button-id="deep_think"]')) return { found: false, enabled: false };
118
+ if (script.includes('.agent-chat__list__item--ai')) return [];
119
+ if (script.includes('const stopLines = new Set([')) return [];
120
+ if (script.includes('Failed to insert the prompt into the Yuanbao composer.')) {
121
+ return sendResult ?? { ok: true, action: 'click' };
122
+ }
123
+ throw new Error(`Unexpected evaluate script in test: ${script.slice(0, 80)}`);
124
+ }),
125
+ } as unknown as IPage;
126
+ }
127
+
128
+ describe('yuanbao ask command', () => {
129
+ it('throws AuthRequiredError when Yuanbao shows a login gate before sending', async () => {
130
+ const page = createAskPageMock({ hasLoginGate: true });
131
+
132
+ await expect(askCommand.func!(page, { prompt: '你好', timeout: '60', search: true, think: false }))
133
+ .rejects.toBeInstanceOf(AuthRequiredError);
134
+ });
135
+
136
+ it('throws CommandExecutionError when the prompt cannot be sent', async () => {
137
+ const page = createAskPageMock({
138
+ sendResult: {
139
+ ok: false,
140
+ reason: 'Yuanbao composer was not found.',
141
+ },
142
+ });
143
+
144
+ await expect(askCommand.func!(page, { prompt: '你好', timeout: '60', search: true, think: false }))
145
+ .rejects.toBeInstanceOf(CommandExecutionError);
146
+ });
147
+
148
+ it('throws TimeoutError when no response arrives before timeout', async () => {
149
+ const page = createAskPageMock({
150
+ sendResult: { ok: true, action: 'click' },
151
+ });
152
+
153
+ await expect(askCommand.func!(page, { prompt: '你好', timeout: '-1', search: true, think: false }))
154
+ .rejects.toBeInstanceOf(TimeoutError);
155
+ });
156
+ });