@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,39 @@
1
+ site: xiaoe
2
+ name: detail
3
+ description: 小鹅通课程详情(名称、价格、学员数、店铺)
4
+ domain: h5.xet.citv.cn
5
+ strategy: cookie
6
+
7
+ args:
8
+ url:
9
+ type: str
10
+ required: true
11
+ positional: true
12
+ description: 课程页面 URL
13
+
14
+ pipeline:
15
+ - navigate: ${{ args.url }}
16
+
17
+ - wait: 5
18
+
19
+ - evaluate: |
20
+ (() => {
21
+ var vm = (document.querySelector('#app') || {}).__vue__;
22
+ if (!vm || !vm.$store) return [];
23
+ var core = vm.$store.state.coreInfo || {};
24
+ var goods = vm.$store.state.goodsInfo || {};
25
+ var shop = ((vm.$store.state.compositeInfo || {}).shop_conf) || {};
26
+ return [{
27
+ name: core.resource_name || '',
28
+ resource_id: core.resource_id || '',
29
+ resource_type: core.resource_type || '',
30
+ cover: core.resource_img || '',
31
+ user_count: core.user_count || 0,
32
+ price: goods.price ? (goods.price / 100).toFixed(2) : '0',
33
+ original_price: goods.line_price ? (goods.line_price / 100).toFixed(2) : '0',
34
+ is_free: goods.is_free || 0,
35
+ shop_name: shop.shop_name || '',
36
+ }];
37
+ })()
38
+
39
+ columns: [name, price, original_price, user_count, shop_name]
@@ -0,0 +1,124 @@
1
+ site: xiaoe
2
+ name: play-url
3
+ description: 小鹅通视频/音频/直播回放 M3U8 播放地址
4
+ domain: h5.xet.citv.cn
5
+ strategy: cookie
6
+
7
+ args:
8
+ url:
9
+ type: str
10
+ required: true
11
+ positional: true
12
+ description: 小节页面 URL
13
+
14
+ pipeline:
15
+ - navigate: ${{ args.url }}
16
+
17
+ - wait: 2
18
+
19
+ - evaluate: |
20
+ (async () => {
21
+ var pageUrl = window.location.href;
22
+ var origin = window.location.origin;
23
+ var resourceId = (pageUrl.match(/[val]_[a-f0-9]+/) || [])[0] || '';
24
+ var productId = (pageUrl.match(/product_id=([^&]+)/) || [])[1] || '';
25
+ var appId = (origin.match(/(app[a-z0-9]+)\./) || [])[1] || '';
26
+ var isLive = resourceId.startsWith('l_') || pageUrl.includes('/alive/');
27
+ var m3u8Url = '', method = '', title = document.title, duration = 0;
28
+
29
+ // 深度搜索 Vue 组件树找 M3U8
30
+ function searchVueM3u8() {
31
+ var el = document.querySelector('#app');
32
+ if (!el || !el.__vue__) return '';
33
+ var walk = function(vm, d) {
34
+ if (!vm || d > 10) return '';
35
+ var data = vm.$data || {};
36
+ for (var k in data) {
37
+ if (k[0] === '_' || k[0] === '$') continue;
38
+ var v = data[k];
39
+ if (typeof v === 'string' && v.includes('.m3u8')) return v;
40
+ if (typeof v === 'object' && v) {
41
+ try {
42
+ var s = JSON.stringify(v);
43
+ var m = s.match(/https?:[^"]*\.m3u8[^"]*/);
44
+ if (m) return m[0].replace(/\\\//g, '/');
45
+ } catch(e) {}
46
+ }
47
+ }
48
+ if (vm.$children) {
49
+ for (var c = 0; c < vm.$children.length; c++) {
50
+ var f = walk(vm.$children[c], d + 1);
51
+ if (f) return f;
52
+ }
53
+ }
54
+ return '';
55
+ };
56
+ return walk(el.__vue__, 0);
57
+ }
58
+
59
+ // ===== 视频课: detail_info → getPlayUrl =====
60
+ if (!isLive && resourceId.startsWith('v_')) {
61
+ try {
62
+ var detailRes = await fetch(origin + '/xe.course.business.video.detail_info.get/2.0.0', {
63
+ method: 'POST', credentials: 'include',
64
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
65
+ body: new URLSearchParams({
66
+ 'bizData[resource_id]': resourceId,
67
+ 'bizData[product_id]': productId || resourceId,
68
+ 'bizData[opr_sys]': 'MacIntel',
69
+ }),
70
+ });
71
+ var detail = await detailRes.json();
72
+ var vi = (detail.data || {}).video_info || {};
73
+ title = vi.file_name || title;
74
+ duration = vi.video_length || 0;
75
+ if (vi.play_sign) {
76
+ var userId = (document.cookie.match(/ctx_user_id=([^;]+)/) || [])[1] || window.__user_id || '';
77
+ var playRes = await fetch(origin + '/xe.material-center.play/getPlayUrl', {
78
+ method: 'POST', credentials: 'include',
79
+ headers: { 'Content-Type': 'application/json' },
80
+ body: JSON.stringify({
81
+ org_app_id: appId, app_id: vi.material_app_id || appId,
82
+ user_id: userId, play_sign: [vi.play_sign],
83
+ play_line: 'A', opr_sys: 'MacIntel',
84
+ }),
85
+ });
86
+ var playData = await playRes.json();
87
+ if (playData.code === 0 && playData.data) {
88
+ var m = JSON.stringify(playData.data).match(/https?:[^"]*\.m3u8[^"]*/);
89
+ if (m) { m3u8Url = m[0].replace(/\\u0026/g, '&').replace(/\\\//g, '/'); method = 'api_direct'; }
90
+ }
91
+ }
92
+ } catch(e) {}
93
+ }
94
+
95
+ // ===== 兜底: Performance API + Vue 搜索轮询 =====
96
+ if (!m3u8Url) {
97
+ for (var attempt = 0; attempt < 30; attempt++) {
98
+ var entries = performance.getEntriesByType('resource');
99
+ for (var i = 0; i < entries.length; i++) {
100
+ if (entries[i].name.includes('.m3u8')) { m3u8Url = entries[i].name; method = 'perf_api'; break; }
101
+ }
102
+ if (!m3u8Url) { m3u8Url = searchVueM3u8(); if (m3u8Url) method = 'vue_search'; }
103
+ if (m3u8Url) break;
104
+ await new Promise(function(r) { setTimeout(r, 500); });
105
+ }
106
+ }
107
+
108
+ if (!duration) {
109
+ var vid = document.querySelector('video'), aud = document.querySelector('audio');
110
+ if (vid && vid.duration && !isNaN(vid.duration)) duration = Math.round(vid.duration);
111
+ if (aud && aud.duration && !isNaN(aud.duration)) duration = Math.round(aud.duration);
112
+ }
113
+
114
+ return [{ title: title, resource_id: resourceId, m3u8_url: m3u8Url, duration_sec: duration, method: method }];
115
+ })()
116
+
117
+ - map:
118
+ title: ${{ item.title }}
119
+ resource_id: ${{ item.resource_id }}
120
+ m3u8_url: ${{ item.m3u8_url }}
121
+ duration_sec: ${{ item.duration_sec }}
122
+ method: ${{ item.method }}
123
+
124
+ columns: [title, resource_id, m3u8_url, duration_sec, method]
@@ -13,8 +13,6 @@ function createPageMock(evaluateResult) {
13
13
  getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
14
14
  wait: vi.fn().mockResolvedValue(undefined),
15
15
  tabs: vi.fn().mockResolvedValue([]),
16
- closeTab: vi.fn().mockResolvedValue(undefined),
17
- newTab: vi.fn().mockResolvedValue(undefined),
18
16
  selectTab: vi.fn().mockResolvedValue(undefined),
19
17
  networkRequests: vi.fn().mockResolvedValue([]),
20
18
  consoleMessages: vi.fn().mockResolvedValue([]),
@@ -19,8 +19,6 @@ function createPageMock(evaluateResult) {
19
19
  getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
20
20
  wait: vi.fn().mockResolvedValue(undefined),
21
21
  tabs: vi.fn().mockResolvedValue([]),
22
- closeTab: vi.fn().mockResolvedValue(undefined),
23
- newTab: vi.fn().mockResolvedValue(undefined),
24
22
  selectTab: vi.fn().mockResolvedValue(undefined),
25
23
  networkRequests: vi.fn().mockResolvedValue([]),
26
24
  consoleMessages: vi.fn().mockResolvedValue([]),
@@ -22,8 +22,6 @@ function createPageMock(evaluateResult, interceptedRequests = []) {
22
22
  getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
23
23
  wait: vi.fn().mockResolvedValue(undefined),
24
24
  tabs: vi.fn().mockResolvedValue([]),
25
- closeTab: vi.fn().mockResolvedValue(undefined),
26
- newTab: vi.fn().mockResolvedValue(undefined),
27
25
  selectTab: vi.fn().mockResolvedValue(undefined),
28
26
  networkRequests: vi.fn().mockResolvedValue([]),
29
27
  consoleMessages: vi.fn().mockResolvedValue([]),
@@ -23,8 +23,6 @@ function createPageMock(evaluateResult) {
23
23
  getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
24
24
  wait: vi.fn().mockResolvedValue(undefined),
25
25
  tabs: vi.fn().mockResolvedValue([]),
26
- closeTab: vi.fn().mockResolvedValue(undefined),
27
- newTab: vi.fn().mockResolvedValue(undefined),
28
26
  selectTab: vi.fn().mockResolvedValue(undefined),
29
27
  networkRequests: vi.fn().mockResolvedValue([]),
30
28
  consoleMessages: vi.fn().mockResolvedValue([]),
@@ -14,8 +14,6 @@ function createPageMock(evaluateResult) {
14
14
  getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
15
15
  wait: vi.fn().mockResolvedValue(undefined),
16
16
  tabs: vi.fn().mockResolvedValue([]),
17
- closeTab: vi.fn().mockResolvedValue(undefined),
18
- newTab: vi.fn().mockResolvedValue(undefined),
19
17
  selectTab: vi.fn().mockResolvedValue(undefined),
20
18
  networkRequests: vi.fn().mockResolvedValue([]),
21
19
  consoleMessages: vi.fn().mockResolvedValue([]),
@@ -20,8 +20,6 @@ function createPageMock(evaluateResults, overrides = {}) {
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([]),
@@ -7,6 +7,28 @@
7
7
  */
8
8
  import { cli, Strategy } from '../../registry.js';
9
9
  import { AuthRequiredError } from '../../errors.js';
10
+ /**
11
+ * Wait for search results or login wall using MutationObserver (max 5s).
12
+ * Returns 'content' if note items appeared, 'login_wall' if login gate
13
+ * detected, or 'timeout' if neither appeared within the deadline.
14
+ */
15
+ const WAIT_FOR_CONTENT_JS = `
16
+ new Promise((resolve) => {
17
+ const detect = () => {
18
+ if (document.querySelector('section.note-item')) return 'content';
19
+ if (/登录后查看搜索结果/.test(document.body?.innerText || '')) return 'login_wall';
20
+ return null;
21
+ };
22
+ const found = detect();
23
+ if (found) return resolve(found);
24
+ const observer = new MutationObserver(() => {
25
+ const result = detect();
26
+ if (result) { observer.disconnect(); resolve(result); }
27
+ });
28
+ observer.observe(document.body, { childList: true, subtree: true });
29
+ setTimeout(() => { observer.disconnect(); resolve('timeout'); }, 5000);
30
+ })
31
+ `;
10
32
  /**
11
33
  * Extract approximate publish date from a Xiaohongshu note URL.
12
34
  * XHS note IDs follow MongoDB ObjectID format where the first 8 hex
@@ -39,22 +61,17 @@ cli({
39
61
  func: async (page, kwargs) => {
40
62
  const keyword = encodeURIComponent(kwargs.query);
41
63
  await page.goto(`https://www.xiaohongshu.com/search_result?keyword=${keyword}&source=web_search_result_notes`);
42
- await page.wait(3);
43
- // Early login-wall detection: XHS may show a login gate instead of
44
- // results. Check *before* autoScroll to avoid crashing on a page
45
- // that has no meaningful content to scroll through.
46
- const loginCheck = await page.evaluate(`
47
- (() => /登录后查看搜索结果/.test(document.body?.innerText || ''))()
48
- `);
49
- if (loginCheck) {
64
+ // Wait for search results to render (or login wall to appear).
65
+ // Uses MutationObserver to resolve as soon as content appears,
66
+ // instead of a fixed delay + blind retry.
67
+ const waitResult = await page.evaluate(WAIT_FOR_CONTENT_JS);
68
+ if (waitResult === 'login_wall') {
50
69
  throw new AuthRequiredError('www.xiaohongshu.com', 'Xiaohongshu search results are blocked behind a login wall');
51
70
  }
52
71
  // Scroll a couple of times to load more results
53
72
  await page.autoScroll({ times: 2 });
54
73
  const payload = await page.evaluate(`
55
74
  (() => {
56
- const loginWall = /登录后查看搜索结果/.test(document.body.innerText || '');
57
-
58
75
  const normalizeUrl = (href) => {
59
76
  if (!href) return '';
60
77
  if (href.startsWith('http://') || href.startsWith('https://')) return href;
@@ -98,18 +115,10 @@ cli({
98
115
  });
99
116
  });
100
117
 
101
- return {
102
- loginWall,
103
- results,
104
- };
118
+ return results;
105
119
  })()
106
120
  `);
107
- if (!payload || typeof payload !== 'object')
108
- return [];
109
- if (payload.loginWall) {
110
- throw new AuthRequiredError('www.xiaohongshu.com', 'Xiaohongshu search results are blocked behind a login wall');
111
- }
112
- const data = Array.isArray(payload.results) ? payload.results : [];
121
+ const data = Array.isArray(payload) ? payload : [];
113
122
  return data
114
123
  .filter((item) => item.title)
115
124
  .slice(0, kwargs.limit)
@@ -17,8 +17,6 @@ function createPageMock(evaluateResults) {
17
17
  getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
18
18
  wait: vi.fn().mockResolvedValue(undefined),
19
19
  tabs: vi.fn().mockResolvedValue([]),
20
- closeTab: vi.fn().mockResolvedValue(undefined),
21
- newTab: vi.fn().mockResolvedValue(undefined),
22
20
  selectTab: vi.fn().mockResolvedValue(undefined),
23
21
  networkRequests: vi.fn().mockResolvedValue([]),
24
22
  consoleMessages: vi.fn().mockResolvedValue([]),
@@ -36,8 +34,8 @@ describe('xiaohongshu search', () => {
36
34
  const cmd = getRegistry().get('xiaohongshu/search');
37
35
  expect(cmd?.func).toBeTypeOf('function');
38
36
  const page = createPageMock([
39
- // First evaluate: early login-wall check (returns true)
40
- true,
37
+ // First evaluate: MutationObserver wait (login wall detected)
38
+ 'login_wall',
41
39
  ]);
42
40
  await expect(cmd.func(page, { query: '特斯拉', limit: 5 })).rejects.toThrow('Xiaohongshu search results are blocked behind a login wall');
43
41
  // autoScroll must NOT be called when a login wall is detected early
@@ -49,21 +47,18 @@ describe('xiaohongshu search', () => {
49
47
  const detailUrl = 'https://www.xiaohongshu.com/search_result/68e90be80000000004022e66?xsec_token=test-token&xsec_source=';
50
48
  const authorUrl = 'https://www.xiaohongshu.com/user/profile/635a9c720000000018028b40?xsec_token=user-token&xsec_source=pc_search';
51
49
  const page = createPageMock([
52
- // First evaluate: early login-wall check (returns false → no wall)
53
- false,
54
- // Second evaluate: main DOM extraction
55
- {
56
- loginWall: false,
57
- results: [
58
- {
59
- title: '某鱼买FSD被坑了4万',
60
- author: '随风',
61
- likes: '261',
62
- url: detailUrl,
63
- author_url: authorUrl,
64
- },
65
- ],
66
- },
50
+ // First evaluate: MutationObserver wait (content appeared)
51
+ 'content',
52
+ // Second evaluate: main DOM extraction (returns array directly)
53
+ [
54
+ {
55
+ title: '某鱼买FSD被坑了4万',
56
+ author: '随风',
57
+ likes: '261',
58
+ url: detailUrl,
59
+ author_url: authorUrl,
60
+ },
61
+ ],
67
62
  ]);
68
63
  const result = await cmd.func(page, { query: '特斯拉', limit: 1 });
69
64
  // Should only do one goto (the search page itself), no per-note detail navigation
@@ -84,41 +79,54 @@ describe('xiaohongshu search', () => {
84
79
  const cmd = getRegistry().get('xiaohongshu/search');
85
80
  expect(cmd?.func).toBeTypeOf('function');
86
81
  const page = createPageMock([
87
- // First evaluate: early login-wall check (returns false → no wall)
88
- false,
89
- // Second evaluate: main DOM extraction
90
- {
91
- loginWall: false,
92
- results: [
93
- {
94
- title: 'Result A',
95
- author: 'UserA',
96
- likes: '10',
97
- url: 'https://www.xiaohongshu.com/search_result/aaa',
98
- author_url: '',
99
- },
100
- {
101
- title: '',
102
- author: 'UserB',
103
- likes: '5',
104
- url: 'https://www.xiaohongshu.com/search_result/bbb',
105
- author_url: '',
106
- },
107
- {
108
- title: 'Result C',
109
- author: 'UserC',
110
- likes: '3',
111
- url: 'https://www.xiaohongshu.com/search_result/ccc',
112
- author_url: '',
113
- },
114
- ],
115
- },
82
+ // First evaluate: MutationObserver wait (content appeared)
83
+ 'content',
84
+ // Second evaluate: main DOM extraction (returns array directly)
85
+ [
86
+ {
87
+ title: 'Result A',
88
+ author: 'UserA',
89
+ likes: '10',
90
+ url: 'https://www.xiaohongshu.com/search_result/aaa',
91
+ author_url: '',
92
+ },
93
+ {
94
+ title: '',
95
+ author: 'UserB',
96
+ likes: '5',
97
+ url: 'https://www.xiaohongshu.com/search_result/bbb',
98
+ author_url: '',
99
+ },
100
+ {
101
+ title: 'Result C',
102
+ author: 'UserC',
103
+ likes: '3',
104
+ url: 'https://www.xiaohongshu.com/search_result/ccc',
105
+ author_url: '',
106
+ },
107
+ ],
116
108
  ]);
117
109
  const result = (await cmd.func(page, { query: '测试', limit: 1 }));
118
110
  // limit=1 should return only the first valid-titled result
119
111
  expect(result).toHaveLength(1);
120
112
  expect(result[0]).toMatchObject({ rank: 1, title: 'Result A' });
121
113
  });
114
+ it('waits for content via MutationObserver before extracting', async () => {
115
+ const cmd = getRegistry().get('xiaohongshu/search');
116
+ expect(cmd?.func).toBeTypeOf('function');
117
+ const page = createPageMock([
118
+ // First evaluate: MutationObserver wait (content appeared)
119
+ 'content',
120
+ // Second evaluate: extraction (returns empty array)
121
+ [],
122
+ ]);
123
+ const result = (await cmd.func(page, { query: '测试等待', limit: 5 }));
124
+ expect(result).toHaveLength(0);
125
+ // Only one navigation, no retry
126
+ expect(page.goto).toHaveBeenCalledTimes(1);
127
+ // Two evaluate calls: wait + extraction
128
+ expect(page.evaluate).toHaveBeenCalledTimes(2);
129
+ });
122
130
  });
123
131
  describe('noteIdToDate (ObjectID timestamp parsing)', () => {
124
132
  it('parses a known note ID to the correct China-timezone date', () => {
@@ -0,0 +1,21 @@
1
+ import { isOnYuanbao } from './shared.js';
2
+ declare function normalizeBooleanFlag(value: unknown, fallback: boolean): boolean;
3
+ export declare function convertYuanbaoHtmlToMarkdown(value: string): string;
4
+ export declare function sanitizeYuanbaoResponseText(value: string, promptText: string): string;
5
+ export declare function collectYuanbaoTranscriptAdditions(beforeLines: string[], currentLines: string[], promptText: string): string;
6
+ export declare function pickLatestYuanbaoAssistantCandidate(messages: string[], baselineCount: number, promptText: string): string;
7
+ export declare function updateStableState(previousText: string, stableCount: number, nextText: string): {
8
+ previousText: string;
9
+ stableCount: number;
10
+ };
11
+ export declare const askCommand: import("../../registry.js").CliCommand;
12
+ export declare const __test__: {
13
+ collectYuanbaoTranscriptAdditions: typeof collectYuanbaoTranscriptAdditions;
14
+ convertYuanbaoHtmlToMarkdown: typeof convertYuanbaoHtmlToMarkdown;
15
+ isOnYuanbao: typeof isOnYuanbao;
16
+ normalizeBooleanFlag: typeof normalizeBooleanFlag;
17
+ pickLatestYuanbaoAssistantCandidate: typeof pickLatestYuanbaoAssistantCandidate;
18
+ sanitizeYuanbaoResponseText: typeof sanitizeYuanbaoResponseText;
19
+ updateStableState: typeof updateStableState;
20
+ };
21
+ export {};