@jackwener/opencli 1.3.3 → 1.4.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 (680) hide show
  1. package/.github/actions/setup-chrome/action.yml +5 -4
  2. package/.github/pull_request_template.md +3 -1
  3. package/.github/workflows/build-extension.yml +7 -1
  4. package/.github/workflows/ci.yml +46 -6
  5. package/.github/workflows/docs.yml +1 -1
  6. package/.github/workflows/e2e-headed.yml +36 -3
  7. package/.github/workflows/release.yml +1 -1
  8. package/.github/workflows/security.yml +0 -3
  9. package/CHANGELOG.md +78 -0
  10. package/CONTRIBUTING.md +6 -3
  11. package/PRIVACY.md +57 -0
  12. package/README.md +31 -4
  13. package/README.zh-CN.md +31 -4
  14. package/SKILL.md +107 -2
  15. package/TESTING.md +1 -0
  16. package/chatwise-opencli.ps1 +82 -0
  17. package/dist/analysis.d.ts +38 -0
  18. package/dist/analysis.js +166 -0
  19. package/dist/browser/cdp.d.ts +0 -4
  20. package/dist/browser/cdp.js +53 -41
  21. package/dist/browser/cdp.test.d.ts +1 -0
  22. package/dist/browser/cdp.test.js +52 -0
  23. package/dist/browser/dom-snapshot.d.ts +2 -2
  24. package/dist/browser/dom-snapshot.js +54 -1
  25. package/dist/browser/dom-snapshot.test.js +36 -0
  26. package/dist/browser/index.d.ts +2 -2
  27. package/dist/browser/index.js +1 -1
  28. package/dist/browser/mcp.d.ts +0 -2
  29. package/dist/browser/mcp.js +2 -3
  30. package/dist/browser/page.d.ts +4 -3
  31. package/dist/browser/page.js +34 -37
  32. package/dist/browser/stealth.d.ts +0 -2
  33. package/dist/browser/stealth.js +24 -9
  34. package/dist/browser.test.js +2 -2
  35. package/dist/build-manifest.js +15 -9
  36. package/dist/build-manifest.test.js +12 -0
  37. package/dist/cascade.js +4 -2
  38. package/dist/cli-manifest.json +1325 -256
  39. package/dist/cli.js +57 -29
  40. package/dist/clis/_shared/desktop-commands.d.ts +22 -0
  41. package/dist/clis/_shared/desktop-commands.js +108 -0
  42. package/dist/clis/antigravity/serve.js +5 -2
  43. package/dist/clis/apple-podcasts/search.js +2 -1
  44. package/dist/clis/arxiv/search.js +3 -3
  45. package/dist/clis/bbc/news.js +0 -1
  46. package/dist/clis/bilibili/dynamic.test.d.ts +1 -0
  47. package/dist/clis/bilibili/dynamic.test.js +68 -0
  48. package/dist/clis/bilibili/favorite.js +4 -2
  49. package/dist/clis/bilibili/following.js +3 -2
  50. package/dist/clis/bilibili/subtitle.js +8 -7
  51. package/dist/clis/bilibili/utils.js +2 -2
  52. package/dist/clis/boss/batchgreet.js +1 -1
  53. package/dist/clis/boss/chatlist.js +1 -1
  54. package/dist/clis/boss/chatmsg.js +1 -1
  55. package/dist/clis/boss/detail.js +1 -1
  56. package/dist/clis/boss/exchange.js +1 -1
  57. package/dist/clis/boss/greet.js +1 -1
  58. package/dist/clis/boss/invite.js +1 -1
  59. package/dist/clis/boss/joblist.js +1 -1
  60. package/dist/clis/boss/mark.js +4 -3
  61. package/dist/clis/boss/recommend.js +1 -1
  62. package/dist/clis/boss/resume.js +1 -1
  63. package/dist/clis/boss/search.js +1 -1
  64. package/dist/clis/boss/send.js +5 -4
  65. package/dist/clis/boss/stats.js +1 -1
  66. package/dist/clis/chatgpt/ask.js +4 -0
  67. package/dist/clis/chatgpt/new.js +5 -1
  68. package/dist/clis/chatgpt/read.js +5 -1
  69. package/dist/clis/chatgpt/send.js +2 -1
  70. package/dist/clis/chatgpt/status.js +5 -1
  71. package/dist/clis/chatwise/ask.js +8 -2
  72. package/dist/clis/chatwise/export.js +2 -0
  73. package/dist/clis/chatwise/history.js +2 -0
  74. package/dist/clis/chatwise/model.js +8 -3
  75. package/dist/clis/chatwise/new.js +3 -18
  76. package/dist/clis/chatwise/read.js +2 -0
  77. package/dist/clis/chatwise/screenshot.js +3 -27
  78. package/dist/clis/chatwise/send.js +8 -2
  79. package/dist/clis/chatwise/shared.d.ts +2 -0
  80. package/dist/clis/chatwise/shared.js +6 -0
  81. package/dist/clis/chatwise/status.js +3 -22
  82. package/dist/clis/codex/ask.js +6 -2
  83. package/dist/clis/codex/dump.js +2 -25
  84. package/dist/clis/codex/new.js +2 -25
  85. package/dist/clis/codex/screenshot.js +2 -27
  86. package/dist/clis/codex/send.js +6 -4
  87. package/dist/clis/codex/status.js +2 -22
  88. package/dist/clis/ctrip/search.js +0 -1
  89. package/dist/clis/cursor/ask.js +2 -1
  90. package/dist/clis/cursor/composer.js +2 -1
  91. package/dist/clis/cursor/dump.js +2 -25
  92. package/dist/clis/cursor/new.js +2 -18
  93. package/dist/clis/cursor/read.js +2 -1
  94. package/dist/clis/cursor/screenshot.js +1 -30
  95. package/dist/clis/cursor/send.js +2 -1
  96. package/dist/clis/cursor/status.js +2 -21
  97. package/dist/clis/dictionary/examples.yaml +25 -0
  98. package/dist/clis/dictionary/search.yaml +27 -0
  99. package/dist/clis/dictionary/synonyms.yaml +25 -0
  100. package/dist/clis/douban/book-hot.js +1 -1
  101. package/dist/clis/douban/movie-hot.js +1 -1
  102. package/dist/clis/douban/search.js +1 -1
  103. package/dist/clis/douban/utils.d.ts +4 -1
  104. package/dist/clis/douban/utils.js +156 -1
  105. package/dist/clis/doubao/ask.js +1 -1
  106. package/dist/clis/doubao/new.js +1 -1
  107. package/dist/clis/doubao/read.js +1 -1
  108. package/dist/clis/doubao/send.js +1 -1
  109. package/dist/clis/doubao/status.js +1 -1
  110. package/dist/clis/doubao-app/ask.js +1 -1
  111. package/dist/clis/doubao-app/new.js +1 -1
  112. package/dist/clis/doubao-app/read.js +1 -1
  113. package/dist/clis/doubao-app/send.js +1 -1
  114. package/dist/clis/douyin/_shared/browser-fetch.d.ts +10 -0
  115. package/dist/clis/douyin/_shared/browser-fetch.js +30 -0
  116. package/dist/clis/douyin/_shared/browser-fetch.test.d.ts +1 -0
  117. package/dist/clis/douyin/_shared/browser-fetch.test.js +31 -0
  118. package/dist/clis/douyin/_shared/creation-id.d.ts +1 -0
  119. package/dist/clis/douyin/_shared/creation-id.js +5 -0
  120. package/dist/clis/douyin/_shared/creation-id.test.d.ts +1 -0
  121. package/dist/clis/douyin/_shared/creation-id.test.js +22 -0
  122. package/dist/clis/douyin/_shared/imagex-upload.d.ts +20 -0
  123. package/dist/clis/douyin/_shared/imagex-upload.js +53 -0
  124. package/dist/clis/douyin/_shared/imagex-upload.test.d.ts +1 -0
  125. package/dist/clis/douyin/_shared/imagex-upload.test.js +87 -0
  126. package/dist/clis/douyin/_shared/sts2.d.ts +8 -0
  127. package/dist/clis/douyin/_shared/sts2.js +15 -0
  128. package/dist/clis/douyin/_shared/text-extra.d.ts +18 -0
  129. package/dist/clis/douyin/_shared/text-extra.js +15 -0
  130. package/dist/clis/douyin/_shared/text-extra.test.d.ts +1 -0
  131. package/dist/clis/douyin/_shared/text-extra.test.js +37 -0
  132. package/dist/clis/douyin/_shared/timing.d.ts +2 -0
  133. package/dist/clis/douyin/_shared/timing.js +22 -0
  134. package/dist/clis/douyin/_shared/timing.test.d.ts +1 -0
  135. package/dist/clis/douyin/_shared/timing.test.js +28 -0
  136. package/dist/clis/douyin/_shared/tos-upload-short-read.test.d.ts +11 -0
  137. package/dist/clis/douyin/_shared/tos-upload-short-read.test.js +83 -0
  138. package/dist/clis/douyin/_shared/tos-upload.d.ts +53 -0
  139. package/dist/clis/douyin/_shared/tos-upload.js +295 -0
  140. package/dist/clis/douyin/_shared/tos-upload.test.d.ts +1 -0
  141. package/dist/clis/douyin/_shared/tos-upload.test.js +229 -0
  142. package/dist/clis/douyin/_shared/transcode.d.ts +27 -0
  143. package/dist/clis/douyin/_shared/transcode.js +45 -0
  144. package/dist/clis/douyin/_shared/transcode.test.d.ts +1 -0
  145. package/dist/clis/douyin/_shared/transcode.test.js +93 -0
  146. package/dist/clis/douyin/_shared/types.d.ts +26 -0
  147. package/dist/clis/douyin/_shared/types.js +1 -0
  148. package/dist/clis/douyin/activities.d.ts +1 -0
  149. package/dist/clis/douyin/activities.js +20 -0
  150. package/dist/clis/douyin/activities.test.d.ts +1 -0
  151. package/dist/clis/douyin/activities.test.js +22 -0
  152. package/dist/clis/douyin/collections.d.ts +1 -0
  153. package/dist/clis/douyin/collections.js +22 -0
  154. package/dist/clis/douyin/collections.test.d.ts +1 -0
  155. package/dist/clis/douyin/collections.test.js +23 -0
  156. package/dist/clis/douyin/delete.d.ts +1 -0
  157. package/dist/clis/douyin/delete.js +18 -0
  158. package/dist/clis/douyin/delete.test.d.ts +1 -0
  159. package/dist/clis/douyin/delete.test.js +11 -0
  160. package/dist/clis/douyin/draft.d.ts +14 -0
  161. package/dist/clis/douyin/draft.js +237 -0
  162. package/dist/clis/douyin/draft.test.d.ts +1 -0
  163. package/dist/clis/douyin/draft.test.js +11 -0
  164. package/dist/clis/douyin/drafts.d.ts +1 -0
  165. package/dist/clis/douyin/drafts.js +23 -0
  166. package/dist/clis/douyin/drafts.test.d.ts +1 -0
  167. package/dist/clis/douyin/drafts.test.js +11 -0
  168. package/dist/clis/douyin/hashtag.d.ts +1 -0
  169. package/dist/clis/douyin/hashtag.js +45 -0
  170. package/dist/clis/douyin/hashtag.test.d.ts +1 -0
  171. package/dist/clis/douyin/hashtag.test.js +25 -0
  172. package/dist/clis/douyin/location.d.ts +1 -0
  173. package/dist/clis/douyin/location.js +24 -0
  174. package/dist/clis/douyin/location.test.d.ts +1 -0
  175. package/dist/clis/douyin/location.test.js +23 -0
  176. package/dist/clis/douyin/profile.d.ts +1 -0
  177. package/dist/clis/douyin/profile.js +28 -0
  178. package/dist/clis/douyin/profile.test.d.ts +1 -0
  179. package/dist/clis/douyin/profile.test.js +11 -0
  180. package/dist/clis/douyin/publish.d.ts +14 -0
  181. package/dist/clis/douyin/publish.js +288 -0
  182. package/dist/clis/douyin/publish.test.d.ts +1 -0
  183. package/dist/clis/douyin/publish.test.js +38 -0
  184. package/dist/clis/douyin/stats.d.ts +1 -0
  185. package/dist/clis/douyin/stats.js +27 -0
  186. package/dist/clis/douyin/stats.test.d.ts +1 -0
  187. package/dist/clis/douyin/stats.test.js +22 -0
  188. package/dist/clis/douyin/update.d.ts +1 -0
  189. package/dist/clis/douyin/update.js +31 -0
  190. package/dist/clis/douyin/update.test.d.ts +1 -0
  191. package/dist/clis/douyin/update.test.js +11 -0
  192. package/dist/clis/douyin/videos.d.ts +1 -0
  193. package/dist/clis/douyin/videos.js +34 -0
  194. package/dist/clis/douyin/videos.test.d.ts +1 -0
  195. package/dist/clis/douyin/videos.test.js +11 -0
  196. package/dist/clis/grok/ask.d.ts +4 -0
  197. package/dist/clis/grok/ask.js +28 -10
  198. package/dist/clis/grok/ask.test.js +18 -0
  199. package/dist/clis/hackernews/search.yaml +1 -1
  200. package/dist/clis/instagram/search.yaml +2 -1
  201. package/dist/clis/jd/item.d.ts +1 -0
  202. package/dist/clis/jd/item.js +96 -0
  203. package/dist/clis/jd/item.test.d.ts +1 -0
  204. package/dist/clis/jd/item.test.js +28 -0
  205. package/dist/clis/jike/feed.js +1 -1
  206. package/dist/clis/jike/search.js +1 -1
  207. package/dist/clis/linkedin/search.js +5 -4
  208. package/dist/clis/linkedin/timeline.d.ts +21 -0
  209. package/dist/clis/linkedin/timeline.js +503 -0
  210. package/dist/clis/linkedin/timeline.test.d.ts +1 -0
  211. package/dist/clis/linkedin/timeline.test.js +81 -0
  212. package/dist/clis/linux-do/search.yaml +3 -1
  213. package/dist/clis/medium/feed.js +1 -1
  214. package/dist/clis/medium/search.js +2 -2
  215. package/dist/clis/medium/user.js +1 -1
  216. package/dist/clis/medium/{shared.js → utils.js} +2 -1
  217. package/dist/clis/pixiv/detail.yaml +49 -0
  218. package/dist/clis/pixiv/download.d.ts +7 -0
  219. package/dist/clis/pixiv/download.js +78 -0
  220. package/dist/clis/pixiv/download.test.d.ts +1 -0
  221. package/dist/clis/pixiv/download.test.js +87 -0
  222. package/dist/clis/pixiv/illusts.d.ts +8 -0
  223. package/dist/clis/pixiv/illusts.js +65 -0
  224. package/dist/clis/pixiv/illusts.test.d.ts +1 -0
  225. package/dist/clis/pixiv/illusts.test.js +99 -0
  226. package/dist/clis/pixiv/ranking.yaml +53 -0
  227. package/dist/clis/pixiv/search.d.ts +6 -0
  228. package/dist/clis/pixiv/search.js +43 -0
  229. package/dist/clis/pixiv/search.test.d.ts +1 -0
  230. package/dist/clis/pixiv/search.test.js +83 -0
  231. package/dist/clis/pixiv/test-utils.d.ts +12 -0
  232. package/dist/clis/pixiv/test-utils.js +23 -0
  233. package/dist/clis/pixiv/user.yaml +46 -0
  234. package/dist/clis/pixiv/utils.d.ts +27 -0
  235. package/dist/clis/pixiv/utils.js +49 -0
  236. package/dist/clis/reddit/comment.js +2 -1
  237. package/dist/clis/reddit/read.js +4 -3
  238. package/dist/clis/reddit/read.test.d.ts +1 -0
  239. package/dist/clis/reddit/read.test.js +28 -0
  240. package/dist/clis/reddit/save.js +2 -1
  241. package/dist/clis/reddit/saved.js +7 -3
  242. package/dist/clis/reddit/subscribe.js +2 -1
  243. package/dist/clis/reddit/upvote.js +2 -1
  244. package/dist/clis/reddit/upvoted.js +7 -3
  245. package/dist/clis/reuters/search.js +0 -1
  246. package/dist/clis/sinablog/article.js +1 -1
  247. package/dist/clis/sinablog/hot.js +1 -1
  248. package/dist/clis/sinablog/user.js +1 -1
  249. package/dist/clis/substack/feed.js +1 -1
  250. package/dist/clis/substack/publication.js +1 -1
  251. package/dist/clis/substack/search.js +3 -2
  252. package/dist/clis/substack/{shared.js → utils.js} +3 -2
  253. package/dist/clis/tiktok/search.yaml +2 -1
  254. package/dist/clis/twitter/accept.js +2 -1
  255. package/dist/clis/twitter/article.js +4 -1
  256. package/dist/clis/twitter/block.js +2 -1
  257. package/dist/clis/twitter/bookmark.js +2 -1
  258. package/dist/clis/twitter/bookmarks.js +3 -2
  259. package/dist/clis/twitter/delete.js +2 -1
  260. package/dist/clis/twitter/follow.js +2 -1
  261. package/dist/clis/twitter/followers.js +3 -2
  262. package/dist/clis/twitter/following.js +3 -2
  263. package/dist/clis/twitter/hide-reply.js +2 -1
  264. package/dist/clis/twitter/like.js +2 -1
  265. package/dist/clis/twitter/notifications.js +2 -1
  266. package/dist/clis/twitter/post.js +2 -1
  267. package/dist/clis/twitter/profile.js +5 -2
  268. package/dist/clis/twitter/reply-dm.js +2 -1
  269. package/dist/clis/twitter/reply.js +2 -1
  270. package/dist/clis/twitter/search.js +32 -13
  271. package/dist/clis/twitter/search.test.d.ts +1 -0
  272. package/dist/clis/twitter/search.test.js +156 -0
  273. package/dist/clis/twitter/thread.js +2 -2
  274. package/dist/clis/twitter/timeline.js +3 -2
  275. package/dist/clis/twitter/trending.js +3 -2
  276. package/dist/clis/twitter/unblock.js +2 -1
  277. package/dist/clis/twitter/unbookmark.js +2 -1
  278. package/dist/clis/twitter/unfollow.js +2 -1
  279. package/dist/clis/v2ex/daily.js +3 -2
  280. package/dist/clis/v2ex/me.js +3 -2
  281. package/dist/clis/v2ex/notifications.js +4 -4
  282. package/dist/clis/web/read.d.ts +16 -0
  283. package/dist/clis/web/read.js +202 -0
  284. package/dist/clis/weibo/comments.d.ts +1 -0
  285. package/dist/clis/weibo/comments.js +53 -0
  286. package/dist/clis/weibo/feed.d.ts +1 -0
  287. package/dist/clis/weibo/feed.js +56 -0
  288. package/dist/clis/weibo/hot.js +0 -1
  289. package/dist/clis/weibo/me.d.ts +1 -0
  290. package/dist/clis/weibo/me.js +76 -0
  291. package/dist/clis/weibo/post.d.ts +1 -0
  292. package/dist/clis/weibo/post.js +75 -0
  293. package/dist/clis/weibo/user.d.ts +1 -0
  294. package/dist/clis/weibo/user.js +63 -0
  295. package/dist/clis/weibo/utils.d.ts +6 -0
  296. package/dist/clis/weibo/utils.js +30 -0
  297. package/dist/clis/weread/search.js +3 -2
  298. package/dist/clis/xueqiu/danjuan-utils.d.ts +55 -0
  299. package/dist/clis/xueqiu/danjuan-utils.js +126 -0
  300. package/dist/clis/xueqiu/danjuan-utils.test.d.ts +1 -0
  301. package/dist/clis/xueqiu/danjuan-utils.test.js +41 -0
  302. package/dist/clis/xueqiu/fund-holdings.d.ts +1 -0
  303. package/dist/clis/xueqiu/fund-holdings.js +28 -0
  304. package/dist/clis/xueqiu/fund-snapshot.d.ts +1 -0
  305. package/dist/clis/xueqiu/fund-snapshot.js +25 -0
  306. package/dist/clis/xueqiu/search.yaml +2 -1
  307. package/dist/clis/yahoo-finance/quote.js +0 -1
  308. package/dist/clis/youtube/channel.d.ts +1 -0
  309. package/dist/clis/youtube/channel.js +150 -0
  310. package/dist/clis/youtube/comments.d.ts +1 -0
  311. package/dist/clis/youtube/comments.js +95 -0
  312. package/dist/clis/youtube/search.js +0 -1
  313. package/dist/clis/youtube/transcript.js +5 -4
  314. package/dist/clis/youtube/video.js +3 -2
  315. package/dist/clis/zhihu/search.yaml +2 -1
  316. package/dist/daemon.js +7 -3
  317. package/dist/discovery.js +11 -10
  318. package/dist/doctor.js +2 -1
  319. package/dist/download/index.d.ts +4 -12
  320. package/dist/download/index.js +33 -12
  321. package/dist/download/index.test.js +79 -2
  322. package/dist/download/media-download.js +4 -2
  323. package/dist/engine.test.js +76 -4
  324. package/dist/execution.d.ts +1 -9
  325. package/dist/execution.js +56 -46
  326. package/dist/explore.js +12 -111
  327. package/dist/external-clis.yaml +0 -25
  328. package/dist/external.js +7 -5
  329. package/dist/external.test.js +4 -0
  330. package/dist/generate.d.ts +0 -9
  331. package/dist/generate.js +4 -20
  332. package/dist/hooks.d.ts +46 -0
  333. package/dist/hooks.js +56 -0
  334. package/dist/hooks.test.d.ts +4 -0
  335. package/dist/hooks.test.js +92 -0
  336. package/dist/interceptor.js +70 -23
  337. package/dist/main.js +2 -0
  338. package/dist/output.js +12 -6
  339. package/dist/pipeline/executor.js +1 -1
  340. package/dist/pipeline/steps/browser.js +1 -3
  341. package/dist/pipeline/steps/download.js +42 -26
  342. package/dist/pipeline/steps/download.test.d.ts +1 -0
  343. package/dist/pipeline/steps/download.test.js +101 -0
  344. package/dist/pipeline/steps/fetch.js +40 -22
  345. package/dist/pipeline/steps/fetch.test.d.ts +1 -0
  346. package/dist/pipeline/steps/fetch.test.js +123 -0
  347. package/dist/pipeline/steps/transform.js +2 -6
  348. package/dist/pipeline/template.js +66 -52
  349. package/dist/pipeline/template.test.js +28 -0
  350. package/dist/pipeline/transform.test.js +18 -0
  351. package/dist/plugin.d.ts +40 -1
  352. package/dist/plugin.js +214 -17
  353. package/dist/plugin.test.d.ts +1 -1
  354. package/dist/plugin.test.js +219 -3
  355. package/dist/record.js +6 -98
  356. package/dist/registry-api.d.ts +2 -0
  357. package/dist/registry-api.js +1 -0
  358. package/dist/registry.d.ts +5 -2
  359. package/dist/registry.js +1 -2
  360. package/dist/runtime.d.ts +0 -1
  361. package/dist/runtime.js +14 -4
  362. package/dist/snapshotFormatter.d.ts +7 -14
  363. package/dist/snapshotFormatter.js +38 -78
  364. package/dist/utils.d.ts +9 -0
  365. package/dist/utils.js +29 -0
  366. package/dist/validate.js +3 -5
  367. package/dist/weread-search-regression.test.d.ts +1 -0
  368. package/dist/weread-search-regression.test.js +39 -0
  369. package/dist/yaml-schema.d.ts +26 -0
  370. package/dist/yaml-schema.js +5 -0
  371. package/docs/.vitepress/config.mts +16 -0
  372. package/docs/adapters/browser/dictionary.md +27 -0
  373. package/docs/adapters/browser/douyin.md +75 -0
  374. package/docs/adapters/browser/jd.md +27 -0
  375. package/docs/adapters/browser/linkedin.md +6 -0
  376. package/docs/adapters/browser/pixiv.md +92 -0
  377. package/docs/adapters/browser/twitter.md +6 -0
  378. package/docs/adapters/browser/web.md +30 -0
  379. package/docs/adapters/browser/xueqiu.md +27 -9
  380. package/docs/adapters/index.md +9 -2
  381. package/docs/comparison.md +125 -0
  382. package/docs/developer/contributing.md +21 -2
  383. package/docs/developer/testing.md +14 -8
  384. package/docs/developer/ts-adapter.md +18 -0
  385. package/docs/developer/yaml-adapter.md +16 -0
  386. package/docs/guide/plugins.md +10 -0
  387. package/docs/zh/guide/plugins.md +10 -0
  388. package/extension/dist/background.js +100 -35
  389. package/extension/manifest.json +6 -2
  390. package/extension/package.json +1 -1
  391. package/extension/popup.html +84 -0
  392. package/extension/popup.js +25 -0
  393. package/extension/src/background.test.ts +46 -1
  394. package/extension/src/background.ts +128 -34
  395. package/extension/src/cdp.ts +9 -9
  396. package/package.json +3 -2
  397. package/scripts/check-doc-coverage.sh +2 -0
  398. package/src/analysis.ts +170 -0
  399. package/src/browser/cdp.test.ts +66 -0
  400. package/src/browser/cdp.ts +59 -44
  401. package/src/browser/dom-snapshot.test.ts +42 -0
  402. package/src/browser/dom-snapshot.ts +56 -3
  403. package/src/browser/index.ts +2 -2
  404. package/src/browser/mcp.ts +2 -4
  405. package/src/browser/page.ts +34 -37
  406. package/src/browser/stealth.ts +24 -10
  407. package/src/browser.test.ts +2 -2
  408. package/src/build-manifest.test.ts +14 -0
  409. package/src/build-manifest.ts +13 -31
  410. package/src/cascade.ts +5 -3
  411. package/src/cli.ts +66 -34
  412. package/src/clis/_shared/desktop-commands.ts +121 -0
  413. package/src/clis/antigravity/serve.ts +6 -3
  414. package/src/clis/apple-podcasts/search.ts +2 -1
  415. package/src/clis/arxiv/search.ts +3 -3
  416. package/src/clis/bbc/news.ts +0 -1
  417. package/src/clis/bilibili/dynamic.test.ts +79 -0
  418. package/src/clis/bilibili/favorite.ts +5 -2
  419. package/src/clis/bilibili/following.ts +3 -2
  420. package/src/clis/bilibili/subtitle.ts +8 -7
  421. package/src/clis/bilibili/utils.ts +2 -2
  422. package/src/clis/boss/batchgreet.ts +1 -1
  423. package/src/clis/boss/chatlist.ts +1 -1
  424. package/src/clis/boss/chatmsg.ts +1 -1
  425. package/src/clis/boss/detail.ts +1 -1
  426. package/src/clis/boss/exchange.ts +1 -1
  427. package/src/clis/boss/greet.ts +1 -1
  428. package/src/clis/boss/invite.ts +1 -1
  429. package/src/clis/boss/joblist.ts +1 -1
  430. package/src/clis/boss/mark.ts +4 -3
  431. package/src/clis/boss/recommend.ts +1 -1
  432. package/src/clis/boss/resume.ts +1 -1
  433. package/src/clis/boss/search.ts +1 -1
  434. package/src/clis/boss/send.ts +5 -4
  435. package/src/clis/boss/stats.ts +1 -1
  436. package/src/clis/chatgpt/ask.ts +5 -0
  437. package/src/clis/chatgpt/new.ts +7 -2
  438. package/src/clis/chatgpt/read.ts +7 -2
  439. package/src/clis/chatgpt/send.ts +3 -2
  440. package/src/clis/chatgpt/status.ts +6 -1
  441. package/src/clis/chatwise/ask.ts +7 -2
  442. package/src/clis/chatwise/export.ts +2 -0
  443. package/src/clis/chatwise/history.ts +2 -0
  444. package/src/clis/chatwise/model.ts +7 -3
  445. package/src/clis/chatwise/new.ts +3 -20
  446. package/src/clis/chatwise/read.ts +2 -0
  447. package/src/clis/chatwise/screenshot.ts +3 -32
  448. package/src/clis/chatwise/send.ts +7 -2
  449. package/src/clis/chatwise/shared.ts +8 -0
  450. package/src/clis/chatwise/status.ts +3 -24
  451. package/src/clis/codex/ask.ts +5 -2
  452. package/src/clis/codex/dump.ts +2 -27
  453. package/src/clis/codex/new.ts +2 -28
  454. package/src/clis/codex/screenshot.ts +2 -32
  455. package/src/clis/codex/send.ts +5 -4
  456. package/src/clis/codex/status.ts +2 -24
  457. package/src/clis/ctrip/search.ts +0 -1
  458. package/src/clis/cursor/ask.ts +2 -1
  459. package/src/clis/cursor/composer.ts +2 -1
  460. package/src/clis/cursor/dump.ts +2 -27
  461. package/src/clis/cursor/new.ts +2 -20
  462. package/src/clis/cursor/read.ts +2 -1
  463. package/src/clis/cursor/screenshot.ts +1 -36
  464. package/src/clis/cursor/send.ts +2 -1
  465. package/src/clis/cursor/status.ts +2 -22
  466. package/src/clis/dictionary/examples.yaml +25 -0
  467. package/src/clis/dictionary/search.yaml +27 -0
  468. package/src/clis/dictionary/synonyms.yaml +25 -0
  469. package/src/clis/douban/book-hot.ts +1 -1
  470. package/src/clis/douban/movie-hot.ts +1 -1
  471. package/src/clis/douban/search.ts +1 -1
  472. package/src/clis/douban/utils.ts +165 -1
  473. package/src/clis/doubao/ask.ts +1 -1
  474. package/src/clis/doubao/new.ts +1 -1
  475. package/src/clis/doubao/read.ts +1 -1
  476. package/src/clis/doubao/send.ts +1 -1
  477. package/src/clis/doubao/status.ts +1 -1
  478. package/src/clis/doubao-app/ask.ts +1 -1
  479. package/src/clis/doubao-app/new.ts +1 -1
  480. package/src/clis/doubao-app/read.ts +1 -1
  481. package/src/clis/doubao-app/send.ts +1 -1
  482. package/src/clis/douyin/_shared/browser-fetch.test.ts +38 -0
  483. package/src/clis/douyin/_shared/browser-fetch.ts +45 -0
  484. package/src/clis/douyin/_shared/creation-id.test.ts +26 -0
  485. package/src/clis/douyin/_shared/creation-id.ts +8 -0
  486. package/src/clis/douyin/_shared/imagex-upload.test.ts +113 -0
  487. package/src/clis/douyin/_shared/imagex-upload.ts +76 -0
  488. package/src/clis/douyin/_shared/sts2.ts +20 -0
  489. package/src/clis/douyin/_shared/text-extra.test.ts +42 -0
  490. package/src/clis/douyin/_shared/text-extra.ts +33 -0
  491. package/src/clis/douyin/_shared/timing.test.ts +38 -0
  492. package/src/clis/douyin/_shared/timing.ts +22 -0
  493. package/src/clis/douyin/_shared/tos-upload-short-read.test.ts +102 -0
  494. package/src/clis/douyin/_shared/tos-upload.test.ts +281 -0
  495. package/src/clis/douyin/_shared/tos-upload.ts +444 -0
  496. package/src/clis/douyin/_shared/transcode.test.ts +117 -0
  497. package/src/clis/douyin/_shared/transcode.ts +78 -0
  498. package/src/clis/douyin/_shared/types.ts +29 -0
  499. package/src/clis/douyin/activities.test.ts +25 -0
  500. package/src/clis/douyin/activities.ts +23 -0
  501. package/src/clis/douyin/collections.test.ts +26 -0
  502. package/src/clis/douyin/collections.ts +25 -0
  503. package/src/clis/douyin/delete.test.ts +12 -0
  504. package/src/clis/douyin/delete.ts +20 -0
  505. package/src/clis/douyin/draft.test.ts +12 -0
  506. package/src/clis/douyin/draft.ts +282 -0
  507. package/src/clis/douyin/drafts.test.ts +12 -0
  508. package/src/clis/douyin/drafts.ts +27 -0
  509. package/src/clis/douyin/hashtag.test.ts +28 -0
  510. package/src/clis/douyin/hashtag.ts +56 -0
  511. package/src/clis/douyin/location.test.ts +26 -0
  512. package/src/clis/douyin/location.ts +27 -0
  513. package/src/clis/douyin/profile.test.ts +12 -0
  514. package/src/clis/douyin/profile.ts +37 -0
  515. package/src/clis/douyin/publish.test.ts +45 -0
  516. package/src/clis/douyin/publish.ts +340 -0
  517. package/src/clis/douyin/stats.test.ts +25 -0
  518. package/src/clis/douyin/stats.ts +30 -0
  519. package/src/clis/douyin/update.test.ts +12 -0
  520. package/src/clis/douyin/update.ts +43 -0
  521. package/src/clis/douyin/videos.test.ts +12 -0
  522. package/src/clis/douyin/videos.ts +49 -0
  523. package/src/clis/grok/ask.test.ts +25 -0
  524. package/src/clis/grok/ask.ts +25 -12
  525. package/src/clis/hackernews/search.yaml +1 -1
  526. package/src/clis/instagram/search.yaml +2 -1
  527. package/src/clis/jd/item.test.ts +35 -0
  528. package/src/clis/jd/item.ts +101 -0
  529. package/src/clis/jike/feed.ts +1 -1
  530. package/src/clis/jike/search.ts +1 -1
  531. package/src/clis/linkedin/search.ts +5 -4
  532. package/src/clis/linkedin/timeline.test.ts +99 -0
  533. package/src/clis/linkedin/timeline.ts +532 -0
  534. package/src/clis/linux-do/search.yaml +3 -1
  535. package/src/clis/medium/feed.ts +1 -1
  536. package/src/clis/medium/search.ts +2 -2
  537. package/src/clis/medium/user.ts +1 -1
  538. package/src/clis/medium/{shared.ts → utils.ts} +2 -1
  539. package/src/clis/pixiv/detail.yaml +49 -0
  540. package/src/clis/pixiv/download.test.ts +114 -0
  541. package/src/clis/pixiv/download.ts +91 -0
  542. package/src/clis/pixiv/illusts.test.ts +115 -0
  543. package/src/clis/pixiv/illusts.ts +78 -0
  544. package/src/clis/pixiv/ranking.yaml +53 -0
  545. package/src/clis/pixiv/search.test.ts +97 -0
  546. package/src/clis/pixiv/search.ts +53 -0
  547. package/src/clis/pixiv/test-utils.ts +29 -0
  548. package/src/clis/pixiv/user.yaml +46 -0
  549. package/src/clis/pixiv/utils.ts +62 -0
  550. package/src/clis/reddit/comment.ts +2 -1
  551. package/src/clis/reddit/read.test.ts +34 -0
  552. package/src/clis/reddit/read.ts +4 -3
  553. package/src/clis/reddit/save.ts +2 -1
  554. package/src/clis/reddit/saved.ts +6 -2
  555. package/src/clis/reddit/subscribe.ts +2 -1
  556. package/src/clis/reddit/upvote.ts +2 -1
  557. package/src/clis/reddit/upvoted.ts +6 -2
  558. package/src/clis/reuters/search.ts +0 -1
  559. package/src/clis/sinablog/article.ts +1 -1
  560. package/src/clis/sinablog/hot.ts +1 -1
  561. package/src/clis/sinablog/user.ts +1 -1
  562. package/src/clis/substack/feed.ts +1 -1
  563. package/src/clis/substack/publication.ts +1 -1
  564. package/src/clis/substack/search.ts +3 -2
  565. package/src/clis/substack/{shared.ts → utils.ts} +3 -2
  566. package/src/clis/tiktok/search.yaml +2 -1
  567. package/src/clis/twitter/accept.ts +2 -1
  568. package/src/clis/twitter/article.ts +3 -1
  569. package/src/clis/twitter/block.ts +2 -1
  570. package/src/clis/twitter/bookmark.ts +2 -1
  571. package/src/clis/twitter/bookmarks.ts +3 -2
  572. package/src/clis/twitter/delete.ts +2 -1
  573. package/src/clis/twitter/follow.ts +2 -1
  574. package/src/clis/twitter/followers.ts +3 -2
  575. package/src/clis/twitter/following.ts +3 -2
  576. package/src/clis/twitter/hide-reply.ts +2 -1
  577. package/src/clis/twitter/like.ts +2 -1
  578. package/src/clis/twitter/notifications.ts +2 -1
  579. package/src/clis/twitter/post.ts +2 -1
  580. package/src/clis/twitter/profile.ts +4 -2
  581. package/src/clis/twitter/reply-dm.ts +2 -1
  582. package/src/clis/twitter/reply.ts +2 -1
  583. package/src/clis/twitter/search.test.ts +180 -0
  584. package/src/clis/twitter/search.ts +40 -14
  585. package/src/clis/twitter/thread.ts +2 -2
  586. package/src/clis/twitter/timeline.ts +3 -2
  587. package/src/clis/twitter/trending.ts +3 -2
  588. package/src/clis/twitter/unblock.ts +2 -1
  589. package/src/clis/twitter/unbookmark.ts +2 -1
  590. package/src/clis/twitter/unfollow.ts +2 -1
  591. package/src/clis/v2ex/daily.ts +3 -2
  592. package/src/clis/v2ex/me.ts +3 -2
  593. package/src/clis/v2ex/notifications.ts +3 -4
  594. package/src/clis/web/read.ts +210 -0
  595. package/src/clis/weibo/comments.ts +54 -0
  596. package/src/clis/weibo/feed.ts +57 -0
  597. package/src/clis/weibo/hot.ts +0 -1
  598. package/src/clis/weibo/me.ts +77 -0
  599. package/src/clis/weibo/post.ts +77 -0
  600. package/src/clis/weibo/user.ts +64 -0
  601. package/src/clis/weibo/utils.ts +32 -0
  602. package/src/clis/weread/search.ts +3 -2
  603. package/src/clis/xueqiu/danjuan-utils.test.ts +49 -0
  604. package/src/clis/xueqiu/danjuan-utils.ts +176 -0
  605. package/src/clis/xueqiu/fund-holdings.ts +32 -0
  606. package/src/clis/xueqiu/fund-snapshot.ts +27 -0
  607. package/src/clis/xueqiu/search.yaml +2 -1
  608. package/src/clis/yahoo-finance/quote.ts +0 -1
  609. package/src/clis/youtube/channel.ts +155 -0
  610. package/src/clis/youtube/comments.ts +97 -0
  611. package/src/clis/youtube/search.ts +0 -1
  612. package/src/clis/youtube/transcript.ts +5 -4
  613. package/src/clis/youtube/video.ts +3 -2
  614. package/src/clis/zhihu/search.yaml +2 -1
  615. package/src/daemon.ts +5 -4
  616. package/src/discovery.ts +12 -34
  617. package/src/doctor.ts +3 -2
  618. package/src/download/index.test.ts +93 -2
  619. package/src/download/index.ts +44 -23
  620. package/src/download/media-download.ts +5 -3
  621. package/src/engine.test.ts +84 -3
  622. package/src/execution.ts +62 -46
  623. package/src/explore.ts +21 -90
  624. package/src/external-clis.yaml +0 -25
  625. package/src/external.test.ts +9 -0
  626. package/src/external.ts +12 -10
  627. package/src/generate.ts +4 -41
  628. package/src/hooks.test.ts +126 -0
  629. package/src/hooks.ts +90 -0
  630. package/src/interceptor.ts +73 -23
  631. package/src/main.ts +2 -0
  632. package/src/output.ts +14 -6
  633. package/src/pipeline/executor.ts +1 -1
  634. package/src/pipeline/steps/browser.ts +1 -3
  635. package/src/pipeline/steps/download.test.ts +136 -0
  636. package/src/pipeline/steps/download.ts +47 -34
  637. package/src/pipeline/steps/fetch.test.ts +179 -0
  638. package/src/pipeline/steps/fetch.ts +39 -23
  639. package/src/pipeline/steps/transform.ts +2 -6
  640. package/src/pipeline/template.test.ts +28 -0
  641. package/src/pipeline/template.ts +67 -79
  642. package/src/pipeline/transform.test.ts +20 -0
  643. package/src/plugin.test.ts +251 -3
  644. package/src/plugin.ts +265 -21
  645. package/src/record.ts +12 -84
  646. package/src/registry-api.ts +2 -0
  647. package/src/registry.ts +7 -4
  648. package/src/runtime.ts +14 -4
  649. package/src/snapshotFormatter.ts +43 -121
  650. package/src/utils.ts +39 -0
  651. package/src/validate.ts +3 -5
  652. package/src/weread-search-regression.test.ts +44 -0
  653. package/src/yaml-schema.ts +28 -0
  654. package/tests/e2e/browser-auth.test.ts +25 -0
  655. package/tests/e2e/browser-public-extended.test.ts +162 -0
  656. package/tests/e2e/browser-public.test.ts +7 -146
  657. package/tests/e2e/plugin-management.test.ts +137 -0
  658. package/tests/e2e/public-commands.test.ts +34 -1
  659. package/vitest.config.ts +33 -8
  660. package/.github/workflows/pkg-pr-new.yml +0 -30
  661. package/dist/clis/douban/shared.d.ts +0 -4
  662. package/dist/clis/douban/shared.js +0 -155
  663. package/src/clis/douban/shared.ts +0 -165
  664. /package/dist/clis/boss/{common.d.ts → utils.d.ts} +0 -0
  665. /package/dist/clis/boss/{common.js → utils.js} +0 -0
  666. /package/dist/clis/doubao/{common.d.ts → utils.d.ts} +0 -0
  667. /package/dist/clis/doubao/{common.js → utils.js} +0 -0
  668. /package/dist/clis/doubao-app/{common.d.ts → utils.d.ts} +0 -0
  669. /package/dist/clis/doubao-app/{common.js → utils.js} +0 -0
  670. /package/dist/clis/jike/{shared.d.ts → utils.d.ts} +0 -0
  671. /package/dist/clis/jike/{shared.js → utils.js} +0 -0
  672. /package/dist/clis/medium/{shared.d.ts → utils.d.ts} +0 -0
  673. /package/dist/clis/sinablog/{shared.d.ts → utils.d.ts} +0 -0
  674. /package/dist/clis/sinablog/{shared.js → utils.js} +0 -0
  675. /package/dist/clis/substack/{shared.d.ts → utils.d.ts} +0 -0
  676. /package/src/clis/boss/{common.ts → utils.ts} +0 -0
  677. /package/src/clis/doubao/{common.ts → utils.ts} +0 -0
  678. /package/src/clis/doubao-app/{common.ts → utils.ts} +0 -0
  679. /package/src/clis/jike/{shared.ts → utils.ts} +0 -0
  680. /package/src/clis/sinablog/{shared.ts → utils.ts} +0 -0
