@jackwener/opencli 1.1.0 → 1.2.0

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 (769) hide show
  1. package/.agents/skills/cross-project-adapter-migration/SKILL.md +2 -2
  2. package/.github/pull_request_template.md +7 -0
  3. package/.github/workflows/doc-check.yml +36 -0
  4. package/.github/workflows/docs.yml +7 -42
  5. package/CHANGELOG.md +23 -0
  6. package/CLI-EXPLORER.md +9 -8
  7. package/CONTRIBUTING.md +39 -1
  8. package/README.md +33 -19
  9. package/README.zh-CN.md +64 -27
  10. package/SKILL.md +102 -33
  11. package/dist/browser/cdp.d.ts +4 -4
  12. package/dist/browser/cdp.js +45 -17
  13. package/dist/browser/daemon-client.d.ts +2 -1
  14. package/dist/browser/dom-helpers.js +38 -7
  15. package/dist/browser/dom-snapshot.d.ts +86 -0
  16. package/dist/browser/dom-snapshot.js +729 -0
  17. package/dist/browser/dom-snapshot.test.d.ts +11 -0
  18. package/dist/browser/dom-snapshot.test.js +212 -0
  19. package/dist/browser/index.d.ts +2 -0
  20. package/dist/browser/index.js +1 -0
  21. package/dist/browser/page.d.ts +18 -25
  22. package/dist/browser/page.js +44 -5
  23. package/dist/build-manifest.d.ts +11 -4
  24. package/dist/build-manifest.js +79 -34
  25. package/dist/build-manifest.test.js +58 -2
  26. package/dist/cli-manifest.json +4273 -1771
  27. package/dist/cli.d.ts +6 -0
  28. package/dist/cli.js +255 -162
  29. package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
  30. package/dist/clis/apple-podcasts/commands.test.js +76 -0
  31. package/dist/clis/apple-podcasts/search.js +2 -2
  32. package/dist/clis/apple-podcasts/top.js +9 -2
  33. package/dist/clis/arxiv/search.js +1 -1
  34. package/dist/clis/barchart/greeks.js +1 -1
  35. package/dist/clis/barchart/options.js +1 -1
  36. package/dist/clis/barchart/quote.js +1 -1
  37. package/dist/clis/bilibili/download.js +1 -1
  38. package/dist/clis/bilibili/dynamic.js +1 -1
  39. package/dist/clis/bilibili/favorite.js +1 -1
  40. package/dist/clis/bilibili/feed.js +1 -1
  41. package/dist/clis/bilibili/following.js +2 -2
  42. package/dist/clis/bilibili/history.js +1 -1
  43. package/dist/clis/bilibili/me.js +1 -1
  44. package/dist/clis/bilibili/ranking.js +1 -1
  45. package/dist/clis/bilibili/search.js +3 -3
  46. package/dist/clis/bilibili/subtitle.js +2 -2
  47. package/dist/clis/bilibili/user-videos.js +2 -2
  48. package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
  49. package/dist/clis/bloomberg/businessweek.js +17 -0
  50. package/dist/clis/bloomberg/economics.js +17 -0
  51. package/dist/clis/bloomberg/feeds.d.ts +1 -0
  52. package/dist/clis/bloomberg/feeds.js +15 -0
  53. package/dist/clis/bloomberg/industries.d.ts +1 -0
  54. package/dist/clis/bloomberg/industries.js +17 -0
  55. package/dist/clis/bloomberg/main.d.ts +1 -0
  56. package/dist/clis/bloomberg/main.js +17 -0
  57. package/dist/clis/bloomberg/markets.d.ts +1 -0
  58. package/dist/clis/bloomberg/markets.js +17 -0
  59. package/dist/clis/bloomberg/news.d.ts +1 -0
  60. package/dist/clis/bloomberg/news.js +105 -0
  61. package/dist/clis/bloomberg/opinions.d.ts +1 -0
  62. package/dist/clis/bloomberg/opinions.js +17 -0
  63. package/dist/clis/bloomberg/politics.d.ts +1 -0
  64. package/dist/clis/bloomberg/politics.js +17 -0
  65. package/dist/clis/bloomberg/tech.d.ts +1 -0
  66. package/dist/clis/bloomberg/tech.js +17 -0
  67. package/dist/clis/bloomberg/utils.d.ts +34 -0
  68. package/dist/clis/bloomberg/utils.js +364 -0
  69. package/dist/clis/bloomberg/utils.test.d.ts +1 -0
  70. package/dist/clis/bloomberg/utils.test.js +129 -0
  71. package/dist/clis/boss/batchgreet.js +12 -99
  72. package/dist/clis/boss/chatlist.js +9 -26
  73. package/dist/clis/boss/chatmsg.js +11 -42
  74. package/dist/clis/boss/common.d.ts +92 -0
  75. package/dist/clis/boss/common.js +223 -0
  76. package/dist/clis/boss/detail.js +8 -50
  77. package/dist/clis/boss/exchange.js +13 -79
  78. package/dist/clis/boss/greet.js +20 -147
  79. package/dist/clis/boss/invite.js +26 -121
  80. package/dist/clis/boss/joblist.js +6 -31
  81. package/dist/clis/boss/mark.js +12 -85
  82. package/dist/clis/boss/recommend.js +10 -49
  83. package/dist/clis/boss/resume.js +18 -118
  84. package/dist/clis/boss/search.js +13 -61
  85. package/dist/clis/boss/send.js +18 -152
  86. package/dist/clis/boss/stats.js +20 -71
  87. package/dist/clis/chaoxing/assignments.js +1 -1
  88. package/dist/clis/chaoxing/exams.js +1 -1
  89. package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
  90. package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
  91. package/dist/clis/chaoxing/utils.test.d.ts +1 -0
  92. package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
  93. package/dist/clis/chatgpt/read.js +1 -1
  94. package/dist/clis/chatwise/export.js +1 -1
  95. package/dist/clis/chatwise/model.js +2 -2
  96. package/dist/clis/chatwise/screenshot.js +1 -1
  97. package/dist/clis/codex/export.js +1 -1
  98. package/dist/clis/codex/model.js +2 -2
  99. package/dist/clis/codex/screenshot.js +1 -1
  100. package/dist/clis/coupang/add-to-cart.js +3 -4
  101. package/dist/clis/coupang/search.js +2 -4
  102. package/dist/clis/coupang/utils.test.d.ts +1 -0
  103. package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
  104. package/dist/clis/ctrip/search.js +1 -1
  105. package/dist/clis/cursor/export.js +1 -1
  106. package/dist/clis/cursor/model.js +2 -2
  107. package/dist/clis/cursor/screenshot.js +1 -1
  108. package/dist/clis/devto/tag.yaml +34 -0
  109. package/dist/clis/devto/top.yaml +29 -0
  110. package/dist/clis/devto/user.yaml +33 -0
  111. package/dist/clis/douban/book-hot.d.ts +1 -0
  112. package/dist/clis/douban/book-hot.js +14 -0
  113. package/dist/clis/douban/marks.d.ts +1 -0
  114. package/dist/clis/douban/marks.js +115 -0
  115. package/dist/clis/douban/movie-hot.d.ts +1 -0
  116. package/dist/clis/douban/movie-hot.js +14 -0
  117. package/dist/clis/douban/reviews.d.ts +1 -0
  118. package/dist/clis/douban/reviews.js +106 -0
  119. package/dist/clis/douban/search.d.ts +1 -0
  120. package/dist/clis/douban/search.js +16 -0
  121. package/dist/clis/douban/shared.d.ts +4 -0
  122. package/dist/clis/douban/shared.js +155 -0
  123. package/dist/clis/douban/subject.yaml +76 -0
  124. package/dist/clis/douban/top250.yaml +70 -0
  125. package/dist/clis/douban/utils.d.ts +35 -0
  126. package/dist/clis/douban/utils.js +48 -0
  127. package/dist/clis/facebook/add-friend.yaml +43 -0
  128. package/dist/clis/facebook/events.yaml +44 -0
  129. package/dist/clis/facebook/feed.yaml +63 -0
  130. package/dist/clis/facebook/friends.yaml +42 -0
  131. package/dist/clis/facebook/groups.yaml +50 -0
  132. package/dist/clis/facebook/join-group.yaml +44 -0
  133. package/dist/clis/facebook/memories.yaml +39 -0
  134. package/dist/clis/facebook/notifications.yaml +40 -0
  135. package/dist/clis/facebook/profile.yaml +37 -0
  136. package/dist/clis/facebook/search.yaml +46 -0
  137. package/dist/clis/google/news.d.ts +5 -0
  138. package/dist/clis/google/news.js +58 -0
  139. package/dist/clis/google/search.d.ts +10 -0
  140. package/dist/clis/google/search.js +127 -0
  141. package/dist/clis/google/suggest.d.ts +5 -0
  142. package/dist/clis/google/suggest.js +34 -0
  143. package/dist/clis/google/trends.d.ts +5 -0
  144. package/dist/clis/google/trends.js +38 -0
  145. package/dist/clis/google/utils.d.ts +9 -0
  146. package/dist/clis/google/utils.js +23 -0
  147. package/dist/clis/google/utils.test.d.ts +1 -0
  148. package/dist/clis/google/utils.test.js +75 -0
  149. package/dist/clis/grok/ask.d.ts +14 -0
  150. package/dist/clis/grok/ask.js +257 -65
  151. package/dist/clis/grok/ask.test.d.ts +1 -0
  152. package/dist/clis/grok/ask.test.js +36 -0
  153. package/dist/clis/instagram/comment.yaml +52 -0
  154. package/dist/clis/instagram/explore.yaml +43 -0
  155. package/dist/clis/instagram/follow.yaml +41 -0
  156. package/dist/clis/instagram/followers.yaml +51 -0
  157. package/dist/clis/instagram/following.yaml +51 -0
  158. package/dist/clis/instagram/like.yaml +46 -0
  159. package/dist/clis/instagram/profile.yaml +42 -0
  160. package/dist/clis/instagram/save.yaml +46 -0
  161. package/dist/clis/instagram/saved.yaml +40 -0
  162. package/dist/clis/instagram/search.yaml +43 -0
  163. package/dist/clis/instagram/unfollow.yaml +38 -0
  164. package/dist/clis/instagram/unlike.yaml +46 -0
  165. package/dist/clis/instagram/unsave.yaml +46 -0
  166. package/dist/clis/instagram/user.yaml +54 -0
  167. package/dist/clis/jike/comment.js +2 -3
  168. package/dist/clis/jike/create.js +1 -2
  169. package/dist/clis/jike/feed.js +0 -1
  170. package/dist/clis/jike/like.js +1 -2
  171. package/dist/clis/jike/notifications.js +0 -1
  172. package/dist/clis/jike/post.yaml +1 -0
  173. package/dist/clis/jike/repost.js +2 -3
  174. package/dist/clis/jike/search.js +2 -3
  175. package/dist/clis/jike/topic.yaml +1 -0
  176. package/dist/clis/jike/user.yaml +1 -0
  177. package/dist/clis/jimeng/generate.yaml +1 -0
  178. package/dist/clis/jimeng/history.yaml +0 -1
  179. package/dist/clis/linkedin/search.js +7 -7
  180. package/dist/clis/linux-do/category.yaml +2 -0
  181. package/dist/clis/linux-do/search.yaml +4 -3
  182. package/dist/clis/linux-do/topic.yaml +1 -0
  183. package/dist/clis/lobsters/active.yaml +29 -0
  184. package/dist/clis/lobsters/hot.yaml +29 -0
  185. package/dist/clis/lobsters/newest.yaml +29 -0
  186. package/dist/clis/lobsters/tag.yaml +34 -0
  187. package/dist/clis/medium/feed.d.ts +1 -0
  188. package/dist/clis/medium/feed.js +15 -0
  189. package/dist/clis/medium/search.d.ts +1 -0
  190. package/dist/clis/medium/search.js +15 -0
  191. package/dist/clis/medium/shared.d.ts +5 -0
  192. package/dist/clis/medium/shared.js +78 -0
  193. package/dist/clis/medium/user.d.ts +1 -0
  194. package/dist/clis/medium/user.js +15 -0
  195. package/dist/clis/notion/export.js +1 -1
  196. package/dist/clis/reddit/comment.js +3 -4
  197. package/dist/clis/reddit/read.js +4 -5
  198. package/dist/clis/reddit/save.js +2 -3
  199. package/dist/clis/reddit/saved.js +0 -1
  200. package/dist/clis/reddit/search.yaml +1 -0
  201. package/dist/clis/reddit/subreddit.yaml +1 -0
  202. package/dist/clis/reddit/subscribe.js +1 -2
  203. package/dist/clis/reddit/upvote.js +2 -3
  204. package/dist/clis/reddit/upvoted.js +0 -1
  205. package/dist/clis/reddit/user-comments.yaml +1 -0
  206. package/dist/clis/reddit/user-posts.yaml +1 -0
  207. package/dist/clis/reddit/user.yaml +1 -0
  208. package/dist/clis/reuters/search.js +1 -1
  209. package/dist/clis/sinablog/article.d.ts +1 -0
  210. package/dist/clis/sinablog/article.js +14 -0
  211. package/dist/clis/sinablog/hot.d.ts +1 -0
  212. package/dist/clis/sinablog/hot.js +14 -0
  213. package/dist/clis/sinablog/search.d.ts +1 -0
  214. package/dist/clis/sinablog/search.js +51 -0
  215. package/dist/clis/sinablog/shared.d.ts +7 -0
  216. package/dist/clis/sinablog/shared.js +187 -0
  217. package/dist/clis/sinablog/user.d.ts +1 -0
  218. package/dist/clis/sinablog/user.js +15 -0
  219. package/dist/clis/smzdm/search.js +2 -3
  220. package/dist/clis/stackoverflow/search.yaml +1 -0
  221. package/dist/clis/steam/top-sellers.yaml +29 -0
  222. package/dist/clis/substack/feed.d.ts +1 -0
  223. package/dist/clis/substack/feed.js +15 -0
  224. package/dist/clis/substack/publication.d.ts +1 -0
  225. package/dist/clis/substack/publication.js +15 -0
  226. package/dist/clis/substack/search.d.ts +1 -0
  227. package/dist/clis/substack/search.js +77 -0
  228. package/dist/clis/substack/shared.d.ts +4 -0
  229. package/dist/clis/substack/shared.js +129 -0
  230. package/dist/clis/tiktok/comment.yaml +66 -0
  231. package/dist/clis/tiktok/explore.yaml +39 -0
  232. package/dist/clis/tiktok/follow.yaml +39 -0
  233. package/dist/clis/tiktok/following.yaml +46 -0
  234. package/dist/clis/tiktok/friends.yaml +47 -0
  235. package/dist/clis/tiktok/like.yaml +38 -0
  236. package/dist/clis/tiktok/live.yaml +51 -0
  237. package/dist/clis/tiktok/notifications.yaml +52 -0
  238. package/dist/clis/tiktok/profile.yaml +45 -0
  239. package/dist/clis/tiktok/save.yaml +34 -0
  240. package/dist/clis/tiktok/search.yaml +46 -0
  241. package/dist/clis/tiktok/unfollow.yaml +44 -0
  242. package/dist/clis/tiktok/unlike.yaml +38 -0
  243. package/dist/clis/tiktok/unsave.yaml +36 -0
  244. package/dist/clis/tiktok/user.yaml +44 -0
  245. package/dist/clis/twitter/accept.js +2 -2
  246. package/dist/clis/twitter/article.js +2 -2
  247. package/dist/clis/twitter/block.d.ts +1 -0
  248. package/dist/clis/twitter/block.js +88 -0
  249. package/dist/clis/twitter/delete.js +1 -1
  250. package/dist/clis/twitter/download.d.ts +1 -1
  251. package/dist/clis/twitter/download.js +3 -3
  252. package/dist/clis/twitter/followers.js +1 -1
  253. package/dist/clis/twitter/following.js +1 -1
  254. package/dist/clis/twitter/hide-reply.d.ts +1 -0
  255. package/dist/clis/twitter/hide-reply.js +66 -0
  256. package/dist/clis/twitter/like.js +1 -1
  257. package/dist/clis/twitter/post.js +1 -1
  258. package/dist/clis/twitter/reply-dm.js +1 -1
  259. package/dist/clis/twitter/reply.js +2 -2
  260. package/dist/clis/twitter/search.js +1 -1
  261. package/dist/clis/twitter/thread.js +2 -2
  262. package/dist/clis/twitter/timeline.d.ts +23 -0
  263. package/dist/clis/twitter/timeline.js +42 -14
  264. package/dist/clis/twitter/timeline.test.d.ts +1 -0
  265. package/dist/clis/twitter/timeline.test.js +102 -0
  266. package/dist/clis/twitter/trending.d.ts +1 -0
  267. package/dist/clis/twitter/trending.js +91 -0
  268. package/dist/clis/twitter/unblock.d.ts +1 -0
  269. package/dist/clis/twitter/unblock.js +71 -0
  270. package/dist/clis/v2ex/topic.yaml +1 -0
  271. package/dist/clis/weibo/hot.js +0 -1
  272. package/dist/clis/weread/book.js +1 -1
  273. package/dist/clis/weread/highlights.js +1 -1
  274. package/dist/clis/weread/notes.js +1 -1
  275. package/dist/clis/weread/search.js +1 -1
  276. package/dist/clis/wikipedia/random.d.ts +1 -0
  277. package/dist/clis/wikipedia/random.js +19 -0
  278. package/dist/clis/wikipedia/search.js +4 -4
  279. package/dist/clis/wikipedia/summary.js +4 -9
  280. package/dist/clis/wikipedia/trending.d.ts +1 -0
  281. package/dist/clis/wikipedia/trending.js +35 -0
  282. package/dist/clis/wikipedia/utils.d.ts +28 -0
  283. package/dist/clis/wikipedia/utils.js +13 -0
  284. package/dist/clis/xiaohongshu/creator-note-detail.d.ts +15 -0
  285. package/dist/clis/xiaohongshu/creator-note-detail.js +69 -5
  286. package/dist/clis/xiaohongshu/creator-note-detail.test.js +82 -33
  287. package/dist/clis/xiaohongshu/creator-notes.js +35 -5
  288. package/dist/clis/xiaohongshu/creator-notes.test.js +37 -6
  289. package/dist/clis/xiaohongshu/creator-profile.js +0 -1
  290. package/dist/clis/xiaohongshu/creator-stats.js +0 -1
  291. package/dist/clis/xiaohongshu/download.js +2 -3
  292. package/dist/clis/xiaohongshu/feed.yaml +0 -1
  293. package/dist/clis/xiaohongshu/notifications.yaml +0 -1
  294. package/dist/clis/xiaohongshu/search.js +2 -2
  295. package/dist/clis/xiaohongshu/user.js +1 -2
  296. package/dist/clis/xueqiu/earnings-date.yaml +69 -0
  297. package/dist/clis/xueqiu/search.yaml +2 -1
  298. package/dist/clis/xueqiu/stock.yaml +2 -0
  299. package/dist/clis/yahoo-finance/quote.js +1 -2
  300. package/dist/clis/youtube/search.js +1 -1
  301. package/dist/clis/youtube/transcript.js +1 -1
  302. package/dist/clis/youtube/video.js +1 -1
  303. package/dist/clis/zhihu/download.js +1 -2
  304. package/dist/clis/zhihu/question.js +1 -1
  305. package/dist/clis/zhihu/search.yaml +4 -3
  306. package/dist/commanderAdapter.d.ts +21 -0
  307. package/dist/commanderAdapter.js +117 -0
  308. package/dist/{engine.d.ts → discovery.d.ts} +6 -4
  309. package/dist/{engine.js → discovery.js} +93 -104
  310. package/dist/doctor.js +3 -1
  311. package/dist/doctor.test.js +46 -2
  312. package/dist/download/index.d.ts +2 -6
  313. package/dist/download/index.js +19 -46
  314. package/dist/engine.test.d.ts +0 -3
  315. package/dist/engine.test.js +80 -11
  316. package/dist/execution.d.ts +24 -0
  317. package/dist/execution.js +153 -0
  318. package/dist/explore.d.ts +76 -3
  319. package/dist/explore.js +132 -111
  320. package/dist/external-clis.yaml +48 -0
  321. package/dist/external.d.ts +7 -2
  322. package/dist/external.js +11 -14
  323. package/dist/generate.d.ts +41 -2
  324. package/dist/generate.js +5 -4
  325. package/dist/main.js +2 -1
  326. package/dist/pipeline/executor.d.ts +2 -2
  327. package/dist/pipeline/executor.js +2 -2
  328. package/dist/pipeline/executor.test.js +33 -6
  329. package/dist/pipeline/registry.d.ts +1 -1
  330. package/dist/pipeline/steps/browser.d.ts +7 -7
  331. package/dist/pipeline/steps/browser.js +21 -7
  332. package/dist/pipeline/steps/fetch.d.ts +1 -1
  333. package/dist/pipeline/steps/fetch.js +11 -7
  334. package/dist/pipeline/steps/transform.d.ts +6 -5
  335. package/dist/pipeline/steps/transform.js +30 -9
  336. package/dist/pipeline/template.d.ts +6 -6
  337. package/dist/pipeline/template.js +43 -5
  338. package/dist/pipeline/template.test.js +18 -0
  339. package/dist/pipeline/transform.test.js +11 -0
  340. package/dist/plugin.d.ts +31 -0
  341. package/dist/plugin.js +216 -0
  342. package/dist/plugin.test.d.ts +4 -0
  343. package/dist/plugin.test.js +76 -0
  344. package/dist/registry-api.d.ts +11 -0
  345. package/dist/registry-api.js +9 -0
  346. package/dist/registry.d.ts +13 -0
  347. package/dist/registry.js +8 -1
  348. package/dist/runtime.d.ts +5 -0
  349. package/dist/runtime.js +8 -0
  350. package/dist/serialization.d.ts +34 -0
  351. package/dist/serialization.js +63 -0
  352. package/dist/synthesize.d.ts +94 -4
  353. package/dist/synthesize.js +5 -4
  354. package/dist/types.d.ts +43 -27
  355. package/dist/validate.js +8 -2
  356. package/docs/.vitepress/config.mts +20 -7
  357. package/docs/adapters/browser/arxiv.md +27 -0
  358. package/docs/adapters/browser/barchart.md +33 -0
  359. package/docs/adapters/browser/bilibili.md +9 -0
  360. package/docs/adapters/browser/bloomberg.md +70 -0
  361. package/docs/adapters/browser/chaoxing.md +39 -0
  362. package/docs/adapters/browser/devto.md +35 -0
  363. package/docs/adapters/browser/douban.md +38 -0
  364. package/docs/adapters/browser/facebook.md +36 -0
  365. package/docs/adapters/browser/google.md +62 -0
  366. package/docs/adapters/browser/grok.md +53 -0
  367. package/docs/adapters/browser/hf.md +42 -0
  368. package/docs/adapters/browser/instagram.md +46 -0
  369. package/docs/adapters/browser/jike.md +45 -0
  370. package/docs/adapters/browser/jimeng.md +39 -0
  371. package/docs/adapters/browser/linux-do.md +45 -0
  372. package/docs/adapters/browser/lobsters.md +32 -0
  373. package/docs/adapters/browser/medium.md +32 -0
  374. package/docs/adapters/browser/reddit.md +9 -0
  375. package/docs/adapters/browser/sinablog.md +36 -0
  376. package/docs/adapters/browser/sinafinance.md +35 -0
  377. package/docs/adapters/browser/stackoverflow.md +35 -0
  378. package/docs/adapters/browser/steam.md +26 -0
  379. package/docs/adapters/browser/substack.md +38 -0
  380. package/docs/adapters/browser/tiktok.md +68 -0
  381. package/docs/adapters/browser/twitter.md +3 -0
  382. package/docs/adapters/browser/weread.md +48 -0
  383. package/docs/adapters/browser/wikipedia.md +39 -0
  384. package/docs/adapters/browser/xiaohongshu.md +5 -1
  385. package/docs/adapters/browser/xueqiu.md +10 -0
  386. package/docs/adapters/browser/yahoo-finance.md +6 -5
  387. package/docs/adapters/desktop/antigravity.md +6 -0
  388. package/docs/adapters/desktop/chatgpt.md +5 -4
  389. package/docs/adapters/desktop/codex.md +5 -1
  390. package/docs/adapters/desktop/cursor.md +4 -0
  391. package/docs/adapters/desktop/discord.md +7 -7
  392. package/docs/adapters/index.md +14 -4
  393. package/docs/advanced/download.md +4 -4
  394. package/docs/developer/architecture.md +17 -4
  395. package/docs/guide/getting-started.md +1 -0
  396. package/docs/guide/plugins.md +153 -0
  397. package/docs/zh/guide/plugins.md +107 -0
  398. package/extension/src/background.ts +18 -11
  399. package/package.json +10 -5
  400. package/scripts/check-doc-coverage.sh +69 -0
  401. package/scripts/clean-dist.cjs +13 -0
  402. package/scripts/copy-yaml.cjs +7 -0
  403. package/src/browser/cdp.ts +77 -32
  404. package/src/browser/daemon-client.ts +2 -1
  405. package/src/browser/dom-helpers.ts +38 -7
  406. package/src/browser/dom-snapshot.test.ts +249 -0
  407. package/src/browser/dom-snapshot.ts +770 -0
  408. package/src/browser/index.ts +2 -0
  409. package/src/browser/page.ts +57 -20
  410. package/src/build-manifest.test.ts +70 -2
  411. package/src/build-manifest.ts +114 -40
  412. package/src/cli.ts +287 -139
  413. package/src/clis/apple-podcasts/commands.test.ts +95 -0
  414. package/src/clis/apple-podcasts/search.ts +2 -2
  415. package/src/clis/apple-podcasts/top.ts +12 -2
  416. package/src/clis/arxiv/search.ts +1 -1
  417. package/src/clis/barchart/greeks.ts +1 -1
  418. package/src/clis/barchart/options.ts +1 -1
  419. package/src/clis/barchart/quote.ts +1 -1
  420. package/src/clis/bilibili/download.ts +1 -1
  421. package/src/clis/bilibili/dynamic.ts +1 -1
  422. package/src/clis/bilibili/favorite.ts +1 -1
  423. package/src/clis/bilibili/feed.ts +1 -1
  424. package/src/clis/bilibili/following.ts +2 -2
  425. package/src/clis/bilibili/history.ts +1 -1
  426. package/src/clis/bilibili/me.ts +1 -1
  427. package/src/clis/bilibili/ranking.ts +1 -1
  428. package/src/clis/bilibili/search.ts +3 -3
  429. package/src/clis/bilibili/subtitle.ts +2 -2
  430. package/src/clis/bilibili/user-videos.ts +2 -2
  431. package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
  432. package/src/clis/bloomberg/businessweek.ts +18 -0
  433. package/src/clis/bloomberg/economics.ts +18 -0
  434. package/src/clis/bloomberg/feeds.ts +16 -0
  435. package/src/clis/bloomberg/industries.ts +18 -0
  436. package/src/clis/bloomberg/main.ts +18 -0
  437. package/src/clis/bloomberg/markets.ts +18 -0
  438. package/src/clis/bloomberg/news.ts +136 -0
  439. package/src/clis/bloomberg/opinions.ts +18 -0
  440. package/src/clis/bloomberg/politics.ts +18 -0
  441. package/src/clis/bloomberg/tech.ts +18 -0
  442. package/src/clis/bloomberg/utils.test.ts +135 -0
  443. package/src/clis/bloomberg/utils.ts +429 -0
  444. package/src/clis/boss/batchgreet.ts +16 -108
  445. package/src/clis/boss/chatlist.ts +13 -27
  446. package/src/clis/boss/chatmsg.ts +16 -40
  447. package/src/clis/boss/common.ts +287 -0
  448. package/src/clis/boss/detail.ts +9 -55
  449. package/src/clis/boss/exchange.ts +15 -89
  450. package/src/clis/boss/greet.ts +25 -162
  451. package/src/clis/boss/invite.ts +36 -133
  452. package/src/clis/boss/joblist.ts +7 -36
  453. package/src/clis/boss/mark.ts +13 -94
  454. package/src/clis/boss/recommend.ts +12 -57
  455. package/src/clis/boss/resume.ts +19 -124
  456. package/src/clis/boss/search.ts +14 -67
  457. package/src/clis/boss/send.ts +22 -162
  458. package/src/clis/boss/stats.ts +21 -76
  459. package/src/clis/chaoxing/assignments.ts +1 -1
  460. package/src/clis/chaoxing/exams.ts +1 -1
  461. package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
  462. package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
  463. package/src/clis/chatgpt/read.ts +1 -1
  464. package/src/clis/chatwise/export.ts +1 -1
  465. package/src/clis/chatwise/model.ts +2 -2
  466. package/src/clis/chatwise/screenshot.ts +1 -1
  467. package/src/clis/codex/export.ts +1 -1
  468. package/src/clis/codex/model.ts +2 -2
  469. package/src/clis/codex/screenshot.ts +1 -1
  470. package/src/clis/coupang/add-to-cart.ts +3 -4
  471. package/src/clis/coupang/search.ts +2 -4
  472. package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
  473. package/src/clis/ctrip/search.ts +1 -1
  474. package/src/clis/cursor/export.ts +1 -1
  475. package/src/clis/cursor/model.ts +2 -2
  476. package/src/clis/cursor/screenshot.ts +1 -1
  477. package/src/clis/devto/tag.yaml +34 -0
  478. package/src/clis/devto/top.yaml +29 -0
  479. package/src/clis/devto/user.yaml +33 -0
  480. package/src/clis/douban/book-hot.ts +15 -0
  481. package/src/clis/douban/marks.ts +135 -0
  482. package/src/clis/douban/movie-hot.ts +15 -0
  483. package/src/clis/douban/reviews.ts +127 -0
  484. package/src/clis/douban/search.ts +17 -0
  485. package/src/clis/douban/shared.ts +165 -0
  486. package/src/clis/douban/subject.yaml +76 -0
  487. package/src/clis/douban/top250.yaml +70 -0
  488. package/src/clis/douban/utils.ts +81 -0
  489. package/src/clis/facebook/add-friend.yaml +43 -0
  490. package/src/clis/facebook/events.yaml +44 -0
  491. package/src/clis/facebook/feed.yaml +63 -0
  492. package/src/clis/facebook/friends.yaml +42 -0
  493. package/src/clis/facebook/groups.yaml +50 -0
  494. package/src/clis/facebook/join-group.yaml +44 -0
  495. package/src/clis/facebook/memories.yaml +39 -0
  496. package/src/clis/facebook/notifications.yaml +40 -0
  497. package/src/clis/facebook/profile.yaml +37 -0
  498. package/src/clis/facebook/search.yaml +46 -0
  499. package/src/clis/google/news.ts +66 -0
  500. package/src/clis/google/search.ts +133 -0
  501. package/src/clis/google/suggest.ts +40 -0
  502. package/src/clis/google/trends.ts +44 -0
  503. package/src/clis/google/utils.test.ts +82 -0
  504. package/src/clis/google/utils.ts +24 -0
  505. package/src/clis/grok/ask.test.ts +53 -0
  506. package/src/clis/grok/ask.ts +300 -69
  507. package/src/clis/instagram/comment.yaml +52 -0
  508. package/src/clis/instagram/explore.yaml +43 -0
  509. package/src/clis/instagram/follow.yaml +41 -0
  510. package/src/clis/instagram/followers.yaml +51 -0
  511. package/src/clis/instagram/following.yaml +51 -0
  512. package/src/clis/instagram/like.yaml +46 -0
  513. package/src/clis/instagram/profile.yaml +42 -0
  514. package/src/clis/instagram/save.yaml +46 -0
  515. package/src/clis/instagram/saved.yaml +40 -0
  516. package/src/clis/instagram/search.yaml +43 -0
  517. package/src/clis/instagram/unfollow.yaml +38 -0
  518. package/src/clis/instagram/unlike.yaml +46 -0
  519. package/src/clis/instagram/unsave.yaml +46 -0
  520. package/src/clis/instagram/user.yaml +54 -0
  521. package/src/clis/jike/comment.ts +2 -3
  522. package/src/clis/jike/create.ts +1 -2
  523. package/src/clis/jike/feed.ts +0 -1
  524. package/src/clis/jike/like.ts +1 -2
  525. package/src/clis/jike/notifications.ts +0 -1
  526. package/src/clis/jike/post.yaml +1 -0
  527. package/src/clis/jike/repost.ts +2 -3
  528. package/src/clis/jike/search.ts +2 -3
  529. package/src/clis/jike/topic.yaml +1 -0
  530. package/src/clis/jike/user.yaml +1 -0
  531. package/src/clis/jimeng/generate.yaml +1 -0
  532. package/src/clis/jimeng/history.yaml +0 -1
  533. package/src/clis/linkedin/search.ts +7 -7
  534. package/src/clis/linux-do/category.yaml +2 -0
  535. package/src/clis/linux-do/search.yaml +4 -3
  536. package/src/clis/linux-do/topic.yaml +1 -0
  537. package/src/clis/lobsters/active.yaml +29 -0
  538. package/src/clis/lobsters/hot.yaml +29 -0
  539. package/src/clis/lobsters/newest.yaml +29 -0
  540. package/src/clis/lobsters/tag.yaml +34 -0
  541. package/src/clis/medium/feed.ts +16 -0
  542. package/src/clis/medium/search.ts +16 -0
  543. package/src/clis/medium/shared.ts +83 -0
  544. package/src/clis/medium/user.ts +16 -0
  545. package/src/clis/notion/export.ts +1 -1
  546. package/src/clis/reddit/comment.ts +3 -4
  547. package/src/clis/reddit/read.ts +4 -5
  548. package/src/clis/reddit/save.ts +2 -3
  549. package/src/clis/reddit/saved.ts +0 -1
  550. package/src/clis/reddit/search.yaml +1 -0
  551. package/src/clis/reddit/subreddit.yaml +1 -0
  552. package/src/clis/reddit/subscribe.ts +1 -2
  553. package/src/clis/reddit/upvote.ts +2 -3
  554. package/src/clis/reddit/upvoted.ts +0 -1
  555. package/src/clis/reddit/user-comments.yaml +1 -0
  556. package/src/clis/reddit/user-posts.yaml +1 -0
  557. package/src/clis/reddit/user.yaml +1 -0
  558. package/src/clis/reuters/search.ts +1 -1
  559. package/src/clis/sinablog/article.ts +15 -0
  560. package/src/clis/sinablog/hot.ts +15 -0
  561. package/src/clis/sinablog/search.ts +56 -0
  562. package/src/clis/sinablog/shared.ts +198 -0
  563. package/src/clis/sinablog/user.ts +16 -0
  564. package/src/clis/smzdm/search.ts +2 -3
  565. package/src/clis/stackoverflow/search.yaml +1 -0
  566. package/src/clis/steam/top-sellers.yaml +29 -0
  567. package/src/clis/substack/feed.ts +16 -0
  568. package/src/clis/substack/publication.ts +16 -0
  569. package/src/clis/substack/search.ts +91 -0
  570. package/src/clis/substack/shared.ts +132 -0
  571. package/src/clis/tiktok/comment.yaml +66 -0
  572. package/src/clis/tiktok/explore.yaml +39 -0
  573. package/src/clis/tiktok/follow.yaml +39 -0
  574. package/src/clis/tiktok/following.yaml +46 -0
  575. package/src/clis/tiktok/friends.yaml +47 -0
  576. package/src/clis/tiktok/like.yaml +38 -0
  577. package/src/clis/tiktok/live.yaml +51 -0
  578. package/src/clis/tiktok/notifications.yaml +52 -0
  579. package/src/clis/tiktok/profile.yaml +45 -0
  580. package/src/clis/tiktok/save.yaml +34 -0
  581. package/src/clis/tiktok/search.yaml +46 -0
  582. package/src/clis/tiktok/unfollow.yaml +44 -0
  583. package/src/clis/tiktok/unlike.yaml +38 -0
  584. package/src/clis/tiktok/unsave.yaml +36 -0
  585. package/src/clis/tiktok/user.yaml +44 -0
  586. package/src/clis/twitter/accept.ts +2 -2
  587. package/src/clis/twitter/article.ts +2 -2
  588. package/src/clis/twitter/block.ts +92 -0
  589. package/src/clis/twitter/delete.ts +1 -1
  590. package/src/clis/twitter/download.ts +3 -3
  591. package/src/clis/twitter/followers.ts +1 -1
  592. package/src/clis/twitter/following.ts +1 -1
  593. package/src/clis/twitter/hide-reply.ts +70 -0
  594. package/src/clis/twitter/like.ts +1 -1
  595. package/src/clis/twitter/post.ts +1 -1
  596. package/src/clis/twitter/reply-dm.ts +1 -1
  597. package/src/clis/twitter/reply.ts +2 -2
  598. package/src/clis/twitter/search.ts +1 -1
  599. package/src/clis/twitter/thread.ts +2 -2
  600. package/src/clis/twitter/timeline.test.ts +109 -0
  601. package/src/clis/twitter/timeline.ts +59 -19
  602. package/src/clis/twitter/trending.ts +113 -0
  603. package/src/clis/twitter/unblock.ts +75 -0
  604. package/src/clis/v2ex/topic.yaml +1 -0
  605. package/src/clis/weibo/hot.ts +0 -1
  606. package/src/clis/weread/book.ts +1 -1
  607. package/src/clis/weread/highlights.ts +1 -1
  608. package/src/clis/weread/notes.ts +1 -1
  609. package/src/clis/weread/search.ts +1 -1
  610. package/src/clis/wikipedia/random.ts +19 -0
  611. package/src/clis/wikipedia/search.ts +11 -5
  612. package/src/clis/wikipedia/summary.ts +4 -9
  613. package/src/clis/wikipedia/trending.ts +41 -0
  614. package/src/clis/wikipedia/utils.ts +31 -0
  615. package/src/clis/xiaohongshu/creator-note-detail.test.ts +84 -33
  616. package/src/clis/xiaohongshu/creator-note-detail.ts +89 -5
  617. package/src/clis/xiaohongshu/creator-notes.test.ts +41 -6
  618. package/src/clis/xiaohongshu/creator-notes.ts +44 -5
  619. package/src/clis/xiaohongshu/creator-profile.ts +0 -1
  620. package/src/clis/xiaohongshu/creator-stats.ts +0 -1
  621. package/src/clis/xiaohongshu/download.ts +2 -3
  622. package/src/clis/xiaohongshu/feed.yaml +0 -1
  623. package/src/clis/xiaohongshu/notifications.yaml +0 -1
  624. package/src/clis/xiaohongshu/search.ts +2 -2
  625. package/src/clis/xiaohongshu/user.ts +1 -2
  626. package/src/clis/xueqiu/earnings-date.yaml +69 -0
  627. package/src/clis/xueqiu/search.yaml +2 -1
  628. package/src/clis/xueqiu/stock.yaml +2 -0
  629. package/src/clis/yahoo-finance/quote.ts +1 -2
  630. package/src/clis/youtube/search.ts +1 -1
  631. package/src/clis/youtube/transcript.ts +1 -1
  632. package/src/clis/youtube/video.ts +1 -1
  633. package/src/clis/zhihu/download.ts +1 -2
  634. package/src/clis/zhihu/question.ts +1 -1
  635. package/src/clis/zhihu/search.yaml +4 -3
  636. package/src/commanderAdapter.ts +120 -0
  637. package/src/discovery.ts +277 -0
  638. package/src/doctor.test.ts +59 -2
  639. package/src/doctor.ts +4 -2
  640. package/src/download/index.ts +21 -54
  641. package/src/engine.test.ts +85 -11
  642. package/src/execution.ts +164 -0
  643. package/src/explore.ts +211 -117
  644. package/src/external-clis.yaml +9 -0
  645. package/src/external.ts +15 -12
  646. package/src/generate.ts +58 -9
  647. package/src/main.ts +2 -1
  648. package/src/pipeline/executor.test.ts +35 -6
  649. package/src/pipeline/executor.ts +11 -7
  650. package/src/pipeline/registry.ts +3 -3
  651. package/src/pipeline/steps/browser.ts +29 -15
  652. package/src/pipeline/steps/fetch.ts +18 -13
  653. package/src/pipeline/steps/transform.ts +40 -15
  654. package/src/pipeline/template.test.ts +18 -0
  655. package/src/pipeline/template.ts +86 -13
  656. package/src/pipeline/transform.test.ts +15 -2
  657. package/src/plugin.test.ts +86 -0
  658. package/src/plugin.ts +254 -0
  659. package/src/registry-api.ts +12 -0
  660. package/src/registry.ts +24 -1
  661. package/src/runtime.ts +9 -0
  662. package/src/serialization.ts +79 -0
  663. package/src/synthesize.ts +102 -21
  664. package/src/types.ts +45 -13
  665. package/src/validate.ts +19 -4
  666. package/tests/e2e/browser-public.test.ts +36 -0
  667. package/tests/e2e/public-commands.test.ts +119 -1
  668. package/dist/clis/feishu/new.d.ts +0 -1
  669. package/dist/clis/feishu/new.js +0 -27
  670. package/dist/clis/feishu/read.d.ts +0 -1
  671. package/dist/clis/feishu/read.js +0 -40
  672. package/dist/clis/feishu/search.d.ts +0 -1
  673. package/dist/clis/feishu/search.js +0 -30
  674. package/dist/clis/feishu/send.d.ts +0 -1
  675. package/dist/clis/feishu/send.js +0 -39
  676. package/dist/clis/feishu/status.d.ts +0 -1
  677. package/dist/clis/feishu/status.js +0 -28
  678. package/dist/clis/neteasemusic/like.d.ts +0 -1
  679. package/dist/clis/neteasemusic/like.js +0 -25
  680. package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
  681. package/dist/clis/neteasemusic/lyrics.js +0 -47
  682. package/dist/clis/neteasemusic/next.d.ts +0 -1
  683. package/dist/clis/neteasemusic/next.js +0 -26
  684. package/dist/clis/neteasemusic/play.d.ts +0 -1
  685. package/dist/clis/neteasemusic/play.js +0 -26
  686. package/dist/clis/neteasemusic/playing.d.ts +0 -1
  687. package/dist/clis/neteasemusic/playing.js +0 -59
  688. package/dist/clis/neteasemusic/playlist.d.ts +0 -1
  689. package/dist/clis/neteasemusic/playlist.js +0 -46
  690. package/dist/clis/neteasemusic/prev.d.ts +0 -1
  691. package/dist/clis/neteasemusic/prev.js +0 -25
  692. package/dist/clis/neteasemusic/search.d.ts +0 -1
  693. package/dist/clis/neteasemusic/search.js +0 -52
  694. package/dist/clis/neteasemusic/status.d.ts +0 -1
  695. package/dist/clis/neteasemusic/status.js +0 -16
  696. package/dist/clis/neteasemusic/volume.d.ts +0 -1
  697. package/dist/clis/neteasemusic/volume.js +0 -54
  698. package/dist/clis/twitter/trending.yaml +0 -46
  699. package/dist/clis/wechat/chats.d.ts +0 -1
  700. package/dist/clis/wechat/chats.js +0 -28
  701. package/dist/clis/wechat/contacts.d.ts +0 -1
  702. package/dist/clis/wechat/contacts.js +0 -28
  703. package/dist/clis/wechat/read.d.ts +0 -1
  704. package/dist/clis/wechat/read.js +0 -58
  705. package/dist/clis/wechat/search.d.ts +0 -1
  706. package/dist/clis/wechat/search.js +0 -31
  707. package/dist/clis/wechat/send.d.ts +0 -1
  708. package/dist/clis/wechat/send.js +0 -42
  709. package/dist/clis/wechat/status.d.ts +0 -1
  710. package/dist/clis/wechat/status.js +0 -29
  711. package/dist/pipeline.d.ts +0 -7
  712. package/dist/pipeline.js +0 -7
  713. package/docs/adapters/browser/github.md +0 -26
  714. package/docs/adapters/desktop/feishu.md +0 -20
  715. package/docs/adapters/desktop/neteasemusic.md +0 -31
  716. package/docs/adapters/desktop/wechat.md +0 -28
  717. package/docs/public/CNAME +0 -1
  718. package/src/clis/antigravity/README.md +0 -5
  719. package/src/clis/antigravity/README.zh-CN.md +0 -51
  720. package/src/clis/chaoxing/README.md +0 -14
  721. package/src/clis/chaoxing/README.zh-CN.md +0 -35
  722. package/src/clis/chatgpt/README.md +0 -5
  723. package/src/clis/chatgpt/README.zh-CN.md +0 -44
  724. package/src/clis/chatwise/README.md +0 -5
  725. package/src/clis/chatwise/README.zh-CN.md +0 -38
  726. package/src/clis/codex/README.md +0 -5
  727. package/src/clis/codex/README.zh-CN.md +0 -33
  728. package/src/clis/cursor/README.md +0 -5
  729. package/src/clis/cursor/README.zh-CN.md +0 -33
  730. package/src/clis/discord-app/README.md +0 -5
  731. package/src/clis/discord-app/README.zh-CN.md +0 -28
  732. package/src/clis/feishu/README.md +0 -5
  733. package/src/clis/feishu/README.zh-CN.md +0 -20
  734. package/src/clis/feishu/new.ts +0 -32
  735. package/src/clis/feishu/read.ts +0 -48
  736. package/src/clis/feishu/search.ts +0 -35
  737. package/src/clis/feishu/send.ts +0 -46
  738. package/src/clis/feishu/status.ts +0 -34
  739. package/src/clis/neteasemusic/README.md +0 -5
  740. package/src/clis/neteasemusic/README.zh-CN.md +0 -31
  741. package/src/clis/neteasemusic/like.ts +0 -28
  742. package/src/clis/neteasemusic/lyrics.ts +0 -53
  743. package/src/clis/neteasemusic/next.ts +0 -30
  744. package/src/clis/neteasemusic/play.ts +0 -30
  745. package/src/clis/neteasemusic/playing.ts +0 -62
  746. package/src/clis/neteasemusic/playlist.ts +0 -51
  747. package/src/clis/neteasemusic/prev.ts +0 -29
  748. package/src/clis/neteasemusic/search.ts +0 -58
  749. package/src/clis/neteasemusic/status.ts +0 -18
  750. package/src/clis/neteasemusic/volume.ts +0 -61
  751. package/src/clis/notion/README.md +0 -5
  752. package/src/clis/notion/README.zh-CN.md +0 -29
  753. package/src/clis/twitter/trending.yaml +0 -46
  754. package/src/clis/wechat/README.md +0 -5
  755. package/src/clis/wechat/README.zh-CN.md +0 -28
  756. package/src/clis/wechat/chats.ts +0 -33
  757. package/src/clis/wechat/contacts.ts +0 -33
  758. package/src/clis/wechat/read.ts +0 -72
  759. package/src/clis/wechat/search.ts +0 -36
  760. package/src/clis/wechat/send.ts +0 -49
  761. package/src/clis/wechat/status.ts +0 -35
  762. package/src/engine.ts +0 -274
  763. package/src/pipeline.ts +0 -8
  764. /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
  765. /package/dist/{chaoxing.test.d.ts → clis/bloomberg/businessweek.d.ts} +0 -0
  766. /package/dist/{coupang.test.d.ts → clis/bloomberg/economics.d.ts} +0 -0
  767. /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
  768. /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
  769. /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
@@ -7,7 +7,7 @@ cli({
7
7
  domain: 'weread.qq.com',
8
8
  strategy: Strategy.COOKIE,
9
9
  args: [
10
- { name: 'bookId', positional: true, required: true, help: 'Book ID (from shelf or search results)' },
10
+ { name: 'book-id', positional: true, required: true, help: 'Book ID (from shelf or search results)' },
11
11
  { name: 'limit', type: 'int', default: 20, help: 'Max results' },
12
12
  ],
13
13
  columns: ['chapter', 'text', 'review', 'createTime'],
@@ -8,7 +8,7 @@ cli({
8
8
  strategy: Strategy.PUBLIC,
9
9
  browser: false,
10
10
  args: [
11
- { name: 'keyword', positional: true, required: true, help: 'Search keyword' },
11
+ { name: 'query', positional: true, required: true, help: 'Search keyword' },
12
12
  { name: 'limit', type: 'int', default: 10, help: 'Max results' },
13
13
  ],
14
14
  columns: ['rank', 'title', 'author', 'bookId'],
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { CliError } from '../../errors.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
+ import { formatSummaryRow, wikiFetch } from './utils.js';
4
+ cli({
5
+ site: 'wikipedia',
6
+ name: 'random',
7
+ description: 'Get a random Wikipedia article',
8
+ strategy: Strategy.PUBLIC,
9
+ browser: false,
10
+ args: [{ name: 'lang', default: 'en', help: 'Language code (e.g. en, zh, ja)' }],
11
+ columns: ['title', 'description', 'extract', 'url'],
12
+ func: async (_page, args) => {
13
+ const lang = args.lang || 'en';
14
+ const data = (await wikiFetch(lang, '/api/rest_v1/page/random/summary'));
15
+ if (!data?.title)
16
+ throw new CliError('NOT_FOUND', 'No random article returned', 'Try again');
17
+ return [formatSummaryRow(data, lang)];
18
+ },
19
+ });
@@ -1,5 +1,5 @@
1
- import { cli, Strategy } from '../../registry.js';
2
1
  import { CliError } from '../../errors.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
3
  import { wikiFetch } from './utils.js';
4
4
  cli({
5
5
  site: 'wikipedia',
@@ -8,7 +8,7 @@ cli({
8
8
  strategy: Strategy.PUBLIC,
9
9
  browser: false,
10
10
  args: [
11
- { name: 'keyword', positional: true, required: true, help: 'Search keyword' },
11
+ { name: 'query', positional: true, required: true, help: 'Search keyword' },
12
12
  { name: 'limit', type: 'int', default: 10, help: 'Max results' },
13
13
  { name: 'lang', default: 'en', help: 'Language code (e.g. en, zh, ja)' },
14
14
  ],
@@ -16,8 +16,8 @@ cli({
16
16
  func: async (_page, args) => {
17
17
  const limit = Math.max(1, Math.min(Number(args.limit), 50));
18
18
  const lang = args.lang || 'en';
19
- const q = encodeURIComponent(args.keyword);
20
- const data = await wikiFetch(lang, `/w/api.php?action=query&list=search&srsearch=${q}&srlimit=${limit}&format=json&utf8=1`);
19
+ const q = encodeURIComponent(args.query);
20
+ const data = (await wikiFetch(lang, `/w/api.php?action=query&list=search&srsearch=${q}&srlimit=${limit}&format=json&utf8=1`));
21
21
  const results = data?.query?.search;
22
22
  if (!results?.length)
23
23
  throw new CliError('NOT_FOUND', 'No articles found', 'Try a different keyword');
@@ -1,6 +1,6 @@
1
- import { cli, Strategy } from '../../registry.js';
2
1
  import { CliError } from '../../errors.js';
3
- import { wikiFetch } from './utils.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
+ import { formatSummaryRow, wikiFetch } from './utils.js';
4
4
  cli({
5
5
  site: 'wikipedia',
6
6
  name: 'summary',
@@ -15,14 +15,9 @@ cli({
15
15
  func: async (_page, args) => {
16
16
  const lang = args.lang || 'en';
17
17
  const title = encodeURIComponent(args.title.replace(/ /g, '_'));
18
- const data = await wikiFetch(lang, `/api/rest_v1/page/summary/${title}`);
18
+ const data = (await wikiFetch(lang, `/api/rest_v1/page/summary/${title}`));
19
19
  if (!data?.title)
20
20
  throw new CliError('NOT_FOUND', `Article "${args.title}" not found`, 'Try searching first: opencli wikipedia search <keyword>');
21
- return [{
22
- title: data.title,
23
- description: data.description ?? '-',
24
- extract: (data.extract ?? '').slice(0, 300),
25
- url: data.content_urls?.desktop?.page ?? `https://${lang}.wikipedia.org/wiki/${title}`,
26
- }];
21
+ return [formatSummaryRow(data, lang)];
27
22
  },
28
23
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,35 @@
1
+ import { CliError } from '../../errors.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
+ import { DESC_MAX_LEN, wikiFetch } from './utils.js';
4
+ cli({
5
+ site: 'wikipedia',
6
+ name: 'trending',
7
+ description: 'Most-read Wikipedia articles (yesterday)',
8
+ strategy: Strategy.PUBLIC,
9
+ browser: false,
10
+ args: [
11
+ { name: 'limit', type: 'int', default: 10, help: 'Max results' },
12
+ { name: 'lang', default: 'en', help: 'Language code (e.g. en, zh, ja)' },
13
+ ],
14
+ columns: ['rank', 'title', 'description', 'views'],
15
+ func: async (_page, args) => {
16
+ const lang = args.lang || 'en';
17
+ const limit = Math.max(1, Math.min(Number(args.limit), 50));
18
+ // Use yesterday's UTC date — Wikipedia API expects UTC and yesterday
19
+ // guarantees data availability (today's aggregation may be incomplete).
20
+ const d = new Date(Date.now() - 86_400_000);
21
+ const yyyy = d.getUTCFullYear();
22
+ const mm = String(d.getUTCMonth() + 1).padStart(2, '0');
23
+ const dd = String(d.getUTCDate()).padStart(2, '0');
24
+ const data = (await wikiFetch(lang, `/api/rest_v1/feed/featured/${yyyy}/${mm}/${dd}`));
25
+ const articles = data?.mostread?.articles;
26
+ if (!articles?.length)
27
+ throw new CliError('NOT_FOUND', 'No trending articles available', 'Try a different language with --lang');
28
+ return articles.slice(0, limit).map((a, i) => ({
29
+ rank: i + 1,
30
+ title: a.title ?? '-',
31
+ description: (a.description ?? '-').slice(0, DESC_MAX_LEN),
32
+ views: a.views ?? 0,
33
+ }));
34
+ },
35
+ });
@@ -5,4 +5,32 @@
5
5
  * REST API: https://en.wikipedia.org/api/rest_v1/
6
6
  * Action API: https://en.wikipedia.org/w/api.php
7
7
  */
8
+ /** Maximum character length for article extract fields. */
9
+ export declare const EXTRACT_MAX_LEN = 300;
10
+ /** Maximum character length for short description fields. */
11
+ export declare const DESC_MAX_LEN = 80;
12
+ /** Response shape shared by /page/summary and /page/random/summary endpoints. */
13
+ export interface WikiSummary {
14
+ title?: string;
15
+ description?: string;
16
+ extract?: string;
17
+ content_urls?: {
18
+ desktop?: {
19
+ page?: string;
20
+ };
21
+ };
22
+ }
23
+ /** Article entry returned by the /feed/featured most-read endpoint. */
24
+ export interface WikiMostReadArticle {
25
+ title?: string;
26
+ description?: string;
27
+ views?: number;
28
+ }
8
29
  export declare function wikiFetch(lang: string, path: string): Promise<unknown>;
30
+ /** Map a WikiSummary API response to the standard output row. */
31
+ export declare function formatSummaryRow(data: WikiSummary, lang: string): {
32
+ title: string;
33
+ description: string;
34
+ extract: string;
35
+ url: string;
36
+ };
@@ -6,6 +6,10 @@
6
6
  * Action API: https://en.wikipedia.org/w/api.php
7
7
  */
8
8
  import { CliError } from '../../errors.js';
9
+ /** Maximum character length for article extract fields. */
10
+ export const EXTRACT_MAX_LEN = 300;
11
+ /** Maximum character length for short description fields. */
12
+ export const DESC_MAX_LEN = 80;
9
13
  export async function wikiFetch(lang, path) {
10
14
  const url = `https://${lang}.wikipedia.org${path}`;
11
15
  const resp = await fetch(url, {
@@ -16,3 +20,12 @@ export async function wikiFetch(lang, path) {
16
20
  }
17
21
  return resp.json();
18
22
  }
23
+ /** Map a WikiSummary API response to the standard output row. */
24
+ export function formatSummaryRow(data, lang) {
25
+ return {
26
+ title: data.title,
27
+ description: data.description ?? '-',
28
+ extract: (data.extract ?? '').slice(0, EXTRACT_MAX_LEN),
29
+ url: data.content_urls?.desktop?.page ?? `https://${lang}.wikipedia.org`,
30
+ };
31
+ }
@@ -16,6 +16,20 @@ type CreatorNoteDetailRow = {
16
16
  extra: string;
17
17
  };
18
18
  export type { CreatorNoteDetailRow };
19
+ type CreatorNoteDetailDomMetric = {
20
+ label: string;
21
+ value: string;
22
+ extra: string;
23
+ };
24
+ type CreatorNoteDetailDomSection = {
25
+ title: string;
26
+ metrics: CreatorNoteDetailDomMetric[];
27
+ };
28
+ type CreatorNoteDetailDomData = {
29
+ title: string;
30
+ infoText: string;
31
+ sections: CreatorNoteDetailDomSection[];
32
+ };
19
33
  type AudienceSourceItem = {
20
34
  title?: string;
21
35
  value_with_double?: number;
@@ -64,6 +78,7 @@ type NoteDetailApiPayload = {
64
78
  };
65
79
  };
66
80
  export declare function parseCreatorNoteDetailText(bodyText: string, noteId: string): CreatorNoteDetailRow[];
81
+ export declare function parseCreatorNoteDetailDomData(dom: CreatorNoteDetailDomData | null | undefined, noteId: string): CreatorNoteDetailRow[];
67
82
  export declare function appendAudienceRows(rows: CreatorNoteDetailRow[], payload?: NoteDetailApiPayload): CreatorNoteDetailRow[];
68
83
  export declare function appendTrendRows(rows: CreatorNoteDetailRow[], payload?: NoteDetailApiPayload): CreatorNoteDetailRow[];
69
84
  export declare function fetchCreatorNoteDetailRows(page: IPage, noteId: string): Promise<CreatorNoteDetailRow[]>;
@@ -22,6 +22,7 @@ const NOTE_DETAIL_METRICS = [
22
22
  { label: '分享数', section: '互动数据' },
23
23
  ];
24
24
  const NOTE_DETAIL_METRIC_LABELS = new Set(NOTE_DETAIL_METRICS.map((metric) => metric.label));
25
+ const NOTE_DETAIL_SECTIONS = new Set(NOTE_DETAIL_METRICS.map((metric) => metric.section));
25
26
  const NOTE_DETAIL_NOISE_LINES = new Set([
26
27
  '切换笔记',
27
28
  '笔记诊断',
@@ -75,6 +76,10 @@ function findMetricValue(lines, startIndex) {
75
76
  }
76
77
  return { value, extra };
77
78
  }
79
+ function findPublishedAt(text) {
80
+ const match = text.match(/\b\d{4}-\d{2}-\d{2} \d{2}:\d{2}\b/);
81
+ return match?.[0] ?? '';
82
+ }
78
83
  export function parseCreatorNoteDetailText(bodyText, noteId) {
79
84
  const lines = bodyText
80
85
  .split('\n')
@@ -101,6 +106,34 @@ export function parseCreatorNoteDetailText(bodyText, noteId) {
101
106
  }
102
107
  return rows;
103
108
  }
109
+ export function parseCreatorNoteDetailDomData(dom, noteId) {
110
+ if (!dom)
111
+ return [];
112
+ const title = typeof dom.title === 'string' ? dom.title.trim() : '';
113
+ const infoText = typeof dom.infoText === 'string' ? dom.infoText : '';
114
+ const sections = Array.isArray(dom.sections) ? dom.sections : [];
115
+ const rows = [
116
+ { section: '笔记信息', metric: 'note_id', value: noteId, extra: '' },
117
+ { section: '笔记信息', metric: 'title', value: title, extra: '' },
118
+ { section: '笔记信息', metric: 'published_at', value: findPublishedAt(infoText), extra: '' },
119
+ ];
120
+ for (const section of sections) {
121
+ if (!NOTE_DETAIL_SECTIONS.has(section.title))
122
+ continue;
123
+ for (const metric of section.metrics) {
124
+ if (!NOTE_DETAIL_METRIC_LABELS.has(metric.label))
125
+ continue;
126
+ rows.push({
127
+ section: section.title,
128
+ metric: metric.label,
129
+ value: metric.value,
130
+ extra: metric.extra,
131
+ });
132
+ }
133
+ }
134
+ const hasMetric = rows.some((row) => row.section !== '笔记信息' && row.value);
135
+ return hasMetric ? rows : [];
136
+ }
104
137
  function toPercentString(value) {
105
138
  return value == null ? '' : `${value}%`;
106
139
  }
@@ -244,11 +277,42 @@ async function captureNoteDetailPayload(page, noteId) {
244
277
  }
245
278
  return captured > 0 ? payload : null;
246
279
  }
280
+ async function captureNoteDetailDomData(page) {
281
+ const result = await page.evaluate(`() => {
282
+ const norm = (value) => (value || '').trim();
283
+ const sections = Array.from(document.querySelectorAll('.shell-container')).map((container) => {
284
+ const containerText = norm(container.innerText);
285
+ const title = containerText.startsWith('互动数据')
286
+ ? '互动数据'
287
+ : containerText.includes('基础数据')
288
+ ? '基础数据'
289
+ : '';
290
+ const metrics = Array.from(container.querySelectorAll('.block-container.block')).map((block) => ({
291
+ label: norm(block.querySelector('.des')?.innerText),
292
+ value: norm(block.querySelector('.content')?.innerText),
293
+ extra: norm(block.querySelector('.text-with-fans')?.innerText),
294
+ })).filter((metric) => metric.label && metric.value);
295
+ return { title, metrics };
296
+ }).filter((section) => section.title && section.metrics.length > 0);
297
+
298
+ return {
299
+ title: norm(document.querySelector('.note-title')?.innerText),
300
+ infoText: norm(document.querySelector('.note-info-content')?.innerText),
301
+ sections,
302
+ };
303
+ }`);
304
+ if (!result || typeof result !== 'object')
305
+ return null;
306
+ return result;
307
+ }
247
308
  export async function fetchCreatorNoteDetailRows(page, noteId) {
248
309
  await page.goto(`https://creator.xiaohongshu.com/statistics/note-detail?noteId=${encodeURIComponent(noteId)}`);
249
- await page.wait(4);
250
- const bodyText = await page.evaluate('() => document.body.innerText');
251
- const rows = parseCreatorNoteDetailText(typeof bodyText === 'string' ? bodyText : '', noteId);
310
+ const domData = await captureNoteDetailDomData(page).catch(() => null);
311
+ let rows = parseCreatorNoteDetailDomData(domData, noteId);
312
+ if (rows.length === 0) {
313
+ const bodyText = await page.evaluate('() => document.body.innerText');
314
+ rows = parseCreatorNoteDetailText(typeof bodyText === 'string' ? bodyText : '', noteId);
315
+ }
252
316
  const apiPayload = await captureNoteDetailPayload(page, noteId).catch(() => null);
253
317
  appendTrendRows(rows, apiPayload ?? undefined);
254
318
  appendAudienceRows(rows, apiPayload ?? undefined);
@@ -262,11 +326,11 @@ cli({
262
326
  strategy: Strategy.COOKIE,
263
327
  browser: true,
264
328
  args: [
265
- { name: 'note_id', type: 'string', required: true, help: 'Note ID (from creator-notes or note-detail page URL)' },
329
+ { name: 'note-id', positional: true, type: 'string', required: true, help: 'Note ID (from creator-notes or note-detail page URL)' },
266
330
  ],
267
331
  columns: ['section', 'metric', 'value', 'extra'],
268
332
  func: async (page, kwargs) => {
269
- const noteId = kwargs.note_id;
333
+ const noteId = kwargs['note-id'];
270
334
  const rows = await fetchCreatorNoteDetailRows(page, noteId);
271
335
  const hasCoreMetric = rows.some((row) => row.section !== '笔记信息' && row.value);
272
336
  if (!hasCoreMetric) {
@@ -1,15 +1,22 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
2
  import { getRegistry } from '../../registry.js';
3
- import { appendAudienceRows, appendTrendRows, parseCreatorNoteDetailText } from './creator-note-detail.js';
3
+ import { appendAudienceRows, appendTrendRows, parseCreatorNoteDetailDomData, parseCreatorNoteDetailText } from './creator-note-detail.js';
4
4
  import './creator-note-detail.js';
5
5
  function createPageMock(evaluateResult) {
6
+ const evaluate = Array.isArray(evaluateResult)
7
+ ? vi.fn()
8
+ .mockResolvedValueOnce(evaluateResult[0])
9
+ .mockResolvedValue(evaluateResult[evaluateResult.length - 1])
10
+ : vi.fn().mockResolvedValue(evaluateResult);
6
11
  return {
7
12
  goto: vi.fn().mockResolvedValue(undefined),
8
- evaluate: vi.fn().mockResolvedValue(evaluateResult),
13
+ evaluate,
9
14
  snapshot: vi.fn().mockResolvedValue(undefined),
10
15
  click: vi.fn().mockResolvedValue(undefined),
11
16
  typeText: vi.fn().mockResolvedValue(undefined),
12
17
  pressKey: vi.fn().mockResolvedValue(undefined),
18
+ scrollTo: vi.fn().mockResolvedValue(undefined),
19
+ getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
13
20
  wait: vi.fn().mockResolvedValue(undefined),
14
21
  tabs: vi.fn().mockResolvedValue([]),
15
22
  closeTab: vi.fn().mockResolvedValue(undefined),
@@ -88,6 +95,46 @@ describe('xiaohongshu creator-note-detail', () => {
88
95
  { section: '互动数据', metric: '分享数', value: '6', extra: '粉丝占比 0%' },
89
96
  ]);
90
97
  });
98
+ it('parses structured note detail dom data into rows', () => {
99
+ expect(parseCreatorNoteDetailDomData({
100
+ title: '神雕侠侣战力金字塔',
101
+ infoText: '神雕侠侣战力金字塔\n#武侠\n2025-12-04 19:45\n切换笔记',
102
+ sections: [
103
+ {
104
+ title: '基础数据',
105
+ metrics: [
106
+ { label: '曝光数', value: '898204', extra: '粉丝占比 0.5%' },
107
+ { label: '观看数', value: '148284', extra: '粉丝占比 0.6%' },
108
+ { label: '封面点击率', value: '17.1%', extra: '粉丝 19.1%' },
109
+ { label: '平均观看时长', value: '30.1秒', extra: '粉丝 17.7秒' },
110
+ { label: '涨粉数', value: '101', extra: '' },
111
+ ],
112
+ },
113
+ {
114
+ title: '互动数据',
115
+ metrics: [
116
+ { label: '点赞数', value: '2280', extra: '粉丝占比 3.6%' },
117
+ { label: '评论数', value: '319', extra: '粉丝占比 9.4%' },
118
+ { label: '收藏数', value: '466', extra: '粉丝占比 9.4%' },
119
+ { label: '分享数', value: '33', extra: '粉丝占比 17.7%' },
120
+ ],
121
+ },
122
+ ],
123
+ }, '693155fc000000000d03b42c')).toEqual([
124
+ { section: '笔记信息', metric: 'note_id', value: '693155fc000000000d03b42c', extra: '' },
125
+ { section: '笔记信息', metric: 'title', value: '神雕侠侣战力金字塔', extra: '' },
126
+ { section: '笔记信息', metric: 'published_at', value: '2025-12-04 19:45', extra: '' },
127
+ { section: '基础数据', metric: '曝光数', value: '898204', extra: '粉丝占比 0.5%' },
128
+ { section: '基础数据', metric: '观看数', value: '148284', extra: '粉丝占比 0.6%' },
129
+ { section: '基础数据', metric: '封面点击率', value: '17.1%', extra: '粉丝 19.1%' },
130
+ { section: '基础数据', metric: '平均观看时长', value: '30.1秒', extra: '粉丝 17.7秒' },
131
+ { section: '基础数据', metric: '涨粉数', value: '101', extra: '' },
132
+ { section: '互动数据', metric: '点赞数', value: '2280', extra: '粉丝占比 3.6%' },
133
+ { section: '互动数据', metric: '评论数', value: '319', extra: '粉丝占比 9.4%' },
134
+ { section: '互动数据', metric: '收藏数', value: '466', extra: '粉丝占比 9.4%' },
135
+ { section: '互动数据', metric: '分享数', value: '33', extra: '粉丝占比 17.7%' },
136
+ ]);
137
+ });
91
138
  it('appends audience source and portrait rows from API payloads', () => {
92
139
  const rows = appendAudienceRows([], {
93
140
  audienceSource: {
@@ -161,38 +208,40 @@ describe('xiaohongshu creator-note-detail', () => {
161
208
  it('navigates to the note detail page and returns parsed rows', async () => {
162
209
  const cmd = getRegistry().get('xiaohongshu/creator-note-detail');
163
210
  expect(cmd?.func).toBeTypeOf('function');
164
- const page = createPageMock(`笔记数据详情
165
- 示例笔记
166
- 2026-03-19 12:00
167
- 曝光数
168
- 100
169
- 粉丝占比 10%
170
- 观看数
171
- 50
172
- 粉丝占比 20%
173
- 封面点击率
174
- 12%
175
- 粉丝 11%
176
- 平均观看时长
177
- 30秒
178
- 粉丝 31秒
179
- 涨粉数
180
- 2
181
- 点赞数
182
- 8
183
- 粉丝占比 25%
184
- 评论数
185
- 1
186
- 粉丝占比 0%
187
- 收藏数
188
- 3
189
- 粉丝占比 50%
190
- 分享数
191
- 0
192
- 粉丝占比 0%`);
193
- const result = await cmd.func(page, { note_id: 'demo-note-id' });
211
+ const page = createPageMock([
212
+ {
213
+ title: '示例笔记',
214
+ infoText: '示例笔记\n2026-03-19 12:00\n切换笔记',
215
+ sections: [
216
+ {
217
+ title: '基础数据',
218
+ metrics: [
219
+ { label: '曝光数', value: '100', extra: '粉丝占比 10%' },
220
+ { label: '观看数', value: '50', extra: '粉丝占比 20%' },
221
+ { label: '封面点击率', value: '12%', extra: '粉丝 11%' },
222
+ { label: '平均观看时长', value: '30秒', extra: '粉丝 31秒' },
223
+ { label: '涨粉数', value: '2', extra: '' },
224
+ ],
225
+ },
226
+ {
227
+ title: '互动数据',
228
+ metrics: [
229
+ { label: '点赞数', value: '8', extra: '粉丝占比 25%' },
230
+ { label: '评论数', value: '1', extra: '粉丝占比 0%' },
231
+ { label: '收藏数', value: '3', extra: '粉丝占比 50%' },
232
+ { label: '分享数', value: '0', extra: '粉丝占比 0%' },
233
+ ],
234
+ },
235
+ ],
236
+ },
237
+ null,
238
+ null,
239
+ null,
240
+ null,
241
+ ]);
242
+ const result = await cmd.func(page, { 'note-id': 'demo-note-id' });
194
243
  expect(page.goto.mock.calls[0][0]).toBe('https://creator.xiaohongshu.com/statistics/note-detail?noteId=demo-note-id');
195
- expect(page.evaluate.mock.calls[0][0]).toBe('() => document.body.innerText');
244
+ expect(page.evaluate.mock.calls[0][0]).toContain("document.querySelector('.note-title')");
196
245
  expect(result).toEqual([
197
246
  { section: '笔记信息', metric: 'note_id', value: 'demo-note-id', extra: '' },
198
247
  { section: '笔记信息', metric: 'title', value: '示例笔记', extra: '' },
@@ -59,9 +59,9 @@ export function parseCreatorNotesText(bodyText) {
59
59
  title,
60
60
  date: dateMatch[1],
61
61
  views: metrics[0] ?? 0,
62
- likes: metrics[1] ?? 0,
63
- collects: metrics[2] ?? 0,
64
- comments: metrics[3] ?? 0,
62
+ likes: metrics[2] ?? 0,
63
+ collects: metrics[3] ?? 0,
64
+ comments: metrics[1] ?? 0,
65
65
  url: '',
66
66
  });
67
67
  i = cursor - 1;
@@ -80,6 +80,18 @@ export function parseCreatorNoteIdsFromHtml(bodyHtml) {
80
80
  }
81
81
  return ids;
82
82
  }
83
+ function mapDomCards(cards) {
84
+ return cards.map((card) => ({
85
+ id: card.id,
86
+ title: card.title,
87
+ date: card.date,
88
+ views: card.metrics[0] ?? 0,
89
+ likes: card.metrics[2] ?? 0,
90
+ collects: card.metrics[3] ?? 0,
91
+ comments: card.metrics[1] ?? 0,
92
+ url: buildNoteDetailUrl(card.id),
93
+ }));
94
+ }
83
95
  function mapAnalyzeItems(items) {
84
96
  return (items ?? []).map((item) => ({
85
97
  id: item.id ?? '',
@@ -97,7 +109,6 @@ async function fetchCreatorNotesByApi(page, limit) {
97
109
  const maxPages = Math.max(1, Math.ceil(limit / pageSize));
98
110
  const notes = [];
99
111
  await page.goto(`https://creator.xiaohongshu.com/statistics/data-analysis?type=0&page_size=${pageSize}&page_num=1`);
100
- await page.wait(4);
101
112
  for (let pageNum = 1; pageNum <= maxPages && notes.length < limit; pageNum++) {
102
113
  const apiPath = `${NOTE_ANALYZE_API_PATH}?type=0&page_size=${pageSize}&page_num=${pageNum}`;
103
114
  const fetched = await page.evaluate(`
@@ -139,9 +150,28 @@ export async function fetchCreatorNotes(page, limit) {
139
150
  let notes = await fetchCreatorNotesByApi(page, limit);
140
151
  if (notes.length === 0) {
141
152
  await page.goto('https://creator.xiaohongshu.com/new/note-manager');
142
- await page.wait(4);
143
153
  const maxPageDowns = Math.max(0, Math.ceil(limit / 10) + 1);
144
154
  for (let i = 0; i <= maxPageDowns; i++) {
155
+ const domCards = await page.evaluate(`() => {
156
+ const noteIdRe = /"noteId":"([0-9a-f]{24})"/;
157
+ return Array.from(document.querySelectorAll('div.note[data-impression], div.note')).map((card) => {
158
+ const impression = card.getAttribute('data-impression') || '';
159
+ const id = impression.match(noteIdRe)?.[1] || '';
160
+ const title = (card.querySelector('.title, .raw')?.innerText || '').trim();
161
+ const dateText = (card.querySelector('.time_status, .time')?.innerText || '').trim();
162
+ const date = dateText.replace(/^发布于\\s*/, '');
163
+ const metrics = Array.from(card.querySelectorAll('.icon_list .icon'))
164
+ .map((el) => parseInt((el.innerText || '').trim(), 10))
165
+ .filter((value) => Number.isFinite(value));
166
+ return { id, title, date, metrics };
167
+ });
168
+ }`);
169
+ const parsedDomNotes = mapDomCards(Array.isArray(domCards) ? domCards : []).filter((note) => note.title && note.date);
170
+ if (parsedDomNotes.length > 0) {
171
+ notes = parsedDomNotes;
172
+ }
173
+ if (notes.length >= limit || (notes.length > 0 && i === 0))
174
+ break;
145
175
  const body = await page.evaluate('() => ({ text: document.body.innerText, html: document.body.innerHTML })');
146
176
  const bodyText = typeof body?.text === 'string' ? body.text : '';
147
177
  const bodyHtml = typeof body?.html === 'string' ? body.html : '';
@@ -18,6 +18,8 @@ function createPageMock(evaluateResult, interceptedRequests = []) {
18
18
  click: vi.fn().mockResolvedValue(undefined),
19
19
  typeText: vi.fn().mockResolvedValue(undefined),
20
20
  pressKey: vi.fn().mockResolvedValue(undefined),
21
+ scrollTo: vi.fn().mockResolvedValue(undefined),
22
+ getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
21
23
  wait: vi.fn().mockResolvedValue(undefined),
22
24
  tabs: vi.fn().mockResolvedValue([]),
23
25
  closeTab: vi.fn().mockResolvedValue(undefined),
@@ -64,9 +66,9 @@ describe('xiaohongshu creator-notes', () => {
64
66
  title: '神雕侠侣战力金字塔',
65
67
  date: '2025年12月04日 19:45',
66
68
  views: 148208,
67
- likes: 324,
68
- collects: 2279,
69
- comments: 465,
69
+ likes: 2279,
70
+ collects: 465,
71
+ comments: 324,
70
72
  url: '',
71
73
  },
72
74
  {
@@ -107,13 +109,42 @@ describe('xiaohongshu creator-notes', () => {
107
109
  title: '示例笔记',
108
110
  date: '2026年03月19日 12:00',
109
111
  views: 10,
110
- likes: 2,
111
- collects: 3,
112
- comments: 4,
112
+ likes: 3,
113
+ collects: 4,
114
+ comments: 2,
113
115
  url: 'https://creator.xiaohongshu.com/statistics/note-detail?noteId=69ba940500000000200384db',
114
116
  },
115
117
  ]);
116
118
  });
119
+ it('prefers note card dom data when the analyze api is unavailable', async () => {
120
+ const cmd = getRegistry().get('xiaohongshu/creator-notes');
121
+ expect(cmd?.func).toBeTypeOf('function');
122
+ const page = createPageMock([
123
+ undefined,
124
+ [
125
+ {
126
+ id: '693155fc000000000d03b42c',
127
+ title: '神雕侠侣战力金字塔',
128
+ date: '2025年12月04日 19:45',
129
+ metrics: [148284, 319, 2280, 466, 33],
130
+ },
131
+ ],
132
+ ]);
133
+ const result = await cmd.func(page, { limit: 1 });
134
+ expect(result).toEqual([
135
+ {
136
+ rank: 1,
137
+ id: '693155fc000000000d03b42c',
138
+ title: '神雕侠侣战力金字塔',
139
+ date: '2025年12月04日 19:45',
140
+ views: 148284,
141
+ likes: 2280,
142
+ collects: 466,
143
+ comments: 319,
144
+ url: 'https://creator.xiaohongshu.com/statistics/note-detail?noteId=693155fc000000000d03b42c',
145
+ },
146
+ ]);
147
+ });
117
148
  it('prefers the creator analyze API and preserves note ids', async () => {
118
149
  const cmd = getRegistry().get('xiaohongshu/creator-notes');
119
150
  expect(cmd?.func).toBeTypeOf('function');
@@ -19,7 +19,6 @@ cli({
19
19
  columns: ['field', 'value'],
20
20
  func: async (page, _kwargs) => {
21
21
  await page.goto('https://creator.xiaohongshu.com/new/home');
22
- await page.wait(3);
23
22
  const data = await page.evaluate(`
24
23
  async () => {
25
24
  try {
@@ -29,7 +29,6 @@ cli({
29
29
  const period = kwargs.period || 'seven';
30
30
  // Navigate to creator center for cookie context
31
31
  await page.goto('https://creator.xiaohongshu.com/new/home');
32
- await page.wait(3);
33
32
  const data = await page.evaluate(`
34
33
  async () => {
35
34
  try {