@jackwener/opencli 1.0.6 → 1.1.1

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 (400) 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/README.md +51 -10
  8. package/README.zh-CN.md +29 -11
  9. package/SKILL.md +102 -33
  10. package/dist/browser/cdp.js +6 -1
  11. package/dist/browser/page.d.ts +4 -1
  12. package/dist/browser/page.js +7 -1
  13. package/dist/build-manifest.js +23 -16
  14. package/dist/cli-manifest.json +951 -296
  15. package/dist/cli.d.ts +6 -0
  16. package/dist/cli.js +225 -148
  17. package/dist/clis/antigravity/serve.js +296 -47
  18. package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
  19. package/dist/clis/apple-podcasts/commands.test.js +76 -0
  20. package/dist/clis/apple-podcasts/search.js +2 -2
  21. package/dist/clis/apple-podcasts/top.js +9 -2
  22. package/dist/clis/arxiv/paper.js +21 -0
  23. package/dist/clis/arxiv/search.js +24 -0
  24. package/dist/clis/arxiv/utils.d.ts +18 -0
  25. package/dist/clis/arxiv/utils.js +49 -0
  26. package/dist/clis/bilibili/dynamic.js +1 -1
  27. package/dist/clis/bilibili/favorite.js +1 -1
  28. package/dist/clis/bilibili/feed.js +1 -1
  29. package/dist/clis/bilibili/following.js +1 -1
  30. package/dist/clis/bilibili/history.js +1 -1
  31. package/dist/clis/bilibili/me.js +1 -1
  32. package/dist/clis/bilibili/ranking.js +1 -1
  33. package/dist/clis/bilibili/search.js +3 -3
  34. package/dist/clis/bilibili/subtitle.js +1 -1
  35. package/dist/clis/bilibili/user-videos.js +1 -1
  36. package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
  37. package/dist/clis/bloomberg/businessweek.d.ts +1 -0
  38. package/dist/clis/bloomberg/businessweek.js +17 -0
  39. package/dist/clis/bloomberg/economics.d.ts +1 -0
  40. package/dist/clis/bloomberg/economics.js +17 -0
  41. package/dist/clis/bloomberg/feeds.d.ts +1 -0
  42. package/dist/clis/bloomberg/feeds.js +15 -0
  43. package/dist/clis/bloomberg/industries.d.ts +1 -0
  44. package/dist/clis/bloomberg/industries.js +17 -0
  45. package/dist/clis/bloomberg/main.d.ts +1 -0
  46. package/dist/clis/bloomberg/main.js +17 -0
  47. package/dist/clis/bloomberg/markets.d.ts +1 -0
  48. package/dist/clis/bloomberg/markets.js +17 -0
  49. package/dist/clis/bloomberg/news.d.ts +1 -0
  50. package/dist/clis/bloomberg/news.js +105 -0
  51. package/dist/clis/bloomberg/opinions.d.ts +1 -0
  52. package/dist/clis/bloomberg/opinions.js +17 -0
  53. package/dist/clis/bloomberg/politics.d.ts +1 -0
  54. package/dist/clis/bloomberg/politics.js +17 -0
  55. package/dist/clis/bloomberg/tech.d.ts +1 -0
  56. package/dist/clis/bloomberg/tech.js +17 -0
  57. package/dist/clis/bloomberg/utils.d.ts +34 -0
  58. package/dist/clis/bloomberg/utils.js +364 -0
  59. package/dist/clis/bloomberg/utils.test.d.ts +1 -0
  60. package/dist/clis/bloomberg/utils.test.js +129 -0
  61. package/dist/clis/boss/batchgreet.d.ts +1 -0
  62. package/dist/clis/boss/batchgreet.js +147 -0
  63. package/dist/clis/boss/chatlist.js +2 -2
  64. package/dist/clis/boss/detail.js +2 -2
  65. package/dist/clis/boss/exchange.d.ts +1 -0
  66. package/dist/clis/boss/exchange.js +111 -0
  67. package/dist/clis/boss/greet.d.ts +1 -0
  68. package/dist/clis/boss/greet.js +175 -0
  69. package/dist/clis/boss/invite.d.ts +1 -0
  70. package/dist/clis/boss/invite.js +158 -0
  71. package/dist/clis/boss/joblist.d.ts +1 -0
  72. package/dist/clis/boss/joblist.js +55 -0
  73. package/dist/clis/boss/mark.d.ts +1 -0
  74. package/dist/clis/boss/mark.js +141 -0
  75. package/dist/clis/boss/recommend.d.ts +1 -0
  76. package/dist/clis/boss/recommend.js +83 -0
  77. package/dist/clis/boss/search.js +1 -1
  78. package/dist/clis/boss/send.js +1 -1
  79. package/dist/clis/boss/stats.d.ts +1 -0
  80. package/dist/clis/boss/stats.js +116 -0
  81. package/dist/clis/chaoxing/assignments.js +1 -1
  82. package/dist/clis/chaoxing/exams.js +1 -1
  83. package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
  84. package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
  85. package/dist/clis/chaoxing/utils.test.d.ts +1 -0
  86. package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
  87. package/dist/clis/chatgpt/read.js +1 -1
  88. package/dist/clis/chatwise/export.js +1 -1
  89. package/dist/clis/chatwise/model.js +2 -2
  90. package/dist/clis/chatwise/screenshot.js +1 -1
  91. package/dist/clis/codex/export.js +1 -1
  92. package/dist/clis/codex/model.js +2 -2
  93. package/dist/clis/codex/screenshot.js +1 -1
  94. package/dist/clis/coupang/add-to-cart.js +3 -4
  95. package/dist/clis/coupang/search.js +2 -4
  96. package/dist/clis/coupang/utils.test.d.ts +1 -0
  97. package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
  98. package/dist/clis/ctrip/search.js +1 -1
  99. package/dist/clis/cursor/export.js +1 -1
  100. package/dist/clis/cursor/model.js +2 -2
  101. package/dist/clis/cursor/screenshot.js +1 -1
  102. package/dist/clis/jike/comment.js +2 -3
  103. package/dist/clis/jike/create.js +1 -2
  104. package/dist/clis/jike/feed.js +0 -1
  105. package/dist/clis/jike/like.js +1 -2
  106. package/dist/clis/jike/notifications.js +0 -1
  107. package/dist/clis/jike/post.yaml +1 -0
  108. package/dist/clis/jike/repost.js +1 -2
  109. package/dist/clis/jike/search.js +2 -3
  110. package/dist/clis/jike/topic.yaml +1 -0
  111. package/dist/clis/jike/user.yaml +1 -0
  112. package/dist/clis/jimeng/history.yaml +0 -1
  113. package/dist/clis/linkedin/search.js +7 -7
  114. package/dist/clis/linux-do/category.yaml +1 -0
  115. package/dist/clis/linux-do/search.yaml +4 -3
  116. package/dist/clis/linux-do/topic.yaml +1 -0
  117. package/dist/clis/notion/export.js +1 -1
  118. package/dist/clis/reddit/comment.js +3 -4
  119. package/dist/clis/reddit/read.js +4 -5
  120. package/dist/clis/reddit/save.js +2 -3
  121. package/dist/clis/reddit/saved.js +0 -1
  122. package/dist/clis/reddit/search.yaml +1 -0
  123. package/dist/clis/reddit/subscribe.js +0 -1
  124. package/dist/clis/reddit/upvote.js +2 -3
  125. package/dist/clis/reddit/upvoted.js +0 -1
  126. package/dist/clis/reddit/user-comments.yaml +1 -0
  127. package/dist/clis/reddit/user-posts.yaml +1 -0
  128. package/dist/clis/reddit/user.yaml +1 -0
  129. package/dist/clis/reuters/search.js +1 -1
  130. package/dist/clis/sinafinance/news.d.ts +7 -0
  131. package/dist/clis/sinafinance/news.js +61 -0
  132. package/dist/clis/smzdm/search.js +2 -3
  133. package/dist/clis/stackoverflow/search.yaml +1 -0
  134. package/dist/clis/steam/top-sellers.yaml +29 -0
  135. package/dist/clis/twitter/accept.js +2 -2
  136. package/dist/clis/twitter/article.js +2 -2
  137. package/dist/clis/twitter/block.d.ts +1 -0
  138. package/dist/clis/twitter/block.js +88 -0
  139. package/dist/clis/twitter/delete.js +1 -1
  140. package/dist/clis/twitter/hide-reply.d.ts +1 -0
  141. package/dist/clis/twitter/hide-reply.js +66 -0
  142. package/dist/clis/twitter/like.js +1 -1
  143. package/dist/clis/twitter/post.js +1 -1
  144. package/dist/clis/twitter/reply-dm.js +1 -1
  145. package/dist/clis/twitter/reply.js +2 -2
  146. package/dist/clis/twitter/search.js +1 -1
  147. package/dist/clis/twitter/thread.js +2 -2
  148. package/dist/clis/twitter/trending.d.ts +1 -0
  149. package/dist/clis/twitter/trending.js +91 -0
  150. package/dist/clis/twitter/unblock.d.ts +1 -0
  151. package/dist/clis/twitter/unblock.js +71 -0
  152. package/dist/clis/v2ex/topic.yaml +1 -0
  153. package/dist/clis/weibo/hot.js +0 -1
  154. package/dist/clis/weread/book.js +1 -1
  155. package/dist/clis/weread/highlights.js +1 -1
  156. package/dist/clis/weread/notes.js +1 -1
  157. package/dist/clis/weread/search.js +1 -1
  158. package/dist/clis/wikipedia/search.d.ts +1 -0
  159. package/dist/clis/wikipedia/search.js +30 -0
  160. package/dist/clis/wikipedia/summary.d.ts +1 -0
  161. package/dist/clis/wikipedia/summary.js +28 -0
  162. package/dist/clis/wikipedia/utils.d.ts +8 -0
  163. package/dist/clis/wikipedia/utils.js +18 -0
  164. package/dist/clis/xiaohongshu/creator-note-detail.d.ts +79 -5
  165. package/dist/clis/xiaohongshu/creator-note-detail.js +323 -70
  166. package/dist/clis/xiaohongshu/creator-note-detail.test.d.ts +1 -0
  167. package/dist/clis/xiaohongshu/creator-note-detail.test.js +258 -0
  168. package/dist/clis/xiaohongshu/creator-notes-summary.d.ts +28 -0
  169. package/dist/clis/xiaohongshu/creator-notes-summary.js +92 -0
  170. package/dist/clis/xiaohongshu/creator-notes-summary.test.d.ts +1 -0
  171. package/dist/clis/xiaohongshu/creator-notes-summary.test.js +49 -0
  172. package/dist/clis/xiaohongshu/creator-notes.d.ts +18 -5
  173. package/dist/clis/xiaohongshu/creator-notes.js +189 -71
  174. package/dist/clis/xiaohongshu/creator-notes.test.d.ts +1 -0
  175. package/dist/clis/xiaohongshu/creator-notes.test.js +191 -0
  176. package/dist/clis/xiaohongshu/creator-profile.js +0 -1
  177. package/dist/clis/xiaohongshu/creator-stats.js +0 -1
  178. package/dist/clis/xiaohongshu/download.js +2 -3
  179. package/dist/clis/xiaohongshu/feed.yaml +0 -1
  180. package/dist/clis/xiaohongshu/notifications.yaml +0 -1
  181. package/dist/clis/xiaohongshu/search.js +2 -2
  182. package/dist/clis/xiaohongshu/user.js +1 -2
  183. package/dist/clis/yahoo-finance/quote.js +0 -1
  184. package/dist/clis/youtube/search.js +1 -1
  185. package/dist/clis/youtube/transcript.js +1 -1
  186. package/dist/clis/youtube/video.js +1 -1
  187. package/dist/clis/zhihu/download.js +1 -2
  188. package/dist/clis/zhihu/question.js +1 -1
  189. package/dist/clis/zhihu/search.yaml +4 -3
  190. package/dist/commanderAdapter.d.ts +21 -0
  191. package/dist/commanderAdapter.js +111 -0
  192. package/dist/{engine.d.ts → discovery.d.ts} +0 -6
  193. package/dist/{engine.js → discovery.js} +1 -98
  194. package/dist/download/index.d.ts +2 -6
  195. package/dist/download/index.js +19 -46
  196. package/dist/engine.test.d.ts +1 -1
  197. package/dist/engine.test.js +8 -7
  198. package/dist/execution.d.ts +22 -0
  199. package/dist/execution.js +129 -0
  200. package/dist/explore.js +121 -107
  201. package/dist/external-clis.yaml +48 -0
  202. package/dist/external.d.ts +25 -0
  203. package/dist/external.js +156 -0
  204. package/dist/main.js +1 -1
  205. package/dist/pipeline/steps/browser.js +8 -2
  206. package/dist/registry.d.ts +2 -0
  207. package/dist/registry.js +2 -0
  208. package/dist/runtime.d.ts +5 -0
  209. package/dist/runtime.js +8 -0
  210. package/dist/serialization.d.ts +34 -0
  211. package/dist/serialization.js +63 -0
  212. package/dist/types.d.ts +4 -1
  213. package/docs/.vitepress/config.mts +14 -3
  214. package/docs/adapters/browser/arxiv.md +27 -0
  215. package/docs/adapters/browser/barchart.md +32 -0
  216. package/docs/adapters/browser/bloomberg.md +70 -0
  217. package/docs/adapters/browser/chaoxing.md +39 -0
  218. package/docs/adapters/browser/grok.md +35 -0
  219. package/docs/adapters/browser/hf.md +42 -0
  220. package/docs/adapters/browser/jike.md +45 -0
  221. package/docs/adapters/browser/jimeng.md +39 -0
  222. package/docs/adapters/browser/linux-do.md +45 -0
  223. package/docs/adapters/browser/sinafinance.md +35 -0
  224. package/docs/adapters/browser/stackoverflow.md +35 -0
  225. package/docs/adapters/browser/steam.md +26 -0
  226. package/docs/adapters/browser/twitter.md +3 -0
  227. package/docs/adapters/browser/weread.md +48 -0
  228. package/docs/adapters/browser/wikipedia.md +30 -0
  229. package/docs/adapters/browser/xiaohongshu.md +5 -1
  230. package/docs/adapters/desktop/chatgpt.md +3 -3
  231. package/docs/adapters/index.md +13 -0
  232. package/docs/advanced/download.md +4 -4
  233. package/docs/developer/architecture.md +17 -4
  234. package/package.json +1 -1
  235. package/scripts/check-doc-coverage.sh +69 -0
  236. package/scripts/copy-yaml.cjs +7 -0
  237. package/src/browser/cdp.ts +9 -4
  238. package/src/browser/page.ts +7 -1
  239. package/src/build-manifest.ts +25 -19
  240. package/src/cli.ts +253 -119
  241. package/src/clis/antigravity/serve.ts +323 -50
  242. package/src/clis/apple-podcasts/commands.test.ts +95 -0
  243. package/src/clis/apple-podcasts/search.ts +2 -2
  244. package/src/clis/apple-podcasts/top.ts +12 -2
  245. package/src/clis/arxiv/paper.ts +21 -0
  246. package/src/clis/arxiv/search.ts +24 -0
  247. package/src/clis/arxiv/utils.ts +63 -0
  248. package/src/clis/bilibili/dynamic.ts +1 -1
  249. package/src/clis/bilibili/favorite.ts +1 -1
  250. package/src/clis/bilibili/feed.ts +1 -1
  251. package/src/clis/bilibili/following.ts +1 -1
  252. package/src/clis/bilibili/history.ts +1 -1
  253. package/src/clis/bilibili/me.ts +1 -1
  254. package/src/clis/bilibili/ranking.ts +1 -1
  255. package/src/clis/bilibili/search.ts +3 -3
  256. package/src/clis/bilibili/subtitle.ts +1 -1
  257. package/src/clis/bilibili/user-videos.ts +1 -1
  258. package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
  259. package/src/clis/bloomberg/businessweek.ts +18 -0
  260. package/src/clis/bloomberg/economics.ts +18 -0
  261. package/src/clis/bloomberg/feeds.ts +16 -0
  262. package/src/clis/bloomberg/industries.ts +18 -0
  263. package/src/clis/bloomberg/main.ts +18 -0
  264. package/src/clis/bloomberg/markets.ts +18 -0
  265. package/src/clis/bloomberg/news.ts +136 -0
  266. package/src/clis/bloomberg/opinions.ts +18 -0
  267. package/src/clis/bloomberg/politics.ts +18 -0
  268. package/src/clis/bloomberg/tech.ts +18 -0
  269. package/src/clis/bloomberg/utils.test.ts +135 -0
  270. package/src/clis/bloomberg/utils.ts +429 -0
  271. package/src/clis/boss/batchgreet.ts +167 -0
  272. package/src/clis/boss/chatlist.ts +2 -2
  273. package/src/clis/boss/detail.ts +2 -2
  274. package/src/clis/boss/exchange.ts +126 -0
  275. package/src/clis/boss/greet.ts +198 -0
  276. package/src/clis/boss/invite.ts +177 -0
  277. package/src/clis/boss/joblist.ts +63 -0
  278. package/src/clis/boss/mark.ts +155 -0
  279. package/src/clis/boss/recommend.ts +94 -0
  280. package/src/clis/boss/search.ts +1 -1
  281. package/src/clis/boss/send.ts +1 -1
  282. package/src/clis/boss/stats.ts +130 -0
  283. package/src/clis/chaoxing/assignments.ts +1 -1
  284. package/src/clis/chaoxing/exams.ts +1 -1
  285. package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
  286. package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
  287. package/src/clis/chatgpt/README.zh-CN.md +3 -3
  288. package/src/clis/chatgpt/read.ts +1 -1
  289. package/src/clis/chatwise/export.ts +1 -1
  290. package/src/clis/chatwise/model.ts +2 -2
  291. package/src/clis/chatwise/screenshot.ts +1 -1
  292. package/src/clis/codex/export.ts +1 -1
  293. package/src/clis/codex/model.ts +2 -2
  294. package/src/clis/codex/screenshot.ts +1 -1
  295. package/src/clis/coupang/add-to-cart.ts +3 -4
  296. package/src/clis/coupang/search.ts +2 -4
  297. package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
  298. package/src/clis/ctrip/search.ts +1 -1
  299. package/src/clis/cursor/export.ts +1 -1
  300. package/src/clis/cursor/model.ts +2 -2
  301. package/src/clis/cursor/screenshot.ts +1 -1
  302. package/src/clis/jike/comment.ts +2 -3
  303. package/src/clis/jike/create.ts +1 -2
  304. package/src/clis/jike/feed.ts +0 -1
  305. package/src/clis/jike/like.ts +1 -2
  306. package/src/clis/jike/notifications.ts +0 -1
  307. package/src/clis/jike/post.yaml +1 -0
  308. package/src/clis/jike/repost.ts +1 -2
  309. package/src/clis/jike/search.ts +2 -3
  310. package/src/clis/jike/topic.yaml +1 -0
  311. package/src/clis/jike/user.yaml +1 -0
  312. package/src/clis/jimeng/history.yaml +0 -1
  313. package/src/clis/linkedin/search.ts +7 -7
  314. package/src/clis/linux-do/category.yaml +1 -0
  315. package/src/clis/linux-do/search.yaml +4 -3
  316. package/src/clis/linux-do/topic.yaml +1 -0
  317. package/src/clis/notion/export.ts +1 -1
  318. package/src/clis/reddit/comment.ts +3 -4
  319. package/src/clis/reddit/read.ts +4 -5
  320. package/src/clis/reddit/save.ts +2 -3
  321. package/src/clis/reddit/saved.ts +0 -1
  322. package/src/clis/reddit/search.yaml +1 -0
  323. package/src/clis/reddit/subscribe.ts +0 -1
  324. package/src/clis/reddit/upvote.ts +2 -3
  325. package/src/clis/reddit/upvoted.ts +0 -1
  326. package/src/clis/reddit/user-comments.yaml +1 -0
  327. package/src/clis/reddit/user-posts.yaml +1 -0
  328. package/src/clis/reddit/user.yaml +1 -0
  329. package/src/clis/reuters/search.ts +1 -1
  330. package/src/clis/sinafinance/news.ts +76 -0
  331. package/src/clis/smzdm/search.ts +2 -3
  332. package/src/clis/stackoverflow/search.yaml +1 -0
  333. package/src/clis/steam/top-sellers.yaml +29 -0
  334. package/src/clis/twitter/accept.ts +2 -2
  335. package/src/clis/twitter/article.ts +2 -2
  336. package/src/clis/twitter/block.ts +92 -0
  337. package/src/clis/twitter/delete.ts +1 -1
  338. package/src/clis/twitter/hide-reply.ts +70 -0
  339. package/src/clis/twitter/like.ts +1 -1
  340. package/src/clis/twitter/post.ts +1 -1
  341. package/src/clis/twitter/reply-dm.ts +1 -1
  342. package/src/clis/twitter/reply.ts +2 -2
  343. package/src/clis/twitter/search.ts +1 -1
  344. package/src/clis/twitter/thread.ts +2 -2
  345. package/src/clis/twitter/trending.ts +113 -0
  346. package/src/clis/twitter/unblock.ts +75 -0
  347. package/src/clis/v2ex/topic.yaml +1 -0
  348. package/src/clis/weibo/hot.ts +0 -1
  349. package/src/clis/weread/book.ts +1 -1
  350. package/src/clis/weread/highlights.ts +1 -1
  351. package/src/clis/weread/notes.ts +1 -1
  352. package/src/clis/weread/search.ts +1 -1
  353. package/src/clis/wikipedia/search.ts +32 -0
  354. package/src/clis/wikipedia/summary.ts +28 -0
  355. package/src/clis/wikipedia/utils.ts +20 -0
  356. package/src/clis/xiaohongshu/creator-note-detail.test.ts +272 -0
  357. package/src/clis/xiaohongshu/creator-note-detail.ts +425 -73
  358. package/src/clis/xiaohongshu/creator-notes-summary.test.ts +54 -0
  359. package/src/clis/xiaohongshu/creator-notes-summary.ts +120 -0
  360. package/src/clis/xiaohongshu/creator-notes.test.ts +211 -0
  361. package/src/clis/xiaohongshu/creator-notes.ts +254 -75
  362. package/src/clis/xiaohongshu/creator-profile.ts +0 -1
  363. package/src/clis/xiaohongshu/creator-stats.ts +0 -1
  364. package/src/clis/xiaohongshu/download.ts +2 -3
  365. package/src/clis/xiaohongshu/feed.yaml +0 -1
  366. package/src/clis/xiaohongshu/notifications.yaml +0 -1
  367. package/src/clis/xiaohongshu/search.ts +2 -2
  368. package/src/clis/xiaohongshu/user.ts +1 -2
  369. package/src/clis/yahoo-finance/quote.ts +0 -1
  370. package/src/clis/youtube/search.ts +1 -1
  371. package/src/clis/youtube/transcript.ts +1 -1
  372. package/src/clis/youtube/video.ts +1 -1
  373. package/src/clis/zhihu/download.ts +1 -2
  374. package/src/clis/zhihu/question.ts +1 -1
  375. package/src/clis/zhihu/search.yaml +4 -3
  376. package/src/commanderAdapter.ts +113 -0
  377. package/src/daemon.ts +3 -3
  378. package/src/{engine.ts → discovery.ts} +1 -108
  379. package/src/download/index.ts +21 -54
  380. package/src/engine.test.ts +8 -7
  381. package/src/execution.ts +138 -0
  382. package/src/explore.ts +135 -109
  383. package/src/external-clis.yaml +48 -0
  384. package/src/external.ts +185 -0
  385. package/src/main.ts +1 -1
  386. package/src/pipeline/steps/browser.ts +7 -2
  387. package/src/registry.ts +5 -0
  388. package/src/runtime.ts +9 -0
  389. package/src/serialization.ts +79 -0
  390. package/src/types.ts +1 -1
  391. package/tests/e2e/browser-public.test.ts +25 -0
  392. package/tests/e2e/public-commands.test.ts +55 -1
  393. package/dist/clis/twitter/trending.yaml +0 -46
  394. package/src/clis/twitter/trending.yaml +0 -46
  395. /package/dist/{chaoxing.test.d.ts → clis/arxiv/paper.d.ts} +0 -0
  396. /package/dist/{coupang.test.d.ts → clis/arxiv/search.d.ts} +0 -0
  397. /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
  398. /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
  399. /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
  400. /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Xiaohongshu Creator Notes Summary — batch summary for recent notes.
3
+ *
4
+ * Combines creator-notes and creator-note-detail into a single command that
5
+ * returns one summary row per note, suitable for quick review or downstream JSON use.
6
+ */
7
+ import { type CreatorNoteRow } from './creator-notes.js';
8
+ import { type CreatorNoteDetailRow } from './creator-note-detail.js';
9
+ type CreatorNoteSummaryRow = {
10
+ rank: number;
11
+ id: string;
12
+ title: string;
13
+ published_at: string;
14
+ views: string;
15
+ likes: string;
16
+ collects: string;
17
+ comments: string;
18
+ shares: string;
19
+ avg_view_time: string;
20
+ rise_fans: string;
21
+ top_source: string;
22
+ top_source_pct: string;
23
+ top_interest: string;
24
+ top_interest_pct: string;
25
+ url: string;
26
+ };
27
+ export declare function summarizeCreatorNote(note: CreatorNoteRow, rows: CreatorNoteDetailRow[], rank: number): CreatorNoteSummaryRow;
28
+ export {};
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Xiaohongshu Creator Notes Summary — batch summary for recent notes.
3
+ *
4
+ * Combines creator-notes and creator-note-detail into a single command that
5
+ * returns one summary row per note, suitable for quick review or downstream JSON use.
6
+ */
7
+ import { cli, Strategy } from '../../registry.js';
8
+ import { fetchCreatorNotes } from './creator-notes.js';
9
+ import { fetchCreatorNoteDetailRows } from './creator-note-detail.js';
10
+ function findDetailValue(rows, metric) {
11
+ return rows.find((row) => row.metric === metric)?.value ?? '';
12
+ }
13
+ function findTopBySectionPrefix(rows, section, prefix) {
14
+ const matches = rows.filter((row) => row.section === section && row.metric.startsWith(prefix) && row.value);
15
+ if (matches.length === 0)
16
+ return { label: '', value: '' };
17
+ const sorted = [...matches].sort((a, b) => parseFloat(b.value) - parseFloat(a.value));
18
+ const top = sorted[0];
19
+ return {
20
+ label: top.metric.slice(prefix.length),
21
+ value: top.value,
22
+ };
23
+ }
24
+ export function summarizeCreatorNote(note, rows, rank) {
25
+ const topSource = findTopBySectionPrefix(rows, '观看来源', '');
26
+ const topInterest = findTopBySectionPrefix(rows, '观众画像', '兴趣/');
27
+ return {
28
+ rank,
29
+ id: note.id,
30
+ title: note.title,
31
+ published_at: findDetailValue(rows, 'published_at') || note.date,
32
+ views: findDetailValue(rows, '观看数') || String(note.views),
33
+ likes: findDetailValue(rows, '点赞数') || String(note.likes),
34
+ collects: findDetailValue(rows, '收藏数') || String(note.collects),
35
+ comments: findDetailValue(rows, '评论数') || String(note.comments),
36
+ shares: findDetailValue(rows, '分享数'),
37
+ avg_view_time: findDetailValue(rows, '平均观看时长'),
38
+ rise_fans: findDetailValue(rows, '涨粉数'),
39
+ top_source: topSource.label,
40
+ top_source_pct: topSource.value,
41
+ top_interest: topInterest.label,
42
+ top_interest_pct: topInterest.value,
43
+ url: note.url,
44
+ };
45
+ }
46
+ cli({
47
+ site: 'xiaohongshu',
48
+ name: 'creator-notes-summary',
49
+ description: '小红书最近笔记批量摘要 (列表 + 单篇关键数据汇总)',
50
+ domain: 'creator.xiaohongshu.com',
51
+ strategy: Strategy.COOKIE,
52
+ browser: true,
53
+ args: [
54
+ { name: 'limit', type: 'int', default: 3, help: 'Number of recent notes to summarize' },
55
+ ],
56
+ columns: ['rank', 'id', 'title', 'views', 'likes', 'collects', 'comments', 'shares', 'avg_view_time', 'rise_fans', 'top_source', 'top_interest', 'url'],
57
+ timeoutSeconds: 180,
58
+ func: async (page, kwargs) => {
59
+ const limit = kwargs.limit || 3;
60
+ const notes = await fetchCreatorNotes(page, limit);
61
+ if (!notes.length) {
62
+ throw new Error('No notes found. Are you logged into creator.xiaohongshu.com?');
63
+ }
64
+ const results = [];
65
+ for (const [index, note] of notes.entries()) {
66
+ if (!note.id) {
67
+ results.push({
68
+ rank: index + 1,
69
+ id: note.id,
70
+ title: note.title,
71
+ published_at: note.date,
72
+ views: String(note.views),
73
+ likes: String(note.likes),
74
+ collects: String(note.collects),
75
+ comments: String(note.comments),
76
+ shares: '',
77
+ avg_view_time: '',
78
+ rise_fans: '',
79
+ top_source: '',
80
+ top_source_pct: '',
81
+ top_interest: '',
82
+ top_interest_pct: '',
83
+ url: note.url,
84
+ });
85
+ continue;
86
+ }
87
+ const detailRows = await fetchCreatorNoteDetailRows(page, note.id);
88
+ results.push(summarizeCreatorNote(note, detailRows, index + 1));
89
+ }
90
+ return results;
91
+ },
92
+ });
@@ -0,0 +1 @@
1
+ import './creator-notes-summary.js';
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { summarizeCreatorNote } from './creator-notes-summary.js';
3
+ import './creator-notes-summary.js';
4
+ describe('xiaohongshu creator-notes-summary', () => {
5
+ it('summarizes note list row and detail rows into one compact row', () => {
6
+ const note = {
7
+ id: '69ba940500000000200384db',
8
+ title: '一张图讲清 诡秘之主·耕种者途径',
9
+ date: '2026年03月18日 20:01',
10
+ views: 549,
11
+ likes: 19,
12
+ collects: 10,
13
+ comments: 7,
14
+ url: 'https://creator.xiaohongshu.com/statistics/note-detail?noteId=69ba940500000000200384db',
15
+ };
16
+ const rows = [
17
+ { section: '笔记信息', metric: 'published_at', value: '2026-03-18 20:01', extra: '' },
18
+ { section: '基础数据', metric: '观看数', value: '549', extra: '' },
19
+ { section: '互动数据', metric: '点赞数', value: '19', extra: '' },
20
+ { section: '互动数据', metric: '收藏数', value: '10', extra: '' },
21
+ { section: '互动数据', metric: '评论数', value: '7', extra: '' },
22
+ { section: '互动数据', metric: '分享数', value: '6', extra: '' },
23
+ { section: '基础数据', metric: '平均观看时长', value: '51.5秒', extra: '' },
24
+ { section: '基础数据', metric: '涨粉数', value: '3', extra: '' },
25
+ { section: '观看来源', metric: '首页推荐', value: '89.9%', extra: '' },
26
+ { section: '观看来源', metric: '搜索', value: '0.3%', extra: '' },
27
+ { section: '观众画像', metric: '兴趣/二次元', value: '13%', extra: '' },
28
+ { section: '观众画像', metric: '兴趣/游戏', value: '11%', extra: '' },
29
+ ];
30
+ expect(summarizeCreatorNote(note, rows, 1)).toEqual({
31
+ rank: 1,
32
+ id: '69ba940500000000200384db',
33
+ title: '一张图讲清 诡秘之主·耕种者途径',
34
+ published_at: '2026-03-18 20:01',
35
+ views: '549',
36
+ likes: '19',
37
+ collects: '10',
38
+ comments: '7',
39
+ shares: '6',
40
+ avg_view_time: '51.5秒',
41
+ rise_fans: '3',
42
+ top_source: '首页推荐',
43
+ top_source_pct: '89.9%',
44
+ top_interest: '二次元',
45
+ top_interest_pct: '13%',
46
+ url: 'https://creator.xiaohongshu.com/statistics/note-detail?noteId=69ba940500000000200384db',
47
+ });
48
+ });
49
+ });
@@ -1,11 +1,24 @@
1
1
  /**
2
2
  * Xiaohongshu Creator Note List — per-note metrics from the creator backend.
3
3
  *
4
- * Navigates to the note manager page and extracts per-note data from
5
- * the rendered DOM. This approach bypasses the v2 API signature requirement.
6
- *
7
- * Returns: note title, publish date, views, likes, collects, comments.
4
+ * In CDP mode we capture the real creator analytics API response so the list
5
+ * includes stable note ids and detail-page URLs. If that capture is unavailable,
6
+ * we fall back to the older interceptor and DOM parsing paths.
8
7
  *
9
8
  * Requires: logged into creator.xiaohongshu.com in Chrome.
10
9
  */
11
- export {};
10
+ import type { IPage } from '../../types.js';
11
+ type CreatorNoteRow = {
12
+ id: string;
13
+ title: string;
14
+ date: string;
15
+ views: number;
16
+ likes: number;
17
+ collects: number;
18
+ comments: number;
19
+ url: string;
20
+ };
21
+ export type { CreatorNoteRow };
22
+ export declare function parseCreatorNotesText(bodyText: string): CreatorNoteRow[];
23
+ export declare function parseCreatorNoteIdsFromHtml(bodyHtml: string): string[];
24
+ export declare function fetchCreatorNotes(page: IPage, limit: number): Promise<CreatorNoteRow[]>;
@@ -1,14 +1,198 @@
1
1
  /**
2
2
  * Xiaohongshu Creator Note List — per-note metrics from the creator backend.
3
3
  *
4
- * Navigates to the note manager page and extracts per-note data from
5
- * the rendered DOM. This approach bypasses the v2 API signature requirement.
6
- *
7
- * Returns: note title, publish date, views, likes, collects, comments.
4
+ * In CDP mode we capture the real creator analytics API response so the list
5
+ * includes stable note ids and detail-page URLs. If that capture is unavailable,
6
+ * we fall back to the older interceptor and DOM parsing paths.
8
7
  *
9
8
  * Requires: logged into creator.xiaohongshu.com in Chrome.
10
9
  */
11
10
  import { cli, Strategy } from '../../registry.js';
11
+ const DATE_LINE_RE = /^发布于 (\d{4}年\d{2}月\d{2}日 \d{2}:\d{2})$/;
12
+ const METRIC_LINE_RE = /^\d+$/;
13
+ const VISIBILITY_LINE_RE = /可见$/;
14
+ const NOTE_ANALYZE_API_PATH = '/api/galaxy/creator/datacenter/note/analyze/list';
15
+ const NOTE_DETAIL_PAGE_URL = 'https://creator.xiaohongshu.com/statistics/note-detail';
16
+ const NOTE_ID_HTML_RE = /&quot;noteId&quot;:&quot;([0-9a-f]{24})&quot;/g;
17
+ function buildNoteDetailUrl(noteId) {
18
+ return noteId ? `${NOTE_DETAIL_PAGE_URL}?noteId=${encodeURIComponent(noteId)}` : '';
19
+ }
20
+ function formatPostTime(ts) {
21
+ if (!ts)
22
+ return '';
23
+ // XHS API timestamps are Beijing time (UTC+8)
24
+ const date = new Date(ts + 8 * 3600_000);
25
+ const pad = (value) => String(value).padStart(2, '0');
26
+ return `${date.getUTCFullYear()}年${pad(date.getUTCMonth() + 1)}月${pad(date.getUTCDate())}日 ${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}`;
27
+ }
28
+ export function parseCreatorNotesText(bodyText) {
29
+ const lines = bodyText
30
+ .split('\n')
31
+ .map((line) => line.trim())
32
+ .filter(Boolean);
33
+ const results = [];
34
+ const seen = new Set();
35
+ for (let i = 0; i < lines.length; i++) {
36
+ const dateMatch = lines[i].match(DATE_LINE_RE);
37
+ if (!dateMatch)
38
+ continue;
39
+ let titleIndex = i - 1;
40
+ while (titleIndex >= 0 && VISIBILITY_LINE_RE.test(lines[titleIndex]))
41
+ titleIndex--;
42
+ if (titleIndex < 0)
43
+ continue;
44
+ const title = lines[titleIndex];
45
+ const metrics = [];
46
+ let cursor = i + 1;
47
+ while (cursor < lines.length && METRIC_LINE_RE.test(lines[cursor]) && metrics.length < 5) {
48
+ metrics.push(parseInt(lines[cursor], 10));
49
+ cursor++;
50
+ }
51
+ if (metrics.length < 4)
52
+ continue;
53
+ const key = `${title}@@${dateMatch[1]}`;
54
+ if (seen.has(key))
55
+ continue;
56
+ seen.add(key);
57
+ results.push({
58
+ id: '',
59
+ title,
60
+ date: dateMatch[1],
61
+ views: metrics[0] ?? 0,
62
+ likes: metrics[2] ?? 0,
63
+ collects: metrics[3] ?? 0,
64
+ comments: metrics[1] ?? 0,
65
+ url: '',
66
+ });
67
+ i = cursor - 1;
68
+ }
69
+ return results;
70
+ }
71
+ export function parseCreatorNoteIdsFromHtml(bodyHtml) {
72
+ const ids = [];
73
+ const seen = new Set();
74
+ for (const match of bodyHtml.matchAll(NOTE_ID_HTML_RE)) {
75
+ const id = match[1];
76
+ if (!id || seen.has(id))
77
+ continue;
78
+ seen.add(id);
79
+ ids.push(id);
80
+ }
81
+ return ids;
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
+ }
95
+ function mapAnalyzeItems(items) {
96
+ return (items ?? []).map((item) => ({
97
+ id: item.id ?? '',
98
+ title: item.title ?? '',
99
+ date: formatPostTime(item.post_time),
100
+ views: item.read_count ?? 0,
101
+ likes: item.like_count ?? 0,
102
+ collects: item.fav_count ?? 0,
103
+ comments: item.comment_count ?? 0,
104
+ url: buildNoteDetailUrl(item.id),
105
+ }));
106
+ }
107
+ async function fetchCreatorNotesByApi(page, limit) {
108
+ const pageSize = Math.min(Math.max(limit, 10), 20);
109
+ const maxPages = Math.max(1, Math.ceil(limit / pageSize));
110
+ const notes = [];
111
+ await page.goto(`https://creator.xiaohongshu.com/statistics/data-analysis?type=0&page_size=${pageSize}&page_num=1`);
112
+ for (let pageNum = 1; pageNum <= maxPages && notes.length < limit; pageNum++) {
113
+ const apiPath = `${NOTE_ANALYZE_API_PATH}?type=0&page_size=${pageSize}&page_num=${pageNum}`;
114
+ const fetched = await page.evaluate(`
115
+ async () => {
116
+ try {
117
+ const resp = await fetch(${JSON.stringify(apiPath)}, { credentials: 'include' });
118
+ if (!resp.ok) return { error: 'HTTP ' + resp.status };
119
+ return await resp.json();
120
+ } catch (e) {
121
+ return { error: e?.message ?? String(e) };
122
+ }
123
+ }
124
+ `);
125
+ let items = fetched?.data?.note_infos ?? [];
126
+ if (!items.length) {
127
+ await page.installInterceptor(NOTE_ANALYZE_API_PATH);
128
+ await page.evaluate(`
129
+ async () => {
130
+ try {
131
+ await fetch(${JSON.stringify(apiPath)}, { credentials: 'include' });
132
+ } catch {}
133
+ return true;
134
+ }
135
+ `);
136
+ await page.wait(1);
137
+ const intercepted = await page.getInterceptedRequests();
138
+ const data = intercepted.find((entry) => Array.isArray(entry?.data?.note_infos));
139
+ items = data?.data?.note_infos ?? [];
140
+ }
141
+ if (!items.length)
142
+ break;
143
+ notes.push(...mapAnalyzeItems(items));
144
+ if (items.length < pageSize)
145
+ break;
146
+ }
147
+ return notes.slice(0, limit);
148
+ }
149
+ export async function fetchCreatorNotes(page, limit) {
150
+ let notes = await fetchCreatorNotesByApi(page, limit);
151
+ if (notes.length === 0) {
152
+ await page.goto('https://creator.xiaohongshu.com/new/note-manager');
153
+ const maxPageDowns = Math.max(0, Math.ceil(limit / 10) + 1);
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;
175
+ const body = await page.evaluate('() => ({ text: document.body.innerText, html: document.body.innerHTML })');
176
+ const bodyText = typeof body?.text === 'string' ? body.text : '';
177
+ const bodyHtml = typeof body?.html === 'string' ? body.html : '';
178
+ const parsedNotes = parseCreatorNotesText(bodyText);
179
+ const noteIds = parseCreatorNoteIdsFromHtml(bodyHtml);
180
+ notes = parsedNotes.map((note, index) => {
181
+ const id = noteIds[index] ?? '';
182
+ return {
183
+ ...note,
184
+ id,
185
+ url: buildNoteDetailUrl(id),
186
+ };
187
+ });
188
+ if (notes.length >= limit || i === maxPageDowns)
189
+ break;
190
+ await page.pressKey('PageDown');
191
+ await page.wait(1);
192
+ }
193
+ }
194
+ return notes.slice(0, limit);
195
+ }
12
196
  cli({
13
197
  site: 'xiaohongshu',
14
198
  name: 'creator-notes',
@@ -22,73 +206,7 @@ cli({
22
206
  columns: ['rank', 'id', 'title', 'date', 'views', 'likes', 'collects', 'comments', 'url'],
23
207
  func: async (page, kwargs) => {
24
208
  const limit = kwargs.limit || 20;
25
- // Navigate to note manager
26
- await page.goto('https://creator.xiaohongshu.com/new/note-manager');
27
- await page.wait(4);
28
- // Scroll to load more notes if needed
29
- await page.autoScroll({ times: Math.ceil(limit / 10), delayMs: 1500 });
30
- // Extract note data from rendered DOM
31
- const notes = await page.evaluate(`
32
- (() => {
33
- const results = [];
34
- // Note cards in the manager page contain title, date, and metric numbers
35
- // Each note card has a consistent structure with the title, date line,
36
- // and a row of 4 numbers (views, likes, collects, comments)
37
- const cards = document.querySelectorAll('[class*="note-item"], [class*="noteItem"], [class*="card"]');
38
-
39
- if (cards.length === 0) {
40
- // Fallback: parse from any container with note-like content
41
- const allText = document.body.innerText;
42
- const notePattern = /(.+?)\\s+发布于\\s+(\\d{4}年\\d{2}月\\d{2}日\\s+\\d{2}:\\d{2})\\s*(\\d+)\\s*(\\d+)\\s*(\\d+)\\s*(\\d+)/g;
43
- let match;
44
- while ((match = notePattern.exec(allText)) !== null) {
45
- results.push({
46
- title: match[1].trim(),
47
- date: match[2],
48
- views: parseInt(match[3]) || 0,
49
- likes: parseInt(match[4]) || 0,
50
- collects: parseInt(match[5]) || 0,
51
- comments: parseInt(match[6]) || 0,
52
- });
53
- }
54
- return results;
55
- }
56
-
57
- cards.forEach(card => {
58
- const text = card.innerText || '';
59
- const linkEl = card.querySelector('a[href*="/publish/"], a[href*="/note/"], a[href*="/explore/"]');
60
- const href = linkEl?.getAttribute('href') || '';
61
- const idMatch = href.match(/\/(?:publish|explore|note)\/([a-zA-Z0-9]+)/);
62
- // Try to extract structured data
63
- const lines = text.split('\\n').map(l => l.trim()).filter(Boolean);
64
- if (lines.length < 2) return;
65
-
66
- const title = lines[0];
67
- const dateLine = lines.find(l => l.includes('发布于'));
68
- const dateMatch = dateLine?.match(/发布于\\s+(\\d{4}年\\d{2}月\\d{2}日\\s+\\d{2}:\\d{2})/);
69
-
70
- // Remove the publish timestamp before collecting note metrics.
71
- // Otherwise year/month/day/hour digits are picked up as views/likes/etc.
72
- const metricText = dateLine ? text.replace(dateLine, ' ') : text;
73
- const nums = metricText.match(/(?:^|\\s)(\\d+)(?:\\s|$)/g)?.map(n => parseInt(n.trim())) || [];
74
-
75
- if (title && !title.includes('全部笔记')) {
76
- results.push({
77
- id: idMatch ? idMatch[1] : '',
78
- title: title.replace(/\\s+/g, ' ').substring(0, 80),
79
- date: dateMatch ? dateMatch[1] : '',
80
- views: nums[0] || 0,
81
- likes: nums[1] || 0,
82
- collects: nums[2] || 0,
83
- comments: nums[3] || 0,
84
- url: href ? new URL(href, window.location.origin).toString() : '',
85
- });
86
- }
87
- });
88
-
89
- return results;
90
- })()
91
- `);
209
+ const notes = await fetchCreatorNotes(page, limit);
92
210
  if (!Array.isArray(notes) || notes.length === 0) {
93
211
  throw new Error('No notes found. Are you logged into creator.xiaohongshu.com?');
94
212
  }
@@ -0,0 +1 @@
1
+ import './creator-notes.js';
@@ -0,0 +1,191 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { getRegistry } from '../../registry.js';
3
+ import { parseCreatorNoteIdsFromHtml, parseCreatorNotesText } from './creator-notes.js';
4
+ import './creator-notes.js';
5
+ function createPageMock(evaluateResult, interceptedRequests = []) {
6
+ const evaluate = Array.isArray(evaluateResult)
7
+ ? vi.fn()
8
+ .mockResolvedValueOnce(evaluateResult[0])
9
+ .mockResolvedValue(evaluateResult[evaluateResult.length - 1])
10
+ : vi.fn().mockResolvedValue(evaluateResult);
11
+ const getInterceptedRequests = Array.isArray(interceptedRequests)
12
+ ? vi.fn().mockResolvedValue(interceptedRequests)
13
+ : vi.fn().mockResolvedValue([]);
14
+ return {
15
+ goto: vi.fn().mockResolvedValue(undefined),
16
+ evaluate,
17
+ snapshot: vi.fn().mockResolvedValue(undefined),
18
+ click: vi.fn().mockResolvedValue(undefined),
19
+ typeText: vi.fn().mockResolvedValue(undefined),
20
+ pressKey: vi.fn().mockResolvedValue(undefined),
21
+ wait: vi.fn().mockResolvedValue(undefined),
22
+ tabs: vi.fn().mockResolvedValue([]),
23
+ closeTab: vi.fn().mockResolvedValue(undefined),
24
+ newTab: vi.fn().mockResolvedValue(undefined),
25
+ selectTab: vi.fn().mockResolvedValue(undefined),
26
+ networkRequests: vi.fn().mockResolvedValue([]),
27
+ consoleMessages: vi.fn().mockResolvedValue([]),
28
+ scroll: vi.fn().mockResolvedValue(undefined),
29
+ autoScroll: vi.fn().mockResolvedValue(undefined),
30
+ installInterceptor: vi.fn().mockResolvedValue(undefined),
31
+ getInterceptedRequests,
32
+ getCookies: vi.fn().mockResolvedValue([]),
33
+ screenshot: vi.fn().mockResolvedValue(''),
34
+ };
35
+ }
36
+ describe('xiaohongshu creator-notes', () => {
37
+ it('parses creator note text blocks into rows', () => {
38
+ const bodyText = `笔记管理
39
+ 全部笔记(366)
40
+ 已发布
41
+ 神雕侠侣战力金字塔
42
+ 发布于 2025年12月04日 19:45
43
+ 148208
44
+ 324
45
+ 2279
46
+ 465
47
+ 32
48
+ 权限设置
49
+ 取消置顶
50
+ 编辑
51
+ 删除
52
+ 仅自己可见
53
+ 终于等到了!!!
54
+ 发布于 2026年03月18日 12:39
55
+ 10
56
+ 0
57
+ 0
58
+ 0
59
+ 0
60
+ 权限设置`;
61
+ expect(parseCreatorNotesText(bodyText)).toEqual([
62
+ {
63
+ id: '',
64
+ title: '神雕侠侣战力金字塔',
65
+ date: '2025年12月04日 19:45',
66
+ views: 148208,
67
+ likes: 2279,
68
+ collects: 465,
69
+ comments: 324,
70
+ url: '',
71
+ },
72
+ {
73
+ id: '',
74
+ title: '终于等到了!!!',
75
+ date: '2026年03月18日 12:39',
76
+ views: 10,
77
+ likes: 0,
78
+ collects: 0,
79
+ comments: 0,
80
+ url: '',
81
+ },
82
+ ]);
83
+ });
84
+ it('reads body text and returns ranked rows', async () => {
85
+ const cmd = getRegistry().get('xiaohongshu/creator-notes');
86
+ expect(cmd?.func).toBeTypeOf('function');
87
+ const page = createPageMock([
88
+ undefined,
89
+ {
90
+ text: `示例笔记
91
+ 发布于 2026年03月19日 12:00
92
+ 10
93
+ 2
94
+ 3
95
+ 4
96
+ 5
97
+ 权限设置`,
98
+ html: '&quot;noteId&quot;:&quot;69ba940500000000200384db&quot;',
99
+ },
100
+ ]);
101
+ const result = await cmd.func(page, { limit: 1 });
102
+ expect(page.evaluate.mock.calls.at(-1)?.[0]).toBe('() => ({ text: document.body.innerText, html: document.body.innerHTML })');
103
+ expect(result).toEqual([
104
+ {
105
+ rank: 1,
106
+ id: '69ba940500000000200384db',
107
+ title: '示例笔记',
108
+ date: '2026年03月19日 12:00',
109
+ views: 10,
110
+ likes: 3,
111
+ collects: 4,
112
+ comments: 2,
113
+ url: 'https://creator.xiaohongshu.com/statistics/note-detail?noteId=69ba940500000000200384db',
114
+ },
115
+ ]);
116
+ });
117
+ it('prefers note card dom data when the analyze api is unavailable', async () => {
118
+ const cmd = getRegistry().get('xiaohongshu/creator-notes');
119
+ expect(cmd?.func).toBeTypeOf('function');
120
+ const page = createPageMock([
121
+ undefined,
122
+ [
123
+ {
124
+ id: '693155fc000000000d03b42c',
125
+ title: '神雕侠侣战力金字塔',
126
+ date: '2025年12月04日 19:45',
127
+ metrics: [148284, 319, 2280, 466, 33],
128
+ },
129
+ ],
130
+ ]);
131
+ const result = await cmd.func(page, { limit: 1 });
132
+ expect(result).toEqual([
133
+ {
134
+ rank: 1,
135
+ id: '693155fc000000000d03b42c',
136
+ title: '神雕侠侣战力金字塔',
137
+ date: '2025年12月04日 19:45',
138
+ views: 148284,
139
+ likes: 2280,
140
+ collects: 466,
141
+ comments: 319,
142
+ url: 'https://creator.xiaohongshu.com/statistics/note-detail?noteId=693155fc000000000d03b42c',
143
+ },
144
+ ]);
145
+ });
146
+ it('prefers the creator analyze API and preserves note ids', async () => {
147
+ const cmd = getRegistry().get('xiaohongshu/creator-notes');
148
+ expect(cmd?.func).toBeTypeOf('function');
149
+ const page = createPageMock(undefined, [{
150
+ data: {
151
+ note_infos: [
152
+ {
153
+ id: '69ba940500000000200384db',
154
+ title: '一张图讲清 诡秘之主·耕种者途径',
155
+ post_time: new Date('2026-03-18T20:01:00+08:00').getTime(),
156
+ read_count: 521,
157
+ like_count: 18,
158
+ fav_count: 10,
159
+ comment_count: 7,
160
+ },
161
+ ],
162
+ },
163
+ }]);
164
+ const result = await cmd.func(page, { limit: 1 });
165
+ expect(page.installInterceptor.mock.calls[0][0]).toContain('/api/galaxy/creator/datacenter/note/analyze/list');
166
+ expect(result).toEqual([
167
+ {
168
+ rank: 1,
169
+ id: '69ba940500000000200384db',
170
+ title: '一张图讲清 诡秘之主·耕种者途径',
171
+ date: '2026年03月18日 20:01',
172
+ views: 521,
173
+ likes: 18,
174
+ collects: 10,
175
+ comments: 7,
176
+ url: 'https://creator.xiaohongshu.com/statistics/note-detail?noteId=69ba940500000000200384db',
177
+ },
178
+ ]);
179
+ });
180
+ it('extracts note ids from creator note-manager html', () => {
181
+ const html = `
182
+ <div>&quot;noteId&quot;:&quot;69ba940500000000200384db&quot;</div>
183
+ <div>&quot;noteId&quot;:&quot;69ba2c98000000001a026e0f&quot;</div>
184
+ <div>&quot;noteId&quot;:&quot;69ba940500000000200384db&quot;</div>
185
+ `;
186
+ expect(parseCreatorNoteIdsFromHtml(html)).toEqual([
187
+ '69ba940500000000200384db',
188
+ '69ba2c98000000001a026e0f',
189
+ ]);
190
+ });
191
+ });