@@ -1,21 +1,14 @@
1
1
  /**
2
2
  * Aria snapshot formatter: parses Playwright MCP snapshot text into clean format.
3
3
  *
4
- * Multi-pass pipeline:
5
- * 1. Parse & filter: strip annotations, metadata, noise roles, ads, decorators
6
- * 2. Deduplicate: generic/text child matching parent label
7
- * 3. Deduplicate: heading + link with identical labels
8
- * 4. Deduplicate: nested identical links
9
- * 5. Prune: empty containers (iterative bottom-up)
10
- * 6. Collapse: single-child containers
4
+ * 4-pass pipeline:
5
+ * 1. Parse & filter: strip annotations, metadata, noise, ads, boilerplate subtrees
6
+ * 2. Deduplicate: generic/text parent match, heading+link, nested identical links
7
+ * 3. Prune: empty containers (iterative bottom-up)
8
+ * 4. Collapse: single-child containers
11
9
  */
12
10
 
13
- export interface FormatOptions {
14
- interactive?: boolean;
15
- compact?: boolean;
16
- maxDepth?: number;
17
- maxTextLength?: number;
18
- }
11
+ import type { SnapshotOptions } from './types.js';
19
12
 
20
13
  const DEFAULT_MAX_TEXT_LENGTH = 200;
21
14
 
@@ -199,19 +192,18 @@ interface Entry {
199
192
  trailingText: string;
200
193
  isInteractive: boolean;
201
194
  isLandmark: boolean;
202
- isSubtreeSkip: boolean; // ad nodes or boilerplate — skip entire subtree
203
195
  }
204
196
 
205
- export function formatSnapshot(raw: string, opts: FormatOptions = {}): string {
197
+ export function formatSnapshot(raw: string, opts: SnapshotOptions = {}): string {
206
198
  if (!raw || typeof raw !== 'string') return '';
207
199
 
208
200
  const maxTextLen = opts.maxTextLength ?? DEFAULT_MAX_TEXT_LENGTH;
209
201
  const lines = raw.split('\n');
210
202
 
211
- // === Pass 1: Parse, filter, and collect entries ===
212
- const entries: Entry[] = [];
203
+ // === Pass 1: Parse, filter, and collect entries (merged with ad/boilerplate subtree skip) ===
204
+ const parsed: Entry[] = [];
213
205
  let refCounter = 0;
214
- let skipUntilDepth = -1; // When >= 0, skip all nodes at depth > this value
206
+ let skipUntilDepth = -1;
215
207
 
216
208
  for (let i = 0; i < lines.length; i++) {
217
209
  const line = lines[i];
@@ -220,148 +212,88 @@ export function formatSnapshot(raw: string, opts: FormatOptions = {}): string {
220
212
  const indent = line.length - line.trimStart().length;
221
213
  const depth = Math.floor(indent / 2);
222
214
 
223
- // If we're in a subtree skip zone, check depth
215
+ // Subtree skip zone (noise roles, ads, boilerplate)
224
216
  if (skipUntilDepth >= 0) {
225
- if (depth > skipUntilDepth) continue; // still inside subtree
226
- skipUntilDepth = -1; // exited subtree
217
+ if (depth > skipUntilDepth) continue;
218
+ skipUntilDepth = -1;
227
219
  }
228
220
 
229
221
  let content = line.trimStart();
230
-
231
- // Strip leading "- "
232
- if (content.startsWith('- ')) {
233
- content = content.slice(2);
234
- }
235
-
236
- // Skip metadata lines
222
+ if (content.startsWith('- ')) content = content.slice(2);
237
223
  if (isMetadataLine(content)) continue;
238
-
239
- // Apply maxDepth filter
240
224
  if (opts.maxDepth !== undefined && depth > opts.maxDepth) continue;
241
225
 
242
226
  const { role, text, hasText, trailingText } = parseLine(content);
243
227
 
244
- // Skip noise nodes
245
228
  if (isNoiseNode(role, hasText, text, trailingText)) continue;
246
229
 
247
- // Skip subtree noise roles (contentinfo footer, etc.) — skip entire subtree
248
- if (SUBTREE_NOISE_ROLES.has(role)) {
249
- skipUntilDepth = depth;
250
- continue;
251
- }
230
+ // Subtree noise roles (contentinfo footer, etc.)
231
+ if (SUBTREE_NOISE_ROLES.has(role)) { skipUntilDepth = depth; continue; }
252
232
 
253
- // Strip annotations
254
- content = stripAnnotations(content);
233
+ // Ads and boilerplate — skip entire subtree (merged from old Pass 2)
234
+ if (isAdNode(text, trailingText) || isBoilerplateNode(text)) { skipUntilDepth = depth; continue; }
255
235
 
256
- // Check if node should trigger subtree skip (ads, boilerplate)
257
- const isSubtreeSkip = isAdNode(text, trailingText) || isBoilerplateNode(text);
236
+ content = stripAnnotations(content);
258
237
 
259
- // Interactive mode filter
260
238
  const isInteractive = INTERACTIVE_ROLES.has(role);
261
239
  const isLandmark = LANDMARK_ROLES.has(role);
262
-
263
240
  if (opts.interactive && !isInteractive && !isLandmark && !hasText) continue;
264
241
 
265
- // Compact mode
266
242
  if (opts.compact) {
267
- content = content
268
- .replace(/\s*\[.*?\]\s*/g, ' ')
269
- .replace(/\s+/g, ' ')
270
- .trim();
243
+ content = content.replace(/\s*\[.*?\]\s*/g, ' ').replace(/\s+/g, ' ').trim();
271
244
  }
272
-
273
- // Text truncation
274
245
  if (maxTextLen > 0 && content.length > maxTextLen) {
275
246
  content = content.slice(0, maxTextLen) + '…';
276
247
  }
277
-
278
- // Assign refs to interactive elements
279
248
  if (isInteractive) {
280
249
  refCounter++;
281
250
  content = `[@${refCounter}] ${content}`;
282
251
  }
283
252
 
284
- entries.push({ depth, content, role, text, trailingText, isInteractive, isLandmark, isSubtreeSkip });
285
- }
286
-
287
- // === Pass 2: Remove subtree-skip nodes (ads, boilerplate, contentinfo) ===
288
- let noAds: Entry[] = [];
289
- for (let i = 0; i < entries.length; i++) {
290
- const entry = entries[i];
291
- if (entry.isSubtreeSkip) {
292
- const skipDepth = entry.depth;
293
- i++;
294
- while (i < entries.length && entries[i].depth > skipDepth) {
295
- i++;
296
- }
297
- i--;
298
- continue;
299
- }
300
- noAds.push(entry);
253
+ parsed.push({ depth, content, role, text, trailingText, isInteractive, isLandmark });
301
254
  }
302
255
 
303
- // === Pass 3: Deduplicate child generic/text matching parent label ===
304
- let deduped: Entry[] = [];
305
- for (let i = 0; i < noAds.length; i++) {
306
- const entry = noAds[i];
256
+ // === Pass 2: Deduplicate (merged: generic/text parent match + heading+link + nested links) ===
257
+ const deduped: Entry[] = [];
258
+ for (let i = 0; i < parsed.length; i++) {
259
+ const entry = parsed[i];
307
260
 
261
+ // Dedup: generic/text child matching parent label
308
262
  if (entry.role === 'generic' || entry.role === 'text') {
309
263
  let parent: Entry | undefined;
310
264
  for (let j = deduped.length - 1; j >= 0; j--) {
311
- if (deduped[j].depth < entry.depth) {
312
- parent = deduped[j];
313
- break;
314
- }
265
+ if (deduped[j].depth < entry.depth) { parent = deduped[j]; break; }
315
266
  if (deduped[j].depth === entry.depth) break;
316
267
  }
317
-
318
268
  if (parent) {
319
269
  const childText = entry.trailingText || entry.text;
320
- if (childText && parent.text && childText === parent.text) {
321
- continue;
322
- }
270
+ if (childText && parent.text && childText === parent.text) continue;
323
271
  }
324
272
  }
325
273
 
326
- deduped.push(entry);
327
- }
328
-
329
- // === Pass 4: Deduplicate heading + child link with identical label ===
330
- // Pattern: heading "Title": → link "Title": (same text) → skip the link
331
- const deduped2: Entry[] = [];
332
- for (let i = 0; i < deduped.length; i++) {
333
- const entry = deduped[i];
334
-
274
+ // Dedup: heading + child link with identical label
335
275
  if (entry.role === 'heading' && entry.text) {
336
- const next = deduped[i + 1];
276
+ const next = parsed[i + 1];
337
277
  if (next && next.role === 'link' && next.text === entry.text && next.depth === entry.depth + 1) {
338
- // Keep the heading, skip the link. But preserve link's children re-parented.
339
- deduped2.push(entry);
340
- i++; // skip the link
278
+ deduped.push(entry);
279
+ i++; // skip the link, preserve its children
341
280
  continue;
342
281
  }
343
282
  }
344
283
 
345
- deduped2.push(entry);
346
- }
347
-
348
- // === Pass 5: Deduplicate nested identical links ===
349
- const deduped3: Entry[] = [];
350
- for (let i = 0; i < deduped2.length; i++) {
351
- const entry = deduped2[i];
352
-
284
+ // Dedup: nested identical links (skip parent, keep child)
353
285
  if (entry.role === 'link' && entry.text) {
354
- const next = deduped2[i + 1];
286
+ const next = parsed[i + 1];
355
287
  if (next && next.role === 'link' && next.text === entry.text && next.depth === entry.depth + 1) {
356
- continue; // Skip parent, keep child
288
+ continue;
357
289
  }
358
290
  }
359
291
 
360
- deduped3.push(entry);
292
+ deduped.push(entry);
361
293
  }
362
294
 
363
- // === Pass 6: Iteratively prune empty containers (bottom-up) ===
364
- let current = deduped3;
295
+ // === Pass 3: Iteratively prune empty containers (bottom-up) ===
296
+ let current = deduped;
365
297
  let changed = true;
366
298
  while (changed) {
367
299
  changed = false;
@@ -372,22 +304,16 @@ export function formatSnapshot(raw: string, opts: FormatOptions = {}): string {
372
304
  let hasChildren = false;
373
305
  for (let j = i + 1; j < current.length; j++) {
374
306
  if (current[j].depth <= entry.depth) break;
375
- if (current[j].depth > entry.depth) {
376
- hasChildren = true;
377
- break;
378
- }
379
- }
380
- if (!hasChildren) {
381
- changed = true;
382
- continue;
307
+ if (current[j].depth > entry.depth) { hasChildren = true; break; }
383
308
  }
309
+ if (!hasChildren) { changed = true; continue; }
384
310
  }
385
311
  next.push(entry);
386
312
  }
387
313
  current = next;
388
314
  }
389
315
 
390
- // === Pass 7: Collapse single-child containers ===
316
+ // === Pass 4: Collapse single-child containers ===
391
317
  const collapsed: Entry[] = [];
392
318
  for (let i = 0; i < current.length; i++) {
393
319
  const entry = current[i];
@@ -408,17 +334,13 @@ export function formatSnapshot(raw: string, opts: FormatOptions = {}): string {
408
334
  let hasGrandchildren = false;
409
335
  for (let j = childIdx + 1; j < current.length; j++) {
410
336
  if (current[j].depth <= child.depth) break;
411
- if (current[j].depth > child.depth) {
412
- hasGrandchildren = true;
413
- break;
414
- }
337
+ if (current[j].depth > child.depth) { hasGrandchildren = true; break; }
415
338
  }
416
339
 
417
340
  if (!hasGrandchildren) {
418
- const mergedContent = entry.content.replace(/:$/, '') + ' > ' + child.content;
419
341
  collapsed.push({
420
342
  ...entry,
421
- content: mergedContent,
343
+ content: entry.content.replace(/:$/, '') + ' > ' + child.content,
422
344
  role: child.role,
423
345
  text: child.text,
424
346
  trailingText: child.trailingText,
package/src/utils.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Shared utility functions used across the codebase.
3
+ */
4
+
5
+ import * as fs from 'node:fs';
6
+ import * as path from 'node:path';
7
+
8
+ /** Type guard: checks if a value is a non-null, non-array object. */
9
+ export function isRecord(value: unknown): value is Record<string, unknown> {
10
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
11
+ }
12
+
13
+ /** Simple async concurrency limiter. */
14
+ export async function mapConcurrent<T, R>(
15
+ items: T[],
16
+ limit: number,
17
+ fn: (item: T, index: number) => Promise<R>,
18
+ ): Promise<R[]> {
19
+ const results: R[] = new Array(items.length);
20
+ let index = 0;
21
+
22
+ async function worker() {
23
+ while (index < items.length) {
24
+ const i = index++;
25
+ results[i] = await fn(items[i], i);
26
+ }
27
+ }
28
+
29
+ const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());
30
+ await Promise.all(workers);
31
+ return results;
32
+ }
33
+
34
+ /** Save a base64-encoded string to a file, creating parent directories as needed. */
35
+ export async function saveBase64ToFile(base64: string, filePath: string): Promise<void> {
36
+ const dir = path.dirname(filePath);
37
+ await fs.promises.mkdir(dir, { recursive: true });
38
+ await fs.promises.writeFile(filePath, Buffer.from(base64, 'base64'));
39
+ }
package/src/validate.ts CHANGED
@@ -6,10 +6,10 @@ import { getErrorMessage } from './errors.js';
6
6
 
7
7
  /** All recognized pipeline step names */
8
8
  const KNOWN_STEP_NAMES = new Set([
9
- 'navigate', 'click', 'type', 'wait', 'press', 'snapshot', 'scroll',
9
+ 'navigate', 'click', 'type', 'wait', 'press', 'snapshot',
10
10
  'fetch', 'evaluate',
11
11
  'select', 'map', 'filter', 'sort', 'limit',
12
- 'intercept', 'tap',
12
+ 'intercept', 'tap', 'download',
13
13
  ]);
14
14
 
15
15
  export interface FileValidationResult {
@@ -34,9 +34,7 @@ interface ValidatedYamlCliDefinition {
34
34
  args?: Record<string, unknown>;
35
35
  }
36
36
 
37
- function isRecord(value: unknown): value is Record<string, unknown> {
38
- return typeof value === 'object' && value !== null && !Array.isArray(value);
39
- }
37
+ import { isRecord } from './utils.js';
40
38
 
41
39
 
42
40
  export function validateClisWithTarget(dirs: string[], target?: string): ValidationReport {
@@ -0,0 +1,44 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { getRegistry } from './registry.js';
3
+ import './clis/weread/search.js';
4
+
5
+ describe('weread/search regression', () => {
6
+ beforeEach(() => {
7
+ vi.restoreAllMocks();
8
+ });
9
+
10
+ it('uses the query argument for the search API and returns urls', async () => {
11
+ const command = getRegistry().get('weread/search');
12
+ expect(command?.func).toBeTypeOf('function');
13
+
14
+ const fetchMock = vi.fn().mockResolvedValue({
15
+ ok: true,
16
+ json: () => Promise.resolve({
17
+ books: [
18
+ {
19
+ bookInfo: {
20
+ title: 'Deep Work',
21
+ author: 'Cal Newport',
22
+ bookId: 'abc123',
23
+ },
24
+ },
25
+ ],
26
+ }),
27
+ });
28
+ vi.stubGlobal('fetch', fetchMock);
29
+
30
+ const result = await command!.func!(null as any, { query: 'deep work', limit: 5 });
31
+
32
+ expect(fetchMock).toHaveBeenCalledTimes(1);
33
+ expect(String(fetchMock.mock.calls[0][0])).toContain('keyword=deep+work');
34
+ expect(result).toEqual([
35
+ {
36
+ rank: 1,
37
+ title: 'Deep Work',
38
+ author: 'Cal Newport',
39
+ bookId: 'abc123',
40
+ url: 'https://weread.qq.com/web/bookDetail/abc123',
41
+ },
42
+ ]);
43
+ });
44
+ });
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Shared YAML CLI definition types.
3
+ * Used by both discovery.ts (runtime) and build-manifest.ts (build-time).
4
+ */
5
+
6
+ export interface YamlArgDefinition {
7
+ type?: string;
8
+ default?: unknown;
9
+ required?: boolean;
10
+ positional?: boolean;
11
+ description?: string;
12
+ help?: string;
13
+ choices?: string[];
14
+ }
15
+
16
+ export interface YamlCliDefinition {
17
+ site?: string;
18
+ name?: string;
19
+ description?: string;
20
+ domain?: string;
21
+ strategy?: string;
22
+ browser?: boolean;
23
+ args?: Record<string, YamlArgDefinition>;
24
+ columns?: string[];
25
+ pipeline?: Record<string, unknown>[];
26
+ timeout?: number;
27
+ navigateBefore?: boolean | string;
28
+ }
@@ -113,6 +113,31 @@ describe('login-required commands — graceful failure', () => {
113
113
  await expectGracefulAuthFailure(['xiaohongshu', 'notifications', '--limit', '3', '-f', 'json'], 'xiaohongshu notifications');
114
114
  }, 60_000);
115
115
 
116
+ // ── pixiv (requires login) ──
117
+ it('pixiv ranking fails gracefully without login', async () => {
118
+ await expectGracefulAuthFailure(['pixiv', 'ranking', '--limit', '3', '-f', 'json'], 'pixiv ranking');
119
+ }, 60_000);
120
+
121
+ it('pixiv search fails gracefully without login', async () => {
122
+ await expectGracefulAuthFailure(['pixiv', 'search', '初音ミク', '--limit', '3', '-f', 'json'], 'pixiv search');
123
+ }, 60_000);
124
+
125
+ it('pixiv user fails gracefully without login', async () => {
126
+ await expectGracefulAuthFailure(['pixiv', 'user', '11', '-f', 'json'], 'pixiv user');
127
+ }, 60_000);
128
+
129
+ it('pixiv illusts fails gracefully without login', async () => {
130
+ await expectGracefulAuthFailure(['pixiv', 'illusts', '11', '--limit', '3', '-f', 'json'], 'pixiv illusts');
131
+ }, 60_000);
132
+
133
+ it('pixiv detail fails gracefully without login', async () => {
134
+ await expectGracefulAuthFailure(['pixiv', 'detail', '123456', '-f', 'json'], 'pixiv detail');
135
+ }, 60_000);
136
+
137
+ it('pixiv download fails gracefully without login', async () => {
138
+ await expectGracefulAuthFailure(['pixiv', 'download', '123456', '--output', '/tmp/pixiv-e2e-test', '-f', 'json'], 'pixiv download');
139
+ }, 60_000);
140
+
116
141
  // ── yollomi (requires login session) ──
117
142
  it('yollomi generate fails gracefully without login', async () => {
118
143
  await expectGracefulAuthFailure(['yollomi', 'generate', 'a cute cat', '--no-download', '-f', 'json'], 'yollomi generate');
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Extended E2E tests for all other browser commands.
3
+ * Opt-in only: OPENCLI_E2E=1 npx vitest run
4
+ */
5
+
6
+ import { describe, it, expect } from 'vitest';
7
+ import { runCli, parseJsonOutput } from './helpers.js';
8
+
9
+ async function tryBrowserCommand(args: string[]): Promise<any[] | null> {
10
+ const { stdout, code } = await runCli(args, { timeout: 60_000 });
11
+ if (code !== 0) return null;
12
+ try {
13
+ const data = parseJsonOutput(stdout);
14
+ return Array.isArray(data) ? data : null;
15
+ } catch {
16
+ return null;
17
+ }
18
+ }
19
+
20
+ function expectDataOrSkip(data: any[] | null, label: string) {
21
+ if (data === null || data.length === 0) {
22
+ console.warn(`${label}: skipped — no data returned (likely bot detection or geo-blocking)`);
23
+ return;
24
+ }
25
+ expect(data.length).toBeGreaterThanOrEqual(1);
26
+ }
27
+
28
+ describe('browser extended public-data commands E2E', () => {
29
+
30
+ // ── bbc ──
31
+ it('bbc news returns headlines', async () => {
32
+ const data = await tryBrowserCommand(['bbc', 'news', '--limit', '3', '-f', 'json']);
33
+ expectDataOrSkip(data, 'bbc news');
34
+ if (data) {
35
+ expect(data[0]).toHaveProperty('title');
36
+ }
37
+ }, 60_000);
38
+
39
+ // ── bloomberg ──
40
+ it('bloomberg news returns article detail when the article page is accessible', async () => {
41
+ const feedResult = await runCli(['bloomberg', 'tech', '--limit', '1', '-f', 'json']);
42
+ if (feedResult.code !== 0) {
43
+ console.warn('bloomberg news: skipped — could not load Bloomberg tech feed');
44
+ return;
45
+ }
46
+
47
+ const feedItems = parseJsonOutput(feedResult.stdout);
48
+ const link = Array.isArray(feedItems) ? feedItems[0]?.link : null;
49
+ if (!link) {
50
+ console.warn('bloomberg news: skipped — tech feed returned no link');
51
+ return;
52
+ }
53
+
54
+ const data = await tryBrowserCommand(['bloomberg', 'news', link, '-f', 'json']);
55
+ expectDataOrSkip(data, 'bloomberg news');
56
+ if (data) {
57
+ expect(data[0]).toHaveProperty('title');
58
+ expect(data[0]).toHaveProperty('summary');
59
+ expect(data[0]).toHaveProperty('link');
60
+ expect(data[0]).toHaveProperty('mediaLinks');
61
+ expect(data[0]).toHaveProperty('content');
62
+ }
63
+ }, 60_000);
64
+
65
+ // ── weibo ──
66
+ it('weibo hot returns trending topics', async () => {
67
+ const data = await tryBrowserCommand(['weibo', 'hot', '--limit', '5', '-f', 'json']);
68
+ expectDataOrSkip(data, 'weibo hot');
69
+ }, 60_000);
70
+
71
+ it('weibo search returns results', async () => {
72
+ const data = await tryBrowserCommand(['weibo', 'search', 'openai', '--limit', '3', '-f', 'json']);
73
+ expectDataOrSkip(data, 'weibo search');
74
+ }, 60_000);
75
+
76
+ // ── reddit ──
77
+ it('reddit hot returns posts', async () => {
78
+ const data = await tryBrowserCommand(['reddit', 'hot', '--limit', '5', '-f', 'json']);
79
+ expectDataOrSkip(data, 'reddit hot');
80
+ }, 60_000);
81
+
82
+ it('reddit frontpage returns posts', async () => {
83
+ const data = await tryBrowserCommand(['reddit', 'frontpage', '--limit', '5', '-f', 'json']);
84
+ expectDataOrSkip(data, 'reddit frontpage');
85
+ }, 60_000);
86
+
87
+ // ── twitter ──
88
+ it('twitter trending returns trends', async () => {
89
+ const data = await tryBrowserCommand(['twitter', 'trending', '--limit', '5', '-f', 'json']);
90
+ expectDataOrSkip(data, 'twitter trending');
91
+ }, 60_000);
92
+
93
+ // ── xueqiu ──
94
+ it('xueqiu hot returns hot posts', async () => {
95
+ const data = await tryBrowserCommand(['xueqiu', 'hot', '--limit', '5', '-f', 'json']);
96
+ expectDataOrSkip(data, 'xueqiu hot');
97
+ }, 60_000);
98
+
99
+ it('xueqiu hot-stock returns stocks', async () => {
100
+ const data = await tryBrowserCommand(['xueqiu', 'hot-stock', '--limit', '5', '-f', 'json']);
101
+ expectDataOrSkip(data, 'xueqiu hot-stock');
102
+ }, 60_000);
103
+
104
+ // ── reuters ──
105
+ it('reuters search returns articles', async () => {
106
+ const data = await tryBrowserCommand(['reuters', 'search', 'technology', '--limit', '3', '-f', 'json']);
107
+ expectDataOrSkip(data, 'reuters search');
108
+ }, 60_000);
109
+
110
+ // ── youtube ──
111
+ it('youtube search returns videos', async () => {
112
+ const data = await tryBrowserCommand(['youtube', 'search', 'typescript tutorial', '--limit', '3', '-f', 'json']);
113
+ expectDataOrSkip(data, 'youtube search');
114
+ }, 60_000);
115
+
116
+ // ── smzdm ──
117
+ it('smzdm search returns deals', async () => {
118
+ const data = await tryBrowserCommand(['smzdm', 'search', '键盘', '--limit', '3', '-f', 'json']);
119
+ expectDataOrSkip(data, 'smzdm search');
120
+ }, 60_000);
121
+
122
+ // ── boss ──
123
+ it('boss search returns jobs', async () => {
124
+ const data = await tryBrowserCommand(['boss', 'search', 'golang', '--limit', '3', '-f', 'json']);
125
+ expectDataOrSkip(data, 'boss search');
126
+ }, 60_000);
127
+
128
+ // ── ctrip ──
129
+ it('ctrip search returns flights', async () => {
130
+ const data = await tryBrowserCommand(['ctrip', 'search', '-f', 'json']);
131
+ expectDataOrSkip(data, 'ctrip search');
132
+ }, 60_000);
133
+
134
+ // ── coupang ──
135
+ it('coupang search returns products', async () => {
136
+ const data = await tryBrowserCommand(['coupang', 'search', 'laptop', '--limit', '3', '-f', 'json']);
137
+ expectDataOrSkip(data, 'coupang search');
138
+ }, 60_000);
139
+
140
+ // ── xiaohongshu ──
141
+ it('xiaohongshu search returns notes', async () => {
142
+ const data = await tryBrowserCommand(['xiaohongshu', 'search', '美食', '--limit', '3', '-f', 'json']);
143
+ expectDataOrSkip(data, 'xiaohongshu search');
144
+ }, 60_000);
145
+
146
+ // ── google ──
147
+ it('google search returns results', async () => {
148
+ const data = await tryBrowserCommand(['google', 'search', 'typescript', '--limit', '5', '-f', 'json']);
149
+ expectDataOrSkip(data, 'google search');
150
+ if (data) {
151
+ expect(data[0]).toHaveProperty('type');
152
+ expect(data[0]).toHaveProperty('title');
153
+ expect(data[0]).toHaveProperty('url');
154
+ }
155
+ }, 60_000);
156
+
157
+ // ── yahoo-finance ──
158
+ it('yahoo-finance quote returns stock data', async () => {
159
+ const data = await tryBrowserCommand(['yahoo-finance', 'quote', '--symbol', 'AAPL', '-f', 'json']);
160
+ expectDataOrSkip(data, 'yahoo-finance quote');
161
+ }, 60_000);
162
+ });