@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
@@ -9,7 +9,7 @@ cli({
9
9
  domain: 'weread.qq.com',
10
10
  strategy: Strategy.COOKIE,
11
11
  args: [
12
- { name: 'bookId', positional: true, required: true, help: 'Book ID (from shelf or search results)' },
12
+ { name: 'book-id', positional: true, required: true, help: 'Book ID (from shelf or search results)' },
13
13
  { name: 'limit', type: 'int', default: 20, help: 'Max results' },
14
14
  ],
15
15
  columns: ['chapter', 'text', 'createTime'],
@@ -9,7 +9,7 @@ cli({
9
9
  domain: 'weread.qq.com',
10
10
  strategy: Strategy.COOKIE,
11
11
  args: [
12
- { name: 'bookId', positional: true, required: true, help: 'Book ID (from shelf or search results)' },
12
+ { name: 'book-id', positional: true, required: true, help: 'Book ID (from shelf or search results)' },
13
13
  { name: 'limit', type: 'int', default: 20, help: 'Max results' },
14
14
  ],
15
15
  columns: ['chapter', 'text', 'review', 'createTime'],
@@ -9,7 +9,7 @@ cli({
9
9
  strategy: Strategy.PUBLIC,
10
10
  browser: false,
11
11
  args: [
12
- { name: 'keyword', positional: true, required: true, help: 'Search keyword' },
12
+ { name: 'query', positional: true, required: true, help: 'Search keyword' },
13
13
  { name: 'limit', type: 'int', default: 10, help: 'Max results' },
14
14
  ],
15
15
  columns: ['rank', 'title', 'author', 'bookId'],
@@ -0,0 +1,19 @@
1
+ import { CliError } from '../../errors.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
+ import { type WikiSummary, formatSummaryRow, wikiFetch } from './utils.js';
4
+
5
+ cli({
6
+ site: 'wikipedia',
7
+ name: 'random',
8
+ description: 'Get a random Wikipedia article',
9
+ strategy: Strategy.PUBLIC,
10
+ browser: false,
11
+ args: [{ name: 'lang', default: 'en', help: 'Language code (e.g. en, zh, ja)' }],
12
+ columns: ['title', 'description', 'extract', 'url'],
13
+ func: async (_page, args) => {
14
+ const lang = args.lang || 'en';
15
+ const data = (await wikiFetch(lang, '/api/rest_v1/page/random/summary')) as WikiSummary;
16
+ if (!data?.title) throw new CliError('NOT_FOUND', 'No random article returned', 'Try again');
17
+ return [formatSummaryRow(data, lang)];
18
+ },
19
+ });
@@ -1,8 +1,11 @@
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
 
5
- interface WikiSearchResult { title: string; snippet: string; }
5
+ interface WikiSearchResult {
6
+ title: string;
7
+ snippet: string;
8
+ }
6
9
 
7
10
  cli({
8
11
  site: 'wikipedia',
@@ -11,7 +14,7 @@ cli({
11
14
  strategy: Strategy.PUBLIC,
12
15
  browser: false,
13
16
  args: [
14
- { name: 'keyword', positional: true, required: true, help: 'Search keyword' },
17
+ { name: 'query', positional: true, required: true, help: 'Search keyword' },
15
18
  { name: 'limit', type: 'int', default: 10, help: 'Max results' },
16
19
  { name: 'lang', default: 'en', help: 'Language code (e.g. en, zh, ja)' },
17
20
  ],
@@ -19,8 +22,11 @@ cli({
19
22
  func: async (_page, args) => {
20
23
  const limit = Math.max(1, Math.min(Number(args.limit), 50));
21
24
  const lang = args.lang || 'en';
22
- const q = encodeURIComponent(args.keyword);
23
- const data = await wikiFetch(lang, `/w/api.php?action=query&list=search&srsearch=${q}&srlimit=${limit}&format=json&utf8=1`) as { query?: { search?: WikiSearchResult[] } };
25
+ const q = encodeURIComponent(args.query);
26
+ const data = (await wikiFetch(
27
+ lang,
28
+ `/w/api.php?action=query&list=search&srsearch=${q}&srlimit=${limit}&format=json&utf8=1`,
29
+ )) as { query?: { search?: WikiSearchResult[] } };
24
30
  const results = data?.query?.search;
25
31
  if (!results?.length) throw new CliError('NOT_FOUND', 'No articles found', 'Try a different keyword');
26
32
  return results.map((r) => ({
@@ -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 { type WikiSummary, formatSummaryRow, wikiFetch } from './utils.js';
4
4
 
5
5
  cli({
6
6
  site: 'wikipedia',
@@ -16,13 +16,8 @@ cli({
16
16
  func: async (_page, args) => {
17
17
  const lang = args.lang || 'en';
18
18
  const title = encodeURIComponent(args.title.replace(/ /g, '_'));
19
- const data = await wikiFetch(lang, `/api/rest_v1/page/summary/${title}`) as { title?: string; description?: string; extract?: string; content_urls?: { desktop?: { page?: string } } };
19
+ const data = (await wikiFetch(lang, `/api/rest_v1/page/summary/${title}`)) as WikiSummary;
20
20
  if (!data?.title) 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,41 @@
1
+ import { CliError } from '../../errors.js';
2
+ import { cli, Strategy } from '../../registry.js';
3
+ import { DESC_MAX_LEN, type WikiMostReadArticle, wikiFetch } from './utils.js';
4
+
5
+ cli({
6
+ site: 'wikipedia',
7
+ name: 'trending',
8
+ description: 'Most-read Wikipedia articles (yesterday)',
9
+ strategy: Strategy.PUBLIC,
10
+ browser: false,
11
+ args: [
12
+ { name: 'limit', type: 'int', default: 10, help: 'Max results' },
13
+ { name: 'lang', default: 'en', help: 'Language code (e.g. en, zh, ja)' },
14
+ ],
15
+ columns: ['rank', 'title', 'description', 'views'],
16
+ func: async (_page, args) => {
17
+ const lang = args.lang || 'en';
18
+ const limit = Math.max(1, Math.min(Number(args.limit), 50));
19
+
20
+ // Use yesterday's UTC date — Wikipedia API expects UTC and yesterday
21
+ // guarantees data availability (today's aggregation may be incomplete).
22
+ const d = new Date(Date.now() - 86_400_000);
23
+ const yyyy = d.getUTCFullYear();
24
+ const mm = String(d.getUTCMonth() + 1).padStart(2, '0');
25
+ const dd = String(d.getUTCDate()).padStart(2, '0');
26
+
27
+ const data = (await wikiFetch(lang, `/api/rest_v1/feed/featured/${yyyy}/${mm}/${dd}`)) as {
28
+ mostread?: { articles?: WikiMostReadArticle[] };
29
+ };
30
+ const articles = data?.mostread?.articles;
31
+ if (!articles?.length)
32
+ throw new CliError('NOT_FOUND', 'No trending articles available', 'Try a different language with --lang');
33
+
34
+ return articles.slice(0, limit).map((a, i) => ({
35
+ rank: i + 1,
36
+ title: a.title ?? '-',
37
+ description: (a.description ?? '-').slice(0, DESC_MAX_LEN),
38
+ views: a.views ?? 0,
39
+ }));
40
+ },
41
+ });
@@ -8,6 +8,27 @@
8
8
 
9
9
  import { CliError } from '../../errors.js';
10
10
 
11
+ /** Maximum character length for article extract fields. */
12
+ export const EXTRACT_MAX_LEN = 300;
13
+
14
+ /** Maximum character length for short description fields. */
15
+ export const DESC_MAX_LEN = 80;
16
+
17
+ /** Response shape shared by /page/summary and /page/random/summary endpoints. */
18
+ export interface WikiSummary {
19
+ title?: string;
20
+ description?: string;
21
+ extract?: string;
22
+ content_urls?: { desktop?: { page?: string } };
23
+ }
24
+
25
+ /** Article entry returned by the /feed/featured most-read endpoint. */
26
+ export interface WikiMostReadArticle {
27
+ title?: string;
28
+ description?: string;
29
+ views?: number;
30
+ }
31
+
11
32
  export async function wikiFetch(lang: string, path: string): Promise<unknown> {
12
33
  const url = `https://${lang}.wikipedia.org${path}`;
13
34
  const resp = await fetch(url, {
@@ -18,3 +39,13 @@ export async function wikiFetch(lang: string, path: string): Promise<unknown> {
18
39
  }
19
40
  return resp.json();
20
41
  }
42
+
43
+ /** Map a WikiSummary API response to the standard output row. */
44
+ export function formatSummaryRow(data: WikiSummary, lang: string) {
45
+ return {
46
+ title: data.title!,
47
+ description: data.description ?? '-',
48
+ extract: (data.extract ?? '').slice(0, EXTRACT_MAX_LEN),
49
+ url: data.content_urls?.desktop?.page ?? `https://${lang}.wikipedia.org`,
50
+ };
51
+ }
@@ -1,17 +1,25 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
2
  import type { IPage } from '../../types.js';
3
3
  import { getRegistry } from '../../registry.js';
4
- import { appendAudienceRows, appendTrendRows, parseCreatorNoteDetailText } from './creator-note-detail.js';
4
+ import { appendAudienceRows, appendTrendRows, parseCreatorNoteDetailDomData, parseCreatorNoteDetailText } from './creator-note-detail.js';
5
5
  import './creator-note-detail.js';
6
6
 
7
7
  function createPageMock(evaluateResult: any): IPage {
8
+ const evaluate = Array.isArray(evaluateResult)
9
+ ? vi.fn()
10
+ .mockResolvedValueOnce(evaluateResult[0])
11
+ .mockResolvedValue(evaluateResult[evaluateResult.length - 1])
12
+ : vi.fn().mockResolvedValue(evaluateResult);
13
+
8
14
  return {
9
15
  goto: vi.fn().mockResolvedValue(undefined),
10
- evaluate: vi.fn().mockResolvedValue(evaluateResult),
16
+ evaluate,
11
17
  snapshot: vi.fn().mockResolvedValue(undefined),
12
18
  click: vi.fn().mockResolvedValue(undefined),
13
19
  typeText: vi.fn().mockResolvedValue(undefined),
14
20
  pressKey: vi.fn().mockResolvedValue(undefined),
21
+ scrollTo: vi.fn().mockResolvedValue(undefined),
22
+ getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
15
23
  wait: vi.fn().mockResolvedValue(undefined),
16
24
  tabs: vi.fn().mockResolvedValue([]),
17
25
  closeTab: vi.fn().mockResolvedValue(undefined),
@@ -93,6 +101,47 @@ describe('xiaohongshu creator-note-detail', () => {
93
101
  ]);
94
102
  });
95
103
 
104
+ it('parses structured note detail dom data into rows', () => {
105
+ expect(parseCreatorNoteDetailDomData({
106
+ title: '神雕侠侣战力金字塔',
107
+ infoText: '神雕侠侣战力金字塔\n#武侠\n2025-12-04 19:45\n切换笔记',
108
+ sections: [
109
+ {
110
+ title: '基础数据',
111
+ metrics: [
112
+ { label: '曝光数', value: '898204', extra: '粉丝占比 0.5%' },
113
+ { label: '观看数', value: '148284', extra: '粉丝占比 0.6%' },
114
+ { label: '封面点击率', value: '17.1%', extra: '粉丝 19.1%' },
115
+ { label: '平均观看时长', value: '30.1秒', extra: '粉丝 17.7秒' },
116
+ { label: '涨粉数', value: '101', extra: '' },
117
+ ],
118
+ },
119
+ {
120
+ title: '互动数据',
121
+ metrics: [
122
+ { label: '点赞数', value: '2280', extra: '粉丝占比 3.6%' },
123
+ { label: '评论数', value: '319', extra: '粉丝占比 9.4%' },
124
+ { label: '收藏数', value: '466', extra: '粉丝占比 9.4%' },
125
+ { label: '分享数', value: '33', extra: '粉丝占比 17.7%' },
126
+ ],
127
+ },
128
+ ],
129
+ }, '693155fc000000000d03b42c')).toEqual([
130
+ { section: '笔记信息', metric: 'note_id', value: '693155fc000000000d03b42c', extra: '' },
131
+ { section: '笔记信息', metric: 'title', value: '神雕侠侣战力金字塔', extra: '' },
132
+ { section: '笔记信息', metric: 'published_at', value: '2025-12-04 19:45', extra: '' },
133
+ { section: '基础数据', metric: '曝光数', value: '898204', extra: '粉丝占比 0.5%' },
134
+ { section: '基础数据', metric: '观看数', value: '148284', extra: '粉丝占比 0.6%' },
135
+ { section: '基础数据', metric: '封面点击率', value: '17.1%', extra: '粉丝 19.1%' },
136
+ { section: '基础数据', metric: '平均观看时长', value: '30.1秒', extra: '粉丝 17.7秒' },
137
+ { section: '基础数据', metric: '涨粉数', value: '101', extra: '' },
138
+ { section: '互动数据', metric: '点赞数', value: '2280', extra: '粉丝占比 3.6%' },
139
+ { section: '互动数据', metric: '评论数', value: '319', extra: '粉丝占比 9.4%' },
140
+ { section: '互动数据', metric: '收藏数', value: '466', extra: '粉丝占比 9.4%' },
141
+ { section: '互动数据', metric: '分享数', value: '33', extra: '粉丝占比 17.7%' },
142
+ ]);
143
+ });
144
+
96
145
  it('appends audience source and portrait rows from API payloads', () => {
97
146
  const rows = appendAudienceRows([], {
98
147
  audienceSource: {
@@ -171,40 +220,42 @@ describe('xiaohongshu creator-note-detail', () => {
171
220
  const cmd = getRegistry().get('xiaohongshu/creator-note-detail');
172
221
  expect(cmd?.func).toBeTypeOf('function');
173
222
 
174
- const page = createPageMock(`笔记数据详情
175
- 示例笔记
176
- 2026-03-19 12:00
177
- 曝光数
178
- 100
179
- 粉丝占比 10%
180
- 观看数
181
- 50
182
- 粉丝占比 20%
183
- 封面点击率
184
- 12%
185
- 粉丝 11%
186
- 平均观看时长
187
- 30秒
188
- 粉丝 31秒
189
- 涨粉数
190
- 2
191
- 点赞数
192
- 8
193
- 粉丝占比 25%
194
- 评论数
195
- 1
196
- 粉丝占比 0%
197
- 收藏数
198
- 3
199
- 粉丝占比 50%
200
- 分享数
201
- 0
202
- 粉丝占比 0%`);
223
+ const page = createPageMock([
224
+ {
225
+ title: '示例笔记',
226
+ infoText: '示例笔记\n2026-03-19 12:00\n切换笔记',
227
+ sections: [
228
+ {
229
+ title: '基础数据',
230
+ metrics: [
231
+ { label: '曝光数', value: '100', extra: '粉丝占比 10%' },
232
+ { label: '观看数', value: '50', extra: '粉丝占比 20%' },
233
+ { label: '封面点击率', value: '12%', extra: '粉丝 11%' },
234
+ { label: '平均观看时长', value: '30秒', extra: '粉丝 31秒' },
235
+ { label: '涨粉数', value: '2', extra: '' },
236
+ ],
237
+ },
238
+ {
239
+ title: '互动数据',
240
+ metrics: [
241
+ { label: '点赞数', value: '8', extra: '粉丝占比 25%' },
242
+ { label: '评论数', value: '1', extra: '粉丝占比 0%' },
243
+ { label: '收藏数', value: '3', extra: '粉丝占比 50%' },
244
+ { label: '分享数', value: '0', extra: '粉丝占比 0%' },
245
+ ],
246
+ },
247
+ ],
248
+ },
249
+ null,
250
+ null,
251
+ null,
252
+ null,
253
+ ]);
203
254
 
204
- const result = await cmd!.func!(page, { note_id: 'demo-note-id' });
255
+ const result = await cmd!.func!(page, { 'note-id': 'demo-note-id' });
205
256
 
206
257
  expect((page.goto as any).mock.calls[0][0]).toBe('https://creator.xiaohongshu.com/statistics/note-detail?noteId=demo-note-id');
207
- expect((page.evaluate as any).mock.calls[0][0]).toBe('() => document.body.innerText');
258
+ expect((page.evaluate as any).mock.calls[0][0]).toContain("document.querySelector('.note-title')");
208
259
  expect(result).toEqual([
209
260
  { section: '笔记信息', metric: 'note_id', value: 'demo-note-id', extra: '' },
210
261
  { section: '笔记信息', metric: 'title', value: '示例笔记', extra: '' },
@@ -21,6 +21,23 @@ type CreatorNoteDetailRow = {
21
21
 
22
22
  export type { CreatorNoteDetailRow };
23
23
 
24
+ type CreatorNoteDetailDomMetric = {
25
+ label: string;
26
+ value: string;
27
+ extra: string;
28
+ };
29
+
30
+ type CreatorNoteDetailDomSection = {
31
+ title: string;
32
+ metrics: CreatorNoteDetailDomMetric[];
33
+ };
34
+
35
+ type CreatorNoteDetailDomData = {
36
+ title: string;
37
+ infoText: string;
38
+ sections: CreatorNoteDetailDomSection[];
39
+ };
40
+
24
41
  type AudienceSourceItem = {
25
42
  title?: string;
26
43
  value_with_double?: number;
@@ -87,6 +104,7 @@ const NOTE_DETAIL_METRICS = [
87
104
  ] as const;
88
105
 
89
106
  const NOTE_DETAIL_METRIC_LABELS = new Set<string>(NOTE_DETAIL_METRICS.map((metric) => metric.label));
107
+ const NOTE_DETAIL_SECTIONS = new Set<string>(NOTE_DETAIL_METRICS.map((metric) => metric.section));
90
108
  const NOTE_DETAIL_NOISE_LINES = new Set([
91
109
  '切换笔记',
92
110
  '笔记诊断',
@@ -144,6 +162,11 @@ function findMetricValue(lines: string[], startIndex: number): { value: string;
144
162
  return { value, extra };
145
163
  }
146
164
 
165
+ function findPublishedAt(text: string): string {
166
+ const match = text.match(/\b\d{4}-\d{2}-\d{2} \d{2}:\d{2}\b/);
167
+ return match?.[0] ?? '';
168
+ }
169
+
147
170
  export function parseCreatorNoteDetailText(bodyText: string, noteId: string): CreatorNoteDetailRow[] {
148
171
  const lines = bodyText
149
172
  .split('\n')
@@ -173,6 +196,35 @@ export function parseCreatorNoteDetailText(bodyText: string, noteId: string): Cr
173
196
  return rows;
174
197
  }
175
198
 
199
+ export function parseCreatorNoteDetailDomData(dom: CreatorNoteDetailDomData | null | undefined, noteId: string): CreatorNoteDetailRow[] {
200
+ if (!dom) return [];
201
+ const title = typeof dom.title === 'string' ? dom.title.trim() : '';
202
+ const infoText = typeof dom.infoText === 'string' ? dom.infoText : '';
203
+ const sections = Array.isArray(dom.sections) ? dom.sections : [];
204
+
205
+ const rows: CreatorNoteDetailRow[] = [
206
+ { section: '笔记信息', metric: 'note_id', value: noteId, extra: '' },
207
+ { section: '笔记信息', metric: 'title', value: title, extra: '' },
208
+ { section: '笔记信息', metric: 'published_at', value: findPublishedAt(infoText), extra: '' },
209
+ ];
210
+
211
+ for (const section of sections) {
212
+ if (!NOTE_DETAIL_SECTIONS.has(section.title)) continue;
213
+ for (const metric of section.metrics) {
214
+ if (!NOTE_DETAIL_METRIC_LABELS.has(metric.label)) continue;
215
+ rows.push({
216
+ section: section.title,
217
+ metric: metric.label,
218
+ value: metric.value,
219
+ extra: metric.extra,
220
+ });
221
+ }
222
+ }
223
+
224
+ const hasMetric = rows.some((row) => row.section !== '笔记信息' && row.value);
225
+ return hasMetric ? rows : [];
226
+ }
227
+
176
228
  function toPercentString(value?: number): string {
177
229
  return value == null ? '' : `${value}%`;
178
230
  }
@@ -325,12 +377,44 @@ async function captureNoteDetailPayload(page: IPage, noteId: string): Promise<No
325
377
  return captured > 0 ? payload : null;
326
378
  }
327
379
 
380
+ async function captureNoteDetailDomData(page: IPage): Promise<CreatorNoteDetailDomData | null> {
381
+ const result = await page.evaluate(`() => {
382
+ const norm = (value) => (value || '').trim();
383
+ const sections = Array.from(document.querySelectorAll('.shell-container')).map((container) => {
384
+ const containerText = norm(container.innerText);
385
+ const title = containerText.startsWith('互动数据')
386
+ ? '互动数据'
387
+ : containerText.includes('基础数据')
388
+ ? '基础数据'
389
+ : '';
390
+ const metrics = Array.from(container.querySelectorAll('.block-container.block')).map((block) => ({
391
+ label: norm(block.querySelector('.des')?.innerText),
392
+ value: norm(block.querySelector('.content')?.innerText),
393
+ extra: norm(block.querySelector('.text-with-fans')?.innerText),
394
+ })).filter((metric) => metric.label && metric.value);
395
+ return { title, metrics };
396
+ }).filter((section) => section.title && section.metrics.length > 0);
397
+
398
+ return {
399
+ title: norm(document.querySelector('.note-title')?.innerText),
400
+ infoText: norm(document.querySelector('.note-info-content')?.innerText),
401
+ sections,
402
+ };
403
+ }`);
404
+
405
+ if (!result || typeof result !== 'object') return null;
406
+ return result as CreatorNoteDetailDomData;
407
+ }
408
+
328
409
  export async function fetchCreatorNoteDetailRows(page: IPage, noteId: string): Promise<CreatorNoteDetailRow[]> {
329
410
  await page.goto(`https://creator.xiaohongshu.com/statistics/note-detail?noteId=${encodeURIComponent(noteId)}`);
330
- await page.wait(4);
331
411
 
332
- const bodyText = await page.evaluate('() => document.body.innerText');
333
- const rows = parseCreatorNoteDetailText(typeof bodyText === 'string' ? bodyText : '', noteId);
412
+ const domData = await captureNoteDetailDomData(page).catch(() => null);
413
+ let rows = parseCreatorNoteDetailDomData(domData, noteId);
414
+ if (rows.length === 0) {
415
+ const bodyText = await page.evaluate('() => document.body.innerText');
416
+ rows = parseCreatorNoteDetailText(typeof bodyText === 'string' ? bodyText : '', noteId);
417
+ }
334
418
  const apiPayload = await captureNoteDetailPayload(page, noteId).catch(() => null);
335
419
  appendTrendRows(rows, apiPayload ?? undefined);
336
420
  appendAudienceRows(rows, apiPayload ?? undefined);
@@ -346,11 +430,11 @@ cli({
346
430
  strategy: Strategy.COOKIE,
347
431
  browser: true,
348
432
  args: [
349
- { name: 'note_id', type: 'string', required: true, help: 'Note ID (from creator-notes or note-detail page URL)' },
433
+ { name: 'note-id', positional: true, type: 'string', required: true, help: 'Note ID (from creator-notes or note-detail page URL)' },
350
434
  ],
351
435
  columns: ['section', 'metric', 'value', 'extra'],
352
436
  func: async (page, kwargs) => {
353
- const noteId: string = kwargs.note_id;
437
+ const noteId: string = kwargs['note-id'];
354
438
  const rows = await fetchCreatorNoteDetailRows(page, noteId);
355
439
 
356
440
  const hasCoreMetric = rows.some((row) => row.section !== '笔记信息' && row.value);
@@ -22,6 +22,8 @@ function createPageMock(evaluateResult: any, interceptedRequests: any[] = []): I
22
22
  click: vi.fn().mockResolvedValue(undefined),
23
23
  typeText: vi.fn().mockResolvedValue(undefined),
24
24
  pressKey: vi.fn().mockResolvedValue(undefined),
25
+ scrollTo: vi.fn().mockResolvedValue(undefined),
26
+ getFormState: vi.fn().mockResolvedValue({ forms: [], orphanFields: [] }),
25
27
  wait: vi.fn().mockResolvedValue(undefined),
26
28
  tabs: vi.fn().mockResolvedValue([]),
27
29
  closeTab: vi.fn().mockResolvedValue(undefined),
@@ -70,9 +72,9 @@ describe('xiaohongshu creator-notes', () => {
70
72
  title: '神雕侠侣战力金字塔',
71
73
  date: '2025年12月04日 19:45',
72
74
  views: 148208,
73
- likes: 324,
74
- collects: 2279,
75
- comments: 465,
75
+ likes: 2279,
76
+ collects: 465,
77
+ comments: 324,
76
78
  url: '',
77
79
  },
78
80
  {
@@ -117,14 +119,47 @@ describe('xiaohongshu creator-notes', () => {
117
119
  title: '示例笔记',
118
120
  date: '2026年03月19日 12:00',
119
121
  views: 10,
120
- likes: 2,
121
- collects: 3,
122
- comments: 4,
122
+ likes: 3,
123
+ collects: 4,
124
+ comments: 2,
123
125
  url: 'https://creator.xiaohongshu.com/statistics/note-detail?noteId=69ba940500000000200384db',
124
126
  },
125
127
  ]);
126
128
  });
127
129
 
130
+ it('prefers note card dom data when the analyze api is unavailable', async () => {
131
+ const cmd = getRegistry().get('xiaohongshu/creator-notes');
132
+ expect(cmd?.func).toBeTypeOf('function');
133
+
134
+ const page = createPageMock([
135
+ undefined,
136
+ [
137
+ {
138
+ id: '693155fc000000000d03b42c',
139
+ title: '神雕侠侣战力金字塔',
140
+ date: '2025年12月04日 19:45',
141
+ metrics: [148284, 319, 2280, 466, 33],
142
+ },
143
+ ],
144
+ ]);
145
+
146
+ const result = await cmd!.func!(page, { limit: 1 });
147
+
148
+ expect(result).toEqual([
149
+ {
150
+ rank: 1,
151
+ id: '693155fc000000000d03b42c',
152
+ title: '神雕侠侣战力金字塔',
153
+ date: '2025年12月04日 19:45',
154
+ views: 148284,
155
+ likes: 2280,
156
+ collects: 466,
157
+ comments: 319,
158
+ url: 'https://creator.xiaohongshu.com/statistics/note-detail?noteId=693155fc000000000d03b42c',
159
+ },
160
+ ]);
161
+ });
162
+
128
163
  it('prefers the creator analyze API and preserves note ids', async () => {
129
164
  const cmd = getRegistry().get('xiaohongshu/creator-notes');
130
165
  expect(cmd?.func).toBeTypeOf('function');
@@ -30,6 +30,13 @@ type CreatorNoteRow = {
30
30
 
31
31
  export type { CreatorNoteRow };
32
32
 
33
+ type CreatorNoteDomCard = {
34
+ id: string;
35
+ title: string;
36
+ date: string;
37
+ metrics: number[];
38
+ };
39
+
33
40
  type CreatorAnalyzeApiResponse = {
34
41
  error?: string;
35
42
  data?: {
@@ -97,9 +104,9 @@ export function parseCreatorNotesText(bodyText: string): CreatorNoteRow[] {
97
104
  title,
98
105
  date: dateMatch[1],
99
106
  views: metrics[0] ?? 0,
100
- likes: metrics[1] ?? 0,
101
- collects: metrics[2] ?? 0,
102
- comments: metrics[3] ?? 0,
107
+ likes: metrics[2] ?? 0,
108
+ collects: metrics[3] ?? 0,
109
+ comments: metrics[1] ?? 0,
103
110
  url: '',
104
111
  });
105
112
 
@@ -123,6 +130,19 @@ export function parseCreatorNoteIdsFromHtml(bodyHtml: string): string[] {
123
130
  return ids;
124
131
  }
125
132
 
133
+ function mapDomCards(cards: CreatorNoteDomCard[]): CreatorNoteRow[] {
134
+ return cards.map((card) => ({
135
+ id: card.id,
136
+ title: card.title,
137
+ date: card.date,
138
+ views: card.metrics[0] ?? 0,
139
+ likes: card.metrics[2] ?? 0,
140
+ collects: card.metrics[3] ?? 0,
141
+ comments: card.metrics[1] ?? 0,
142
+ url: buildNoteDetailUrl(card.id),
143
+ }));
144
+ }
145
+
126
146
  function mapAnalyzeItems(items: NonNullable<CreatorAnalyzeApiResponse['data']>['note_infos']): CreatorNoteRow[] {
127
147
  return (items ?? []).map((item) => ({
128
148
  id: item.id ?? '',
@@ -142,7 +162,6 @@ async function fetchCreatorNotesByApi(page: IPage, limit: number): Promise<Creat
142
162
  const notes: CreatorNoteRow[] = [];
143
163
 
144
164
  await page.goto(`https://creator.xiaohongshu.com/statistics/data-analysis?type=0&page_size=${pageSize}&page_num=1`);
145
- await page.wait(4);
146
165
 
147
166
  for (let pageNum = 1; pageNum <= maxPages && notes.length < limit; pageNum++) {
148
167
  const apiPath = `${NOTE_ANALYZE_API_PATH}?type=0&page_size=${pageSize}&page_num=${pageNum}`;
@@ -190,10 +209,30 @@ export async function fetchCreatorNotes(page: IPage, limit: number): Promise<Cre
190
209
 
191
210
  if (notes.length === 0) {
192
211
  await page.goto('https://creator.xiaohongshu.com/new/note-manager');
193
- await page.wait(4);
194
212
 
195
213
  const maxPageDowns = Math.max(0, Math.ceil(limit / 10) + 1);
196
214
  for (let i = 0; i <= maxPageDowns; i++) {
215
+ const domCards = await page.evaluate(`() => {
216
+ const noteIdRe = /"noteId":"([0-9a-f]{24})"/;
217
+ return Array.from(document.querySelectorAll('div.note[data-impression], div.note')).map((card) => {
218
+ const impression = card.getAttribute('data-impression') || '';
219
+ const id = impression.match(noteIdRe)?.[1] || '';
220
+ const title = (card.querySelector('.title, .raw')?.innerText || '').trim();
221
+ const dateText = (card.querySelector('.time_status, .time')?.innerText || '').trim();
222
+ const date = dateText.replace(/^发布于\\s*/, '');
223
+ const metrics = Array.from(card.querySelectorAll('.icon_list .icon'))
224
+ .map((el) => parseInt((el.innerText || '').trim(), 10))
225
+ .filter((value) => Number.isFinite(value));
226
+ return { id, title, date, metrics };
227
+ });
228
+ }`) as CreatorNoteDomCard[] | undefined;
229
+ const parsedDomNotes = mapDomCards(Array.isArray(domCards) ? domCards : []).filter((note) => note.title && note.date);
230
+ if (parsedDomNotes.length > 0) {
231
+ notes = parsedDomNotes;
232
+ }
233
+
234
+ if (notes.length >= limit || (notes.length > 0 && i === 0)) break;
235
+
197
236
  const body = await page.evaluate('() => ({ text: document.body.innerText, html: document.body.innerHTML })') as {
198
237
  text?: string;
199
238
  html?: string;
@@ -21,7 +21,6 @@ cli({
21
21
  columns: ['field', 'value'],
22
22
  func: async (page, _kwargs) => {
23
23
  await page.goto('https://creator.xiaohongshu.com/new/home');
24
- await page.wait(3);
25
24
 
26
25
  const data = await page.evaluate(`
27
26
  async () => {
@@ -32,7 +32,6 @@ cli({
32
32
 
33
33
  // Navigate to creator center for cookie context
34
34
  await page.goto('https://creator.xiaohongshu.com/new/home');
35
- await page.wait(3);
36
35
 
37
36
  const data = await page.evaluate(`
38
37
  async () => {