@jackwener/opencli 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (769) hide show
  1. package/.agents/skills/cross-project-adapter-migration/SKILL.md +2 -2
  2. package/.github/pull_request_template.md +7 -0
  3. package/.github/workflows/doc-check.yml +36 -0
  4. package/.github/workflows/docs.yml +7 -42
  5. package/CHANGELOG.md +23 -0
  6. package/CLI-EXPLORER.md +9 -8
  7. package/CONTRIBUTING.md +39 -1
  8. package/README.md +33 -19
  9. package/README.zh-CN.md +64 -27
  10. package/SKILL.md +102 -33
  11. package/dist/browser/cdp.d.ts +4 -4
  12. package/dist/browser/cdp.js +45 -17
  13. package/dist/browser/daemon-client.d.ts +2 -1
  14. package/dist/browser/dom-helpers.js +38 -7
  15. package/dist/browser/dom-snapshot.d.ts +86 -0
  16. package/dist/browser/dom-snapshot.js +729 -0
  17. package/dist/browser/dom-snapshot.test.d.ts +11 -0
  18. package/dist/browser/dom-snapshot.test.js +212 -0
  19. package/dist/browser/index.d.ts +2 -0
  20. package/dist/browser/index.js +1 -0
  21. package/dist/browser/page.d.ts +18 -25
  22. package/dist/browser/page.js +44 -5
  23. package/dist/build-manifest.d.ts +11 -4
  24. package/dist/build-manifest.js +79 -34
  25. package/dist/build-manifest.test.js +58 -2
  26. package/dist/cli-manifest.json +4273 -1771
  27. package/dist/cli.d.ts +6 -0
  28. package/dist/cli.js +255 -162
  29. package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
  30. package/dist/clis/apple-podcasts/commands.test.js +76 -0
  31. package/dist/clis/apple-podcasts/search.js +2 -2
  32. package/dist/clis/apple-podcasts/top.js +9 -2
  33. package/dist/clis/arxiv/search.js +1 -1
  34. package/dist/clis/barchart/greeks.js +1 -1
  35. package/dist/clis/barchart/options.js +1 -1
  36. package/dist/clis/barchart/quote.js +1 -1
  37. package/dist/clis/bilibili/download.js +1 -1
  38. package/dist/clis/bilibili/dynamic.js +1 -1
  39. package/dist/clis/bilibili/favorite.js +1 -1
  40. package/dist/clis/bilibili/feed.js +1 -1
  41. package/dist/clis/bilibili/following.js +2 -2
  42. package/dist/clis/bilibili/history.js +1 -1
  43. package/dist/clis/bilibili/me.js +1 -1
  44. package/dist/clis/bilibili/ranking.js +1 -1
  45. package/dist/clis/bilibili/search.js +3 -3
  46. package/dist/clis/bilibili/subtitle.js +2 -2
  47. package/dist/clis/bilibili/user-videos.js +2 -2
  48. package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
  49. package/dist/clis/bloomberg/businessweek.js +17 -0
  50. package/dist/clis/bloomberg/economics.js +17 -0
  51. package/dist/clis/bloomberg/feeds.d.ts +1 -0
  52. package/dist/clis/bloomberg/feeds.js +15 -0
  53. package/dist/clis/bloomberg/industries.d.ts +1 -0
  54. package/dist/clis/bloomberg/industries.js +17 -0
  55. package/dist/clis/bloomberg/main.d.ts +1 -0
  56. package/dist/clis/bloomberg/main.js +17 -0
  57. package/dist/clis/bloomberg/markets.d.ts +1 -0
  58. package/dist/clis/bloomberg/markets.js +17 -0
  59. package/dist/clis/bloomberg/news.d.ts +1 -0
  60. package/dist/clis/bloomberg/news.js +105 -0
  61. package/dist/clis/bloomberg/opinions.d.ts +1 -0
  62. package/dist/clis/bloomberg/opinions.js +17 -0
  63. package/dist/clis/bloomberg/politics.d.ts +1 -0
  64. package/dist/clis/bloomberg/politics.js +17 -0
  65. package/dist/clis/bloomberg/tech.d.ts +1 -0
  66. package/dist/clis/bloomberg/tech.js +17 -0
  67. package/dist/clis/bloomberg/utils.d.ts +34 -0
  68. package/dist/clis/bloomberg/utils.js +364 -0
  69. package/dist/clis/bloomberg/utils.test.d.ts +1 -0
  70. package/dist/clis/bloomberg/utils.test.js +129 -0
  71. package/dist/clis/boss/batchgreet.js +12 -99
  72. package/dist/clis/boss/chatlist.js +9 -26
  73. package/dist/clis/boss/chatmsg.js +11 -42
  74. package/dist/clis/boss/common.d.ts +92 -0
  75. package/dist/clis/boss/common.js +223 -0
  76. package/dist/clis/boss/detail.js +8 -50
  77. package/dist/clis/boss/exchange.js +13 -79
  78. package/dist/clis/boss/greet.js +20 -147
  79. package/dist/clis/boss/invite.js +26 -121
  80. package/dist/clis/boss/joblist.js +6 -31
  81. package/dist/clis/boss/mark.js +12 -85
  82. package/dist/clis/boss/recommend.js +10 -49
  83. package/dist/clis/boss/resume.js +18 -118
  84. package/dist/clis/boss/search.js +13 -61
  85. package/dist/clis/boss/send.js +18 -152
  86. package/dist/clis/boss/stats.js +20 -71
  87. package/dist/clis/chaoxing/assignments.js +1 -1
  88. package/dist/clis/chaoxing/exams.js +1 -1
  89. package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
  90. package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
  91. package/dist/clis/chaoxing/utils.test.d.ts +1 -0
  92. package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
  93. package/dist/clis/chatgpt/read.js +1 -1
  94. package/dist/clis/chatwise/export.js +1 -1
  95. package/dist/clis/chatwise/model.js +2 -2
  96. package/dist/clis/chatwise/screenshot.js +1 -1
  97. package/dist/clis/codex/export.js +1 -1
  98. package/dist/clis/codex/model.js +2 -2
  99. package/dist/clis/codex/screenshot.js +1 -1
  100. package/dist/clis/coupang/add-to-cart.js +3 -4
  101. package/dist/clis/coupang/search.js +2 -4
  102. package/dist/clis/coupang/utils.test.d.ts +1 -0
  103. package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
  104. package/dist/clis/ctrip/search.js +1 -1
  105. package/dist/clis/cursor/export.js +1 -1
  106. package/dist/clis/cursor/model.js +2 -2
  107. package/dist/clis/cursor/screenshot.js +1 -1
  108. package/dist/clis/devto/tag.yaml +34 -0
  109. package/dist/clis/devto/top.yaml +29 -0
  110. package/dist/clis/devto/user.yaml +33 -0
  111. package/dist/clis/douban/book-hot.d.ts +1 -0
  112. package/dist/clis/douban/book-hot.js +14 -0
  113. package/dist/clis/douban/marks.d.ts +1 -0
  114. package/dist/clis/douban/marks.js +115 -0
  115. package/dist/clis/douban/movie-hot.d.ts +1 -0
  116. package/dist/clis/douban/movie-hot.js +14 -0
  117. package/dist/clis/douban/reviews.d.ts +1 -0
  118. package/dist/clis/douban/reviews.js +106 -0
  119. package/dist/clis/douban/search.d.ts +1 -0
  120. package/dist/clis/douban/search.js +16 -0
  121. package/dist/clis/douban/shared.d.ts +4 -0
  122. package/dist/clis/douban/shared.js +155 -0
  123. package/dist/clis/douban/subject.yaml +76 -0
  124. package/dist/clis/douban/top250.yaml +70 -0
  125. package/dist/clis/douban/utils.d.ts +35 -0
  126. package/dist/clis/douban/utils.js +48 -0
  127. package/dist/clis/facebook/add-friend.yaml +43 -0
  128. package/dist/clis/facebook/events.yaml +44 -0
  129. package/dist/clis/facebook/feed.yaml +63 -0
  130. package/dist/clis/facebook/friends.yaml +42 -0
  131. package/dist/clis/facebook/groups.yaml +50 -0
  132. package/dist/clis/facebook/join-group.yaml +44 -0
  133. package/dist/clis/facebook/memories.yaml +39 -0
  134. package/dist/clis/facebook/notifications.yaml +40 -0
  135. package/dist/clis/facebook/profile.yaml +37 -0
  136. package/dist/clis/facebook/search.yaml +46 -0
  137. package/dist/clis/google/news.d.ts +5 -0
  138. package/dist/clis/google/news.js +58 -0
  139. package/dist/clis/google/search.d.ts +10 -0
  140. package/dist/clis/google/search.js +127 -0
  141. package/dist/clis/google/suggest.d.ts +5 -0
  142. package/dist/clis/google/suggest.js +34 -0
  143. package/dist/clis/google/trends.d.ts +5 -0
  144. package/dist/clis/google/trends.js +38 -0
  145. package/dist/clis/google/utils.d.ts +9 -0
  146. package/dist/clis/google/utils.js +23 -0
  147. package/dist/clis/google/utils.test.d.ts +1 -0
  148. package/dist/clis/google/utils.test.js +75 -0
  149. package/dist/clis/grok/ask.d.ts +14 -0
  150. package/dist/clis/grok/ask.js +257 -65
  151. package/dist/clis/grok/ask.test.d.ts +1 -0
  152. package/dist/clis/grok/ask.test.js +36 -0
  153. package/dist/clis/instagram/comment.yaml +52 -0
  154. package/dist/clis/instagram/explore.yaml +43 -0
  155. package/dist/clis/instagram/follow.yaml +41 -0
  156. package/dist/clis/instagram/followers.yaml +51 -0
  157. package/dist/clis/instagram/following.yaml +51 -0
  158. package/dist/clis/instagram/like.yaml +46 -0
  159. package/dist/clis/instagram/profile.yaml +42 -0
  160. package/dist/clis/instagram/save.yaml +46 -0
  161. package/dist/clis/instagram/saved.yaml +40 -0
  162. package/dist/clis/instagram/search.yaml +43 -0
  163. package/dist/clis/instagram/unfollow.yaml +38 -0
  164. package/dist/clis/instagram/unlike.yaml +46 -0
  165. package/dist/clis/instagram/unsave.yaml +46 -0
  166. package/dist/clis/instagram/user.yaml +54 -0
  167. package/dist/clis/jike/comment.js +2 -3
  168. package/dist/clis/jike/create.js +1 -2
  169. package/dist/clis/jike/feed.js +0 -1
  170. package/dist/clis/jike/like.js +1 -2
  171. package/dist/clis/jike/notifications.js +0 -1
  172. package/dist/clis/jike/post.yaml +1 -0
  173. package/dist/clis/jike/repost.js +2 -3
  174. package/dist/clis/jike/search.js +2 -3
  175. package/dist/clis/jike/topic.yaml +1 -0
  176. package/dist/clis/jike/user.yaml +1 -0
  177. package/dist/clis/jimeng/generate.yaml +1 -0
  178. package/dist/clis/jimeng/history.yaml +0 -1
  179. package/dist/clis/linkedin/search.js +7 -7
  180. package/dist/clis/linux-do/category.yaml +2 -0
  181. package/dist/clis/linux-do/search.yaml +4 -3
  182. package/dist/clis/linux-do/topic.yaml +1 -0
  183. package/dist/clis/lobsters/active.yaml +29 -0
  184. package/dist/clis/lobsters/hot.yaml +29 -0
  185. package/dist/clis/lobsters/newest.yaml +29 -0
  186. package/dist/clis/lobsters/tag.yaml +34 -0
  187. package/dist/clis/medium/feed.d.ts +1 -0
  188. package/dist/clis/medium/feed.js +15 -0
  189. package/dist/clis/medium/search.d.ts +1 -0
  190. package/dist/clis/medium/search.js +15 -0
  191. package/dist/clis/medium/shared.d.ts +5 -0
  192. package/dist/clis/medium/shared.js +78 -0
  193. package/dist/clis/medium/user.d.ts +1 -0
  194. package/dist/clis/medium/user.js +15 -0
  195. package/dist/clis/notion/export.js +1 -1
  196. package/dist/clis/reddit/comment.js +3 -4
  197. package/dist/clis/reddit/read.js +4 -5
  198. package/dist/clis/reddit/save.js +2 -3
  199. package/dist/clis/reddit/saved.js +0 -1
  200. package/dist/clis/reddit/search.yaml +1 -0
  201. package/dist/clis/reddit/subreddit.yaml +1 -0
  202. package/dist/clis/reddit/subscribe.js +1 -2
  203. package/dist/clis/reddit/upvote.js +2 -3
  204. package/dist/clis/reddit/upvoted.js +0 -1
  205. package/dist/clis/reddit/user-comments.yaml +1 -0
  206. package/dist/clis/reddit/user-posts.yaml +1 -0
  207. package/dist/clis/reddit/user.yaml +1 -0
  208. package/dist/clis/reuters/search.js +1 -1
  209. package/dist/clis/sinablog/article.d.ts +1 -0
  210. package/dist/clis/sinablog/article.js +14 -0
  211. package/dist/clis/sinablog/hot.d.ts +1 -0
  212. package/dist/clis/sinablog/hot.js +14 -0
  213. package/dist/clis/sinablog/search.d.ts +1 -0
  214. package/dist/clis/sinablog/search.js +51 -0
  215. package/dist/clis/sinablog/shared.d.ts +7 -0
  216. package/dist/clis/sinablog/shared.js +187 -0
  217. package/dist/clis/sinablog/user.d.ts +1 -0
  218. package/dist/clis/sinablog/user.js +15 -0
  219. package/dist/clis/smzdm/search.js +2 -3
  220. package/dist/clis/stackoverflow/search.yaml +1 -0
  221. package/dist/clis/steam/top-sellers.yaml +29 -0
  222. package/dist/clis/substack/feed.d.ts +1 -0
  223. package/dist/clis/substack/feed.js +15 -0
  224. package/dist/clis/substack/publication.d.ts +1 -0
  225. package/dist/clis/substack/publication.js +15 -0
  226. package/dist/clis/substack/search.d.ts +1 -0
  227. package/dist/clis/substack/search.js +77 -0
  228. package/dist/clis/substack/shared.d.ts +4 -0
  229. package/dist/clis/substack/shared.js +129 -0
  230. package/dist/clis/tiktok/comment.yaml +66 -0
  231. package/dist/clis/tiktok/explore.yaml +39 -0
  232. package/dist/clis/tiktok/follow.yaml +39 -0
  233. package/dist/clis/tiktok/following.yaml +46 -0
  234. package/dist/clis/tiktok/friends.yaml +47 -0
  235. package/dist/clis/tiktok/like.yaml +38 -0
  236. package/dist/clis/tiktok/live.yaml +51 -0
  237. package/dist/clis/tiktok/notifications.yaml +52 -0
  238. package/dist/clis/tiktok/profile.yaml +45 -0
  239. package/dist/clis/tiktok/save.yaml +34 -0
  240. package/dist/clis/tiktok/search.yaml +46 -0
  241. package/dist/clis/tiktok/unfollow.yaml +44 -0
  242. package/dist/clis/tiktok/unlike.yaml +38 -0
  243. package/dist/clis/tiktok/unsave.yaml +36 -0
  244. package/dist/clis/tiktok/user.yaml +44 -0
  245. package/dist/clis/twitter/accept.js +2 -2
  246. package/dist/clis/twitter/article.js +2 -2
  247. package/dist/clis/twitter/block.d.ts +1 -0
  248. package/dist/clis/twitter/block.js +88 -0
  249. package/dist/clis/twitter/delete.js +1 -1
  250. package/dist/clis/twitter/download.d.ts +1 -1
  251. package/dist/clis/twitter/download.js +3 -3
  252. package/dist/clis/twitter/followers.js +1 -1
  253. package/dist/clis/twitter/following.js +1 -1
  254. package/dist/clis/twitter/hide-reply.d.ts +1 -0
  255. package/dist/clis/twitter/hide-reply.js +66 -0
  256. package/dist/clis/twitter/like.js +1 -1
  257. package/dist/clis/twitter/post.js +1 -1
  258. package/dist/clis/twitter/reply-dm.js +1 -1
  259. package/dist/clis/twitter/reply.js +2 -2
  260. package/dist/clis/twitter/search.js +1 -1
  261. package/dist/clis/twitter/thread.js +2 -2
  262. package/dist/clis/twitter/timeline.d.ts +23 -0
  263. package/dist/clis/twitter/timeline.js +42 -14
  264. package/dist/clis/twitter/timeline.test.d.ts +1 -0
  265. package/dist/clis/twitter/timeline.test.js +102 -0
  266. package/dist/clis/twitter/trending.d.ts +1 -0
  267. package/dist/clis/twitter/trending.js +91 -0
  268. package/dist/clis/twitter/unblock.d.ts +1 -0
  269. package/dist/clis/twitter/unblock.js +71 -0
  270. package/dist/clis/v2ex/topic.yaml +1 -0
  271. package/dist/clis/weibo/hot.js +0 -1
  272. package/dist/clis/weread/book.js +1 -1
  273. package/dist/clis/weread/highlights.js +1 -1
  274. package/dist/clis/weread/notes.js +1 -1
  275. package/dist/clis/weread/search.js +1 -1
  276. package/dist/clis/wikipedia/random.d.ts +1 -0
  277. package/dist/clis/wikipedia/random.js +19 -0
  278. package/dist/clis/wikipedia/search.js +4 -4
  279. package/dist/clis/wikipedia/summary.js +4 -9
  280. package/dist/clis/wikipedia/trending.d.ts +1 -0
  281. package/dist/clis/wikipedia/trending.js +35 -0
  282. package/dist/clis/wikipedia/utils.d.ts +28 -0
  283. package/dist/clis/wikipedia/utils.js +13 -0
  284. package/dist/clis/xiaohongshu/creator-note-detail.d.ts +15 -0
  285. package/dist/clis/xiaohongshu/creator-note-detail.js +69 -5
  286. package/dist/clis/xiaohongshu/creator-note-detail.test.js +82 -33
  287. package/dist/clis/xiaohongshu/creator-notes.js +35 -5
  288. package/dist/clis/xiaohongshu/creator-notes.test.js +37 -6
  289. package/dist/clis/xiaohongshu/creator-profile.js +0 -1
  290. package/dist/clis/xiaohongshu/creator-stats.js +0 -1
  291. package/dist/clis/xiaohongshu/download.js +2 -3
  292. package/dist/clis/xiaohongshu/feed.yaml +0 -1
  293. package/dist/clis/xiaohongshu/notifications.yaml +0 -1
  294. package/dist/clis/xiaohongshu/search.js +2 -2
  295. package/dist/clis/xiaohongshu/user.js +1 -2
  296. package/dist/clis/xueqiu/earnings-date.yaml +69 -0
  297. package/dist/clis/xueqiu/search.yaml +2 -1
  298. package/dist/clis/xueqiu/stock.yaml +2 -0
  299. package/dist/clis/yahoo-finance/quote.js +1 -2
  300. package/dist/clis/youtube/search.js +1 -1
  301. package/dist/clis/youtube/transcript.js +1 -1
  302. package/dist/clis/youtube/video.js +1 -1
  303. package/dist/clis/zhihu/download.js +1 -2
  304. package/dist/clis/zhihu/question.js +1 -1
  305. package/dist/clis/zhihu/search.yaml +4 -3
  306. package/dist/commanderAdapter.d.ts +21 -0
  307. package/dist/commanderAdapter.js +117 -0
  308. package/dist/{engine.d.ts → discovery.d.ts} +6 -4
  309. package/dist/{engine.js → discovery.js} +93 -104
  310. package/dist/doctor.js +3 -1
  311. package/dist/doctor.test.js +46 -2
  312. package/dist/download/index.d.ts +2 -6
  313. package/dist/download/index.js +19 -46
  314. package/dist/engine.test.d.ts +0 -3
  315. package/dist/engine.test.js +80 -11
  316. package/dist/execution.d.ts +24 -0
  317. package/dist/execution.js +153 -0
  318. package/dist/explore.d.ts +76 -3
  319. package/dist/explore.js +132 -111
  320. package/dist/external-clis.yaml +48 -0
  321. package/dist/external.d.ts +7 -2
  322. package/dist/external.js +11 -14
  323. package/dist/generate.d.ts +41 -2
  324. package/dist/generate.js +5 -4
  325. package/dist/main.js +2 -1
  326. package/dist/pipeline/executor.d.ts +2 -2
  327. package/dist/pipeline/executor.js +2 -2
  328. package/dist/pipeline/executor.test.js +33 -6
  329. package/dist/pipeline/registry.d.ts +1 -1
  330. package/dist/pipeline/steps/browser.d.ts +7 -7
  331. package/dist/pipeline/steps/browser.js +21 -7
  332. package/dist/pipeline/steps/fetch.d.ts +1 -1
  333. package/dist/pipeline/steps/fetch.js +11 -7
  334. package/dist/pipeline/steps/transform.d.ts +6 -5
  335. package/dist/pipeline/steps/transform.js +30 -9
  336. package/dist/pipeline/template.d.ts +6 -6
  337. package/dist/pipeline/template.js +43 -5
  338. package/dist/pipeline/template.test.js +18 -0
  339. package/dist/pipeline/transform.test.js +11 -0
  340. package/dist/plugin.d.ts +31 -0
  341. package/dist/plugin.js +216 -0
  342. package/dist/plugin.test.d.ts +4 -0
  343. package/dist/plugin.test.js +76 -0
  344. package/dist/registry-api.d.ts +11 -0
  345. package/dist/registry-api.js +9 -0
  346. package/dist/registry.d.ts +13 -0
  347. package/dist/registry.js +8 -1
  348. package/dist/runtime.d.ts +5 -0
  349. package/dist/runtime.js +8 -0
  350. package/dist/serialization.d.ts +34 -0
  351. package/dist/serialization.js +63 -0
  352. package/dist/synthesize.d.ts +94 -4
  353. package/dist/synthesize.js +5 -4
  354. package/dist/types.d.ts +43 -27
  355. package/dist/validate.js +8 -2
  356. package/docs/.vitepress/config.mts +20 -7
  357. package/docs/adapters/browser/arxiv.md +27 -0
  358. package/docs/adapters/browser/barchart.md +33 -0
  359. package/docs/adapters/browser/bilibili.md +9 -0
  360. package/docs/adapters/browser/bloomberg.md +70 -0
  361. package/docs/adapters/browser/chaoxing.md +39 -0
  362. package/docs/adapters/browser/devto.md +35 -0
  363. package/docs/adapters/browser/douban.md +38 -0
  364. package/docs/adapters/browser/facebook.md +36 -0
  365. package/docs/adapters/browser/google.md +62 -0
  366. package/docs/adapters/browser/grok.md +53 -0
  367. package/docs/adapters/browser/hf.md +42 -0
  368. package/docs/adapters/browser/instagram.md +46 -0
  369. package/docs/adapters/browser/jike.md +45 -0
  370. package/docs/adapters/browser/jimeng.md +39 -0
  371. package/docs/adapters/browser/linux-do.md +45 -0
  372. package/docs/adapters/browser/lobsters.md +32 -0
  373. package/docs/adapters/browser/medium.md +32 -0
  374. package/docs/adapters/browser/reddit.md +9 -0
  375. package/docs/adapters/browser/sinablog.md +36 -0
  376. package/docs/adapters/browser/sinafinance.md +35 -0
  377. package/docs/adapters/browser/stackoverflow.md +35 -0
  378. package/docs/adapters/browser/steam.md +26 -0
  379. package/docs/adapters/browser/substack.md +38 -0
  380. package/docs/adapters/browser/tiktok.md +68 -0
  381. package/docs/adapters/browser/twitter.md +3 -0
  382. package/docs/adapters/browser/weread.md +48 -0
  383. package/docs/adapters/browser/wikipedia.md +39 -0
  384. package/docs/adapters/browser/xiaohongshu.md +5 -1
  385. package/docs/adapters/browser/xueqiu.md +10 -0
  386. package/docs/adapters/browser/yahoo-finance.md +6 -5
  387. package/docs/adapters/desktop/antigravity.md +6 -0
  388. package/docs/adapters/desktop/chatgpt.md +5 -4
  389. package/docs/adapters/desktop/codex.md +5 -1
  390. package/docs/adapters/desktop/cursor.md +4 -0
  391. package/docs/adapters/desktop/discord.md +7 -7
  392. package/docs/adapters/index.md +14 -4
  393. package/docs/advanced/download.md +4 -4
  394. package/docs/developer/architecture.md +17 -4
  395. package/docs/guide/getting-started.md +1 -0
  396. package/docs/guide/plugins.md +153 -0
  397. package/docs/zh/guide/plugins.md +107 -0
  398. package/extension/src/background.ts +18 -11
  399. package/package.json +10 -5
  400. package/scripts/check-doc-coverage.sh +69 -0
  401. package/scripts/clean-dist.cjs +13 -0
  402. package/scripts/copy-yaml.cjs +7 -0
  403. package/src/browser/cdp.ts +77 -32
  404. package/src/browser/daemon-client.ts +2 -1
  405. package/src/browser/dom-helpers.ts +38 -7
  406. package/src/browser/dom-snapshot.test.ts +249 -0
  407. package/src/browser/dom-snapshot.ts +770 -0
  408. package/src/browser/index.ts +2 -0
  409. package/src/browser/page.ts +57 -20
  410. package/src/build-manifest.test.ts +70 -2
  411. package/src/build-manifest.ts +114 -40
  412. package/src/cli.ts +287 -139
  413. package/src/clis/apple-podcasts/commands.test.ts +95 -0
  414. package/src/clis/apple-podcasts/search.ts +2 -2
  415. package/src/clis/apple-podcasts/top.ts +12 -2
  416. package/src/clis/arxiv/search.ts +1 -1
  417. package/src/clis/barchart/greeks.ts +1 -1
  418. package/src/clis/barchart/options.ts +1 -1
  419. package/src/clis/barchart/quote.ts +1 -1
  420. package/src/clis/bilibili/download.ts +1 -1
  421. package/src/clis/bilibili/dynamic.ts +1 -1
  422. package/src/clis/bilibili/favorite.ts +1 -1
  423. package/src/clis/bilibili/feed.ts +1 -1
  424. package/src/clis/bilibili/following.ts +2 -2
  425. package/src/clis/bilibili/history.ts +1 -1
  426. package/src/clis/bilibili/me.ts +1 -1
  427. package/src/clis/bilibili/ranking.ts +1 -1
  428. package/src/clis/bilibili/search.ts +3 -3
  429. package/src/clis/bilibili/subtitle.ts +2 -2
  430. package/src/clis/bilibili/user-videos.ts +2 -2
  431. package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
  432. package/src/clis/bloomberg/businessweek.ts +18 -0
  433. package/src/clis/bloomberg/economics.ts +18 -0
  434. package/src/clis/bloomberg/feeds.ts +16 -0
  435. package/src/clis/bloomberg/industries.ts +18 -0
  436. package/src/clis/bloomberg/main.ts +18 -0
  437. package/src/clis/bloomberg/markets.ts +18 -0
  438. package/src/clis/bloomberg/news.ts +136 -0
  439. package/src/clis/bloomberg/opinions.ts +18 -0
  440. package/src/clis/bloomberg/politics.ts +18 -0
  441. package/src/clis/bloomberg/tech.ts +18 -0
  442. package/src/clis/bloomberg/utils.test.ts +135 -0
  443. package/src/clis/bloomberg/utils.ts +429 -0
  444. package/src/clis/boss/batchgreet.ts +16 -108
  445. package/src/clis/boss/chatlist.ts +13 -27
  446. package/src/clis/boss/chatmsg.ts +16 -40
  447. package/src/clis/boss/common.ts +287 -0
  448. package/src/clis/boss/detail.ts +9 -55
  449. package/src/clis/boss/exchange.ts +15 -89
  450. package/src/clis/boss/greet.ts +25 -162
  451. package/src/clis/boss/invite.ts +36 -133
  452. package/src/clis/boss/joblist.ts +7 -36
  453. package/src/clis/boss/mark.ts +13 -94
  454. package/src/clis/boss/recommend.ts +12 -57
  455. package/src/clis/boss/resume.ts +19 -124
  456. package/src/clis/boss/search.ts +14 -67
  457. package/src/clis/boss/send.ts +22 -162
  458. package/src/clis/boss/stats.ts +21 -76
  459. package/src/clis/chaoxing/assignments.ts +1 -1
  460. package/src/clis/chaoxing/exams.ts +1 -1
  461. package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
  462. package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
  463. package/src/clis/chatgpt/read.ts +1 -1
  464. package/src/clis/chatwise/export.ts +1 -1
  465. package/src/clis/chatwise/model.ts +2 -2
  466. package/src/clis/chatwise/screenshot.ts +1 -1
  467. package/src/clis/codex/export.ts +1 -1
  468. package/src/clis/codex/model.ts +2 -2
  469. package/src/clis/codex/screenshot.ts +1 -1
  470. package/src/clis/coupang/add-to-cart.ts +3 -4
  471. package/src/clis/coupang/search.ts +2 -4
  472. package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
  473. package/src/clis/ctrip/search.ts +1 -1
  474. package/src/clis/cursor/export.ts +1 -1
  475. package/src/clis/cursor/model.ts +2 -2
  476. package/src/clis/cursor/screenshot.ts +1 -1
  477. package/src/clis/devto/tag.yaml +34 -0
  478. package/src/clis/devto/top.yaml +29 -0
  479. package/src/clis/devto/user.yaml +33 -0
  480. package/src/clis/douban/book-hot.ts +15 -0
  481. package/src/clis/douban/marks.ts +135 -0
  482. package/src/clis/douban/movie-hot.ts +15 -0
  483. package/src/clis/douban/reviews.ts +127 -0
  484. package/src/clis/douban/search.ts +17 -0
  485. package/src/clis/douban/shared.ts +165 -0
  486. package/src/clis/douban/subject.yaml +76 -0
  487. package/src/clis/douban/top250.yaml +70 -0
  488. package/src/clis/douban/utils.ts +81 -0
  489. package/src/clis/facebook/add-friend.yaml +43 -0
  490. package/src/clis/facebook/events.yaml +44 -0
  491. package/src/clis/facebook/feed.yaml +63 -0
  492. package/src/clis/facebook/friends.yaml +42 -0
  493. package/src/clis/facebook/groups.yaml +50 -0
  494. package/src/clis/facebook/join-group.yaml +44 -0
  495. package/src/clis/facebook/memories.yaml +39 -0
  496. package/src/clis/facebook/notifications.yaml +40 -0
  497. package/src/clis/facebook/profile.yaml +37 -0
  498. package/src/clis/facebook/search.yaml +46 -0
  499. package/src/clis/google/news.ts +66 -0
  500. package/src/clis/google/search.ts +133 -0
  501. package/src/clis/google/suggest.ts +40 -0
  502. package/src/clis/google/trends.ts +44 -0
  503. package/src/clis/google/utils.test.ts +82 -0
  504. package/src/clis/google/utils.ts +24 -0
  505. package/src/clis/grok/ask.test.ts +53 -0
  506. package/src/clis/grok/ask.ts +300 -69
  507. package/src/clis/instagram/comment.yaml +52 -0
  508. package/src/clis/instagram/explore.yaml +43 -0
  509. package/src/clis/instagram/follow.yaml +41 -0
  510. package/src/clis/instagram/followers.yaml +51 -0
  511. package/src/clis/instagram/following.yaml +51 -0
  512. package/src/clis/instagram/like.yaml +46 -0
  513. package/src/clis/instagram/profile.yaml +42 -0
  514. package/src/clis/instagram/save.yaml +46 -0
  515. package/src/clis/instagram/saved.yaml +40 -0
  516. package/src/clis/instagram/search.yaml +43 -0
  517. package/src/clis/instagram/unfollow.yaml +38 -0
  518. package/src/clis/instagram/unlike.yaml +46 -0
  519. package/src/clis/instagram/unsave.yaml +46 -0
  520. package/src/clis/instagram/user.yaml +54 -0
  521. package/src/clis/jike/comment.ts +2 -3
  522. package/src/clis/jike/create.ts +1 -2
  523. package/src/clis/jike/feed.ts +0 -1
  524. package/src/clis/jike/like.ts +1 -2
  525. package/src/clis/jike/notifications.ts +0 -1
  526. package/src/clis/jike/post.yaml +1 -0
  527. package/src/clis/jike/repost.ts +2 -3
  528. package/src/clis/jike/search.ts +2 -3
  529. package/src/clis/jike/topic.yaml +1 -0
  530. package/src/clis/jike/user.yaml +1 -0
  531. package/src/clis/jimeng/generate.yaml +1 -0
  532. package/src/clis/jimeng/history.yaml +0 -1
  533. package/src/clis/linkedin/search.ts +7 -7
  534. package/src/clis/linux-do/category.yaml +2 -0
  535. package/src/clis/linux-do/search.yaml +4 -3
  536. package/src/clis/linux-do/topic.yaml +1 -0
  537. package/src/clis/lobsters/active.yaml +29 -0
  538. package/src/clis/lobsters/hot.yaml +29 -0
  539. package/src/clis/lobsters/newest.yaml +29 -0
  540. package/src/clis/lobsters/tag.yaml +34 -0
  541. package/src/clis/medium/feed.ts +16 -0
  542. package/src/clis/medium/search.ts +16 -0
  543. package/src/clis/medium/shared.ts +83 -0
  544. package/src/clis/medium/user.ts +16 -0
  545. package/src/clis/notion/export.ts +1 -1
  546. package/src/clis/reddit/comment.ts +3 -4
  547. package/src/clis/reddit/read.ts +4 -5
  548. package/src/clis/reddit/save.ts +2 -3
  549. package/src/clis/reddit/saved.ts +0 -1
  550. package/src/clis/reddit/search.yaml +1 -0
  551. package/src/clis/reddit/subreddit.yaml +1 -0
  552. package/src/clis/reddit/subscribe.ts +1 -2
  553. package/src/clis/reddit/upvote.ts +2 -3
  554. package/src/clis/reddit/upvoted.ts +0 -1
  555. package/src/clis/reddit/user-comments.yaml +1 -0
  556. package/src/clis/reddit/user-posts.yaml +1 -0
  557. package/src/clis/reddit/user.yaml +1 -0
  558. package/src/clis/reuters/search.ts +1 -1
  559. package/src/clis/sinablog/article.ts +15 -0
  560. package/src/clis/sinablog/hot.ts +15 -0
  561. package/src/clis/sinablog/search.ts +56 -0
  562. package/src/clis/sinablog/shared.ts +198 -0
  563. package/src/clis/sinablog/user.ts +16 -0
  564. package/src/clis/smzdm/search.ts +2 -3
  565. package/src/clis/stackoverflow/search.yaml +1 -0
  566. package/src/clis/steam/top-sellers.yaml +29 -0
  567. package/src/clis/substack/feed.ts +16 -0
  568. package/src/clis/substack/publication.ts +16 -0
  569. package/src/clis/substack/search.ts +91 -0
  570. package/src/clis/substack/shared.ts +132 -0
  571. package/src/clis/tiktok/comment.yaml +66 -0
  572. package/src/clis/tiktok/explore.yaml +39 -0
  573. package/src/clis/tiktok/follow.yaml +39 -0
  574. package/src/clis/tiktok/following.yaml +46 -0
  575. package/src/clis/tiktok/friends.yaml +47 -0
  576. package/src/clis/tiktok/like.yaml +38 -0
  577. package/src/clis/tiktok/live.yaml +51 -0
  578. package/src/clis/tiktok/notifications.yaml +52 -0
  579. package/src/clis/tiktok/profile.yaml +45 -0
  580. package/src/clis/tiktok/save.yaml +34 -0
  581. package/src/clis/tiktok/search.yaml +46 -0
  582. package/src/clis/tiktok/unfollow.yaml +44 -0
  583. package/src/clis/tiktok/unlike.yaml +38 -0
  584. package/src/clis/tiktok/unsave.yaml +36 -0
  585. package/src/clis/tiktok/user.yaml +44 -0
  586. package/src/clis/twitter/accept.ts +2 -2
  587. package/src/clis/twitter/article.ts +2 -2
  588. package/src/clis/twitter/block.ts +92 -0
  589. package/src/clis/twitter/delete.ts +1 -1
  590. package/src/clis/twitter/download.ts +3 -3
  591. package/src/clis/twitter/followers.ts +1 -1
  592. package/src/clis/twitter/following.ts +1 -1
  593. package/src/clis/twitter/hide-reply.ts +70 -0
  594. package/src/clis/twitter/like.ts +1 -1
  595. package/src/clis/twitter/post.ts +1 -1
  596. package/src/clis/twitter/reply-dm.ts +1 -1
  597. package/src/clis/twitter/reply.ts +2 -2
  598. package/src/clis/twitter/search.ts +1 -1
  599. package/src/clis/twitter/thread.ts +2 -2
  600. package/src/clis/twitter/timeline.test.ts +109 -0
  601. package/src/clis/twitter/timeline.ts +59 -19
  602. package/src/clis/twitter/trending.ts +113 -0
  603. package/src/clis/twitter/unblock.ts +75 -0
  604. package/src/clis/v2ex/topic.yaml +1 -0
  605. package/src/clis/weibo/hot.ts +0 -1
  606. package/src/clis/weread/book.ts +1 -1
  607. package/src/clis/weread/highlights.ts +1 -1
  608. package/src/clis/weread/notes.ts +1 -1
  609. package/src/clis/weread/search.ts +1 -1
  610. package/src/clis/wikipedia/random.ts +19 -0
  611. package/src/clis/wikipedia/search.ts +11 -5
  612. package/src/clis/wikipedia/summary.ts +4 -9
  613. package/src/clis/wikipedia/trending.ts +41 -0
  614. package/src/clis/wikipedia/utils.ts +31 -0
  615. package/src/clis/xiaohongshu/creator-note-detail.test.ts +84 -33
  616. package/src/clis/xiaohongshu/creator-note-detail.ts +89 -5
  617. package/src/clis/xiaohongshu/creator-notes.test.ts +41 -6
  618. package/src/clis/xiaohongshu/creator-notes.ts +44 -5
  619. package/src/clis/xiaohongshu/creator-profile.ts +0 -1
  620. package/src/clis/xiaohongshu/creator-stats.ts +0 -1
  621. package/src/clis/xiaohongshu/download.ts +2 -3
  622. package/src/clis/xiaohongshu/feed.yaml +0 -1
  623. package/src/clis/xiaohongshu/notifications.yaml +0 -1
  624. package/src/clis/xiaohongshu/search.ts +2 -2
  625. package/src/clis/xiaohongshu/user.ts +1 -2
  626. package/src/clis/xueqiu/earnings-date.yaml +69 -0
  627. package/src/clis/xueqiu/search.yaml +2 -1
  628. package/src/clis/xueqiu/stock.yaml +2 -0
  629. package/src/clis/yahoo-finance/quote.ts +1 -2
  630. package/src/clis/youtube/search.ts +1 -1
  631. package/src/clis/youtube/transcript.ts +1 -1
  632. package/src/clis/youtube/video.ts +1 -1
  633. package/src/clis/zhihu/download.ts +1 -2
  634. package/src/clis/zhihu/question.ts +1 -1
  635. package/src/clis/zhihu/search.yaml +4 -3
  636. package/src/commanderAdapter.ts +120 -0
  637. package/src/discovery.ts +277 -0
  638. package/src/doctor.test.ts +59 -2
  639. package/src/doctor.ts +4 -2
  640. package/src/download/index.ts +21 -54
  641. package/src/engine.test.ts +85 -11
  642. package/src/execution.ts +164 -0
  643. package/src/explore.ts +211 -117
  644. package/src/external-clis.yaml +9 -0
  645. package/src/external.ts +15 -12
  646. package/src/generate.ts +58 -9
  647. package/src/main.ts +2 -1
  648. package/src/pipeline/executor.test.ts +35 -6
  649. package/src/pipeline/executor.ts +11 -7
  650. package/src/pipeline/registry.ts +3 -3
  651. package/src/pipeline/steps/browser.ts +29 -15
  652. package/src/pipeline/steps/fetch.ts +18 -13
  653. package/src/pipeline/steps/transform.ts +40 -15
  654. package/src/pipeline/template.test.ts +18 -0
  655. package/src/pipeline/template.ts +86 -13
  656. package/src/pipeline/transform.test.ts +15 -2
  657. package/src/plugin.test.ts +86 -0
  658. package/src/plugin.ts +254 -0
  659. package/src/registry-api.ts +12 -0
  660. package/src/registry.ts +24 -1
  661. package/src/runtime.ts +9 -0
  662. package/src/serialization.ts +79 -0
  663. package/src/synthesize.ts +102 -21
  664. package/src/types.ts +45 -13
  665. package/src/validate.ts +19 -4
  666. package/tests/e2e/browser-public.test.ts +36 -0
  667. package/tests/e2e/public-commands.test.ts +119 -1
  668. package/dist/clis/feishu/new.d.ts +0 -1
  669. package/dist/clis/feishu/new.js +0 -27
  670. package/dist/clis/feishu/read.d.ts +0 -1
  671. package/dist/clis/feishu/read.js +0 -40
  672. package/dist/clis/feishu/search.d.ts +0 -1
  673. package/dist/clis/feishu/search.js +0 -30
  674. package/dist/clis/feishu/send.d.ts +0 -1
  675. package/dist/clis/feishu/send.js +0 -39
  676. package/dist/clis/feishu/status.d.ts +0 -1
  677. package/dist/clis/feishu/status.js +0 -28
  678. package/dist/clis/neteasemusic/like.d.ts +0 -1
  679. package/dist/clis/neteasemusic/like.js +0 -25
  680. package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
  681. package/dist/clis/neteasemusic/lyrics.js +0 -47
  682. package/dist/clis/neteasemusic/next.d.ts +0 -1
  683. package/dist/clis/neteasemusic/next.js +0 -26
  684. package/dist/clis/neteasemusic/play.d.ts +0 -1
  685. package/dist/clis/neteasemusic/play.js +0 -26
  686. package/dist/clis/neteasemusic/playing.d.ts +0 -1
  687. package/dist/clis/neteasemusic/playing.js +0 -59
  688. package/dist/clis/neteasemusic/playlist.d.ts +0 -1
  689. package/dist/clis/neteasemusic/playlist.js +0 -46
  690. package/dist/clis/neteasemusic/prev.d.ts +0 -1
  691. package/dist/clis/neteasemusic/prev.js +0 -25
  692. package/dist/clis/neteasemusic/search.d.ts +0 -1
  693. package/dist/clis/neteasemusic/search.js +0 -52
  694. package/dist/clis/neteasemusic/status.d.ts +0 -1
  695. package/dist/clis/neteasemusic/status.js +0 -16
  696. package/dist/clis/neteasemusic/volume.d.ts +0 -1
  697. package/dist/clis/neteasemusic/volume.js +0 -54
  698. package/dist/clis/twitter/trending.yaml +0 -46
  699. package/dist/clis/wechat/chats.d.ts +0 -1
  700. package/dist/clis/wechat/chats.js +0 -28
  701. package/dist/clis/wechat/contacts.d.ts +0 -1
  702. package/dist/clis/wechat/contacts.js +0 -28
  703. package/dist/clis/wechat/read.d.ts +0 -1
  704. package/dist/clis/wechat/read.js +0 -58
  705. package/dist/clis/wechat/search.d.ts +0 -1
  706. package/dist/clis/wechat/search.js +0 -31
  707. package/dist/clis/wechat/send.d.ts +0 -1
  708. package/dist/clis/wechat/send.js +0 -42
  709. package/dist/clis/wechat/status.d.ts +0 -1
  710. package/dist/clis/wechat/status.js +0 -29
  711. package/dist/pipeline.d.ts +0 -7
  712. package/dist/pipeline.js +0 -7
  713. package/docs/adapters/browser/github.md +0 -26
  714. package/docs/adapters/desktop/feishu.md +0 -20
  715. package/docs/adapters/desktop/neteasemusic.md +0 -31
  716. package/docs/adapters/desktop/wechat.md +0 -28
  717. package/docs/public/CNAME +0 -1
  718. package/src/clis/antigravity/README.md +0 -5
  719. package/src/clis/antigravity/README.zh-CN.md +0 -51
  720. package/src/clis/chaoxing/README.md +0 -14
  721. package/src/clis/chaoxing/README.zh-CN.md +0 -35
  722. package/src/clis/chatgpt/README.md +0 -5
  723. package/src/clis/chatgpt/README.zh-CN.md +0 -44
  724. package/src/clis/chatwise/README.md +0 -5
  725. package/src/clis/chatwise/README.zh-CN.md +0 -38
  726. package/src/clis/codex/README.md +0 -5
  727. package/src/clis/codex/README.zh-CN.md +0 -33
  728. package/src/clis/cursor/README.md +0 -5
  729. package/src/clis/cursor/README.zh-CN.md +0 -33
  730. package/src/clis/discord-app/README.md +0 -5
  731. package/src/clis/discord-app/README.zh-CN.md +0 -28
  732. package/src/clis/feishu/README.md +0 -5
  733. package/src/clis/feishu/README.zh-CN.md +0 -20
  734. package/src/clis/feishu/new.ts +0 -32
  735. package/src/clis/feishu/read.ts +0 -48
  736. package/src/clis/feishu/search.ts +0 -35
  737. package/src/clis/feishu/send.ts +0 -46
  738. package/src/clis/feishu/status.ts +0 -34
  739. package/src/clis/neteasemusic/README.md +0 -5
  740. package/src/clis/neteasemusic/README.zh-CN.md +0 -31
  741. package/src/clis/neteasemusic/like.ts +0 -28
  742. package/src/clis/neteasemusic/lyrics.ts +0 -53
  743. package/src/clis/neteasemusic/next.ts +0 -30
  744. package/src/clis/neteasemusic/play.ts +0 -30
  745. package/src/clis/neteasemusic/playing.ts +0 -62
  746. package/src/clis/neteasemusic/playlist.ts +0 -51
  747. package/src/clis/neteasemusic/prev.ts +0 -29
  748. package/src/clis/neteasemusic/search.ts +0 -58
  749. package/src/clis/neteasemusic/status.ts +0 -18
  750. package/src/clis/neteasemusic/volume.ts +0 -61
  751. package/src/clis/notion/README.md +0 -5
  752. package/src/clis/notion/README.zh-CN.md +0 -29
  753. package/src/clis/twitter/trending.yaml +0 -46
  754. package/src/clis/wechat/README.md +0 -5
  755. package/src/clis/wechat/README.zh-CN.md +0 -28
  756. package/src/clis/wechat/chats.ts +0 -33
  757. package/src/clis/wechat/contacts.ts +0 -33
  758. package/src/clis/wechat/read.ts +0 -72
  759. package/src/clis/wechat/search.ts +0 -36
  760. package/src/clis/wechat/send.ts +0 -49
  761. package/src/clis/wechat/status.ts +0 -35
  762. package/src/engine.ts +0 -274
  763. package/src/pipeline.ts +0 -8
  764. /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
  765. /package/dist/{chaoxing.test.d.ts → clis/bloomberg/businessweek.d.ts} +0 -0
  766. /package/dist/{coupang.test.d.ts → clis/bloomberg/economics.d.ts} +0 -0
  767. /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
  768. /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
  769. /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tests for dom-snapshot.ts: DOM snapshot engine.
3
+ *
4
+ * Since the engine generates JavaScript strings for in-page evaluation,
5
+ * these tests validate:
6
+ * 1. The generated code is syntactically valid JS
7
+ * 2. Options are correctly embedded
8
+ * 3. The output structure matches expected format
9
+ * 4. All features are present (Shadow DOM, iframe, table, diff, etc.)
10
+ */
11
+ export {};
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Tests for dom-snapshot.ts: DOM snapshot engine.
3
+ *
4
+ * Since the engine generates JavaScript strings for in-page evaluation,
5
+ * these tests validate:
6
+ * 1. The generated code is syntactically valid JS
7
+ * 2. Options are correctly embedded
8
+ * 3. The output structure matches expected format
9
+ * 4. All features are present (Shadow DOM, iframe, table, diff, etc.)
10
+ */
11
+ import { describe, it, expect } from 'vitest';
12
+ import { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
13
+ describe('generateSnapshotJs', () => {
14
+ it('returns a non-empty string', () => {
15
+ const js = generateSnapshotJs();
16
+ expect(typeof js).toBe('string');
17
+ expect(js.length).toBeGreaterThan(100);
18
+ });
19
+ it('generates syntactically valid JS (can be parsed)', () => {
20
+ const js = generateSnapshotJs();
21
+ expect(() => new Function(js)).not.toThrow();
22
+ });
23
+ it('embeds default options correctly', () => {
24
+ const js = generateSnapshotJs();
25
+ expect(js).toContain('VIEWPORT_EXPAND = 800');
26
+ expect(js).toContain('MAX_DEPTH = 50');
27
+ expect(js).toContain('INTERACTIVE_ONLY = false');
28
+ expect(js).toContain('MAX_TEXT_LEN = 120');
29
+ expect(js).toContain('INCLUDE_SCROLL_INFO = true');
30
+ expect(js).toContain('BBOX_DEDUP = true');
31
+ expect(js).toContain('INCLUDE_SHADOW_DOM = true');
32
+ expect(js).toContain('INCLUDE_IFRAMES = true');
33
+ expect(js).toContain('PAINT_ORDER_CHECK = true');
34
+ expect(js).toContain('ANNOTATE_REFS = true');
35
+ expect(js).toContain('REPORT_HIDDEN = true');
36
+ expect(js).toContain('FILTER_ADS = true');
37
+ expect(js).toContain('MARKDOWN_TABLES = true');
38
+ expect(js).toContain('PREV_HASHES = null');
39
+ });
40
+ it('embeds custom options correctly', () => {
41
+ const js = generateSnapshotJs({
42
+ viewportExpand: 2000,
43
+ maxDepth: 30,
44
+ interactiveOnly: true,
45
+ maxTextLength: 200,
46
+ includeScrollInfo: false,
47
+ bboxDedup: false,
48
+ includeShadowDom: false,
49
+ includeIframes: false,
50
+ maxIframes: 3,
51
+ paintOrderCheck: false,
52
+ annotateRefs: false,
53
+ reportHidden: false,
54
+ filterAds: false,
55
+ markdownTables: false,
56
+ });
57
+ expect(js).toContain('VIEWPORT_EXPAND = 2000');
58
+ expect(js).toContain('MAX_DEPTH = 30');
59
+ expect(js).toContain('INTERACTIVE_ONLY = true');
60
+ expect(js).toContain('MAX_TEXT_LEN = 200');
61
+ expect(js).toContain('INCLUDE_SCROLL_INFO = false');
62
+ expect(js).toContain('BBOX_DEDUP = false');
63
+ expect(js).toContain('INCLUDE_SHADOW_DOM = false');
64
+ expect(js).toContain('INCLUDE_IFRAMES = false');
65
+ expect(js).toContain('MAX_IFRAMES = 3');
66
+ expect(js).toContain('PAINT_ORDER_CHECK = false');
67
+ expect(js).toContain('ANNOTATE_REFS = false');
68
+ expect(js).toContain('REPORT_HIDDEN = false');
69
+ expect(js).toContain('FILTER_ADS = false');
70
+ expect(js).toContain('MARKDOWN_TABLES = false');
71
+ });
72
+ it('clamps maxDepth between 1 and 200', () => {
73
+ expect(generateSnapshotJs({ maxDepth: -5 })).toContain('MAX_DEPTH = 1');
74
+ expect(generateSnapshotJs({ maxDepth: 999 })).toContain('MAX_DEPTH = 200');
75
+ expect(generateSnapshotJs({ maxDepth: 75 })).toContain('MAX_DEPTH = 75');
76
+ });
77
+ it('wraps output as an IIFE', () => {
78
+ const js = generateSnapshotJs();
79
+ expect(js.startsWith('(() =>')).toBe(true);
80
+ expect(js.trimEnd().endsWith(')()')).toBe(true);
81
+ });
82
+ it('embeds previousHashes for incremental diff', () => {
83
+ const hashes = JSON.stringify(['12345', '67890']);
84
+ const js = generateSnapshotJs({ previousHashes: hashes });
85
+ expect(js).toContain('new Set(["12345","67890"])');
86
+ });
87
+ it('includes all core features in generated code', () => {
88
+ const js = generateSnapshotJs();
89
+ // Tag filtering
90
+ expect(js).toContain('SKIP_TAGS');
91
+ expect(js).toContain("'script'");
92
+ expect(js).toContain("'style'");
93
+ // SVG collapsing
94
+ expect(js).toContain('SVG_CHILDREN');
95
+ // Interactive detection
96
+ expect(js).toContain('INTERACTIVE_TAGS');
97
+ expect(js).toContain('INTERACTIVE_ROLES');
98
+ expect(js).toContain('isInteractive');
99
+ // Visibility
100
+ expect(js).toContain('isVisibleByCSS');
101
+ expect(js).toContain('isInExpandedViewport');
102
+ // BBox dedup
103
+ expect(js).toContain('isContainedBy');
104
+ expect(js).toContain('PROPAGATING_TAGS');
105
+ // Shadow DOM
106
+ expect(js).toContain('shadowRoot');
107
+ expect(js).toContain('|shadow|');
108
+ // iframe
109
+ expect(js).toContain('walkIframe');
110
+ expect(js).toContain('|iframe|');
111
+ // Paint order
112
+ expect(js).toContain('isOccludedByOverlay');
113
+ expect(js).toContain('elementFromPoint');
114
+ // Ad filtering
115
+ expect(js).toContain('isAdElement');
116
+ expect(js).toContain('AD_PATTERNS');
117
+ // data-ref annotation
118
+ expect(js).toContain('data-opencli-ref');
119
+ // Hidden elements report
120
+ expect(js).toContain('hiddenInteractives');
121
+ expect(js).toContain('hidden_interactive');
122
+ // Incremental diff
123
+ expect(js).toContain('hashElement');
124
+ expect(js).toContain('currentHashes');
125
+ expect(js).toContain('__opencli_prev_hashes');
126
+ // Table serialization
127
+ expect(js).toContain('serializeTable');
128
+ expect(js).toContain('|table|');
129
+ // Synthetic attributes
130
+ expect(js).toContain("'YYYY-MM-DD'");
131
+ expect(js).toContain('value=••••');
132
+ // Page metadata
133
+ expect(js).toContain('location.href');
134
+ expect(js).toContain('document.title');
135
+ });
136
+ it('contains proper attribute whitelist', () => {
137
+ const js = generateSnapshotJs();
138
+ const expectedAttrs = [
139
+ 'aria-label', 'aria-expanded', 'aria-checked', 'aria-selected',
140
+ 'placeholder', 'href', 'role', 'data-testid', 'autocomplete',
141
+ ];
142
+ for (const attr of expectedAttrs) {
143
+ expect(js).toContain(`'${attr}'`);
144
+ }
145
+ });
146
+ it('includes scroll info formatting', () => {
147
+ const js = generateSnapshotJs();
148
+ expect(js).toContain('scrollHeight');
149
+ expect(js).toContain('scrollTop');
150
+ expect(js).toContain('|scroll|');
151
+ expect(js).toContain('page_scroll');
152
+ });
153
+ });
154
+ describe('scrollToRefJs', () => {
155
+ it('generates valid JS', () => {
156
+ const js = scrollToRefJs('42');
157
+ expect(() => new Function(js)).not.toThrow();
158
+ });
159
+ it('targets data-opencli-ref', () => {
160
+ const js = scrollToRefJs('7');
161
+ expect(js).toContain('data-opencli-ref');
162
+ expect(js).toContain('scrollIntoView');
163
+ expect(js).toContain('"7"');
164
+ });
165
+ it('falls back to data-ref', () => {
166
+ const js = scrollToRefJs('3');
167
+ expect(js).toContain('data-ref');
168
+ });
169
+ it('returns scrolled info', () => {
170
+ const js = scrollToRefJs('1');
171
+ expect(js).toContain('scrolled: true');
172
+ expect(js).toContain('tag:');
173
+ });
174
+ });
175
+ describe('getFormStateJs', () => {
176
+ it('generates valid JS', () => {
177
+ const js = getFormStateJs();
178
+ expect(() => new Function(js)).not.toThrow();
179
+ });
180
+ it('collects form elements', () => {
181
+ const js = getFormStateJs();
182
+ expect(js).toContain('document.forms');
183
+ expect(js).toContain('form.elements');
184
+ });
185
+ it('collects orphan fields', () => {
186
+ const js = getFormStateJs();
187
+ expect(js).toContain('orphanFields');
188
+ expect(js).toContain('el.form');
189
+ });
190
+ it('handles different input types', () => {
191
+ const js = getFormStateJs();
192
+ expect(js).toContain('checkbox');
193
+ expect(js).toContain('radio');
194
+ expect(js).toContain('password');
195
+ expect(js).toContain('contenteditable');
196
+ });
197
+ it('extracts labels', () => {
198
+ const js = getFormStateJs();
199
+ expect(js).toContain('aria-label');
200
+ expect(js).toContain('label[for=');
201
+ expect(js).toContain('closest');
202
+ expect(js).toContain('placeholder');
203
+ });
204
+ it('masks passwords', () => {
205
+ const js = getFormStateJs();
206
+ expect(js).toContain('••••');
207
+ });
208
+ it('includes data-opencli-ref in output', () => {
209
+ const js = getFormStateJs();
210
+ expect(js).toContain('data-opencli-ref');
211
+ });
212
+ });
@@ -8,6 +8,8 @@ export { Page } from './page.js';
8
8
  export { BrowserBridge, BrowserBridge as PlaywrightMCP } from './mcp.js';
9
9
  export { CDPBridge } from './cdp.js';
10
10
  export { isDaemonRunning } from './daemon-client.js';
11
+ export { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
12
+ export type { SnapshotOptions } from './dom-snapshot.js';
11
13
  import { extractTabEntries, diffTabIndexes, appendLimited } from './tabs.js';
12
14
  import { withTimeoutMs } from '../runtime.js';
13
15
  export declare const __test__: {
@@ -8,6 +8,7 @@ export { Page } from './page.js';
8
8
  export { BrowserBridge, BrowserBridge as PlaywrightMCP } from './mcp.js';
9
9
  export { CDPBridge } from './cdp.js';
10
10
  export { isDaemonRunning } from './daemon-client.js';
11
+ export { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
11
12
  import { extractTabEntries, diffTabIndexes, appendLimited } from './tabs.js';
12
13
  import { __test__ as cdpTest } from './cdp.js';
13
14
  import { withTimeoutMs } from '../runtime.js';
@@ -9,7 +9,7 @@
9
9
  * where resolveTabId() in the extension picks a chrome:// or
10
10
  * chrome-extension:// tab that can't be debugged.
11
11
  */
12
- import type { IPage } from '../types.js';
12
+ import type { BrowserCookie, IPage, ScreenshotOptions, SnapshotOptions, WaitOptions } from '../types.js';
13
13
  /**
14
14
  * Page — implements IPage by talking to the daemon via HTTP.
15
15
  */
@@ -21,39 +21,37 @@ export declare class Page implements IPage {
21
21
  /** Helper: spread tabId into command params if we have one */
22
22
  private _tabOpt;
23
23
  private _workspaceOpt;
24
- goto(url: string): Promise<void>;
24
+ goto(url: string, options?: {
25
+ waitUntil?: 'load' | 'none';
26
+ settleMs?: number;
27
+ }): Promise<void>;
25
28
  /** Close the automation window in the extension */
26
29
  closeWindow(): Promise<void>;
27
- evaluate(js: string): Promise<any>;
30
+ evaluate(js: string): Promise<unknown>;
28
31
  getCookies(opts?: {
29
32
  domain?: string;
30
33
  url?: string;
31
- }): Promise<any[]>;
32
- snapshot(opts?: {
33
- interactive?: boolean;
34
- compact?: boolean;
35
- maxDepth?: number;
36
- raw?: boolean;
37
- }): Promise<any>;
34
+ }): Promise<BrowserCookie[]>;
35
+ snapshot(opts?: SnapshotOptions): Promise<unknown>;
36
+ /** Fallback basic snapshot — original buildTree approach */
37
+ private _basicSnapshot;
38
38
  click(ref: string): Promise<void>;
39
39
  typeText(ref: string, text: string): Promise<void>;
40
40
  pressKey(key: string): Promise<void>;
41
- wait(options: number | {
42
- text?: string;
43
- time?: number;
44
- timeout?: number;
45
- }): Promise<void>;
46
- tabs(): Promise<any>;
41
+ scrollTo(ref: string): Promise<unknown>;
42
+ getFormState(): Promise<Record<string, unknown>>;
43
+ wait(options: number | WaitOptions): Promise<void>;
44
+ tabs(): Promise<unknown[]>;
47
45
  closeTab(index?: number): Promise<void>;
48
46
  newTab(): Promise<void>;
49
47
  selectTab(index: number): Promise<void>;
50
- networkRequests(includeStatic?: boolean): Promise<any>;
48
+ networkRequests(includeStatic?: boolean): Promise<unknown[]>;
51
49
  /**
52
50
  * Console messages are not available in lightweight daemon mode.
53
51
  * Would require CDP Runtime.consoleAPICalled event listener.
54
52
  * @returns Always returns empty array.
55
53
  */
56
- consoleMessages(_level?: string): Promise<any>;
54
+ consoleMessages(_level?: string): Promise<unknown[]>;
57
55
  /**
58
56
  * Capture a screenshot via CDP Page.captureScreenshot.
59
57
  * @param options.format - 'png' (default) or 'jpeg'
@@ -61,17 +59,12 @@ export declare class Page implements IPage {
61
59
  * @param options.fullPage - capture full scrollable page
62
60
  * @param options.path - save to file path (returns base64 if omitted)
63
61
  */
64
- screenshot(options?: {
65
- format?: 'png' | 'jpeg';
66
- quality?: number;
67
- fullPage?: boolean;
68
- path?: string;
69
- }): Promise<string>;
62
+ screenshot(options?: ScreenshotOptions): Promise<string>;
70
63
  scroll(direction?: string, amount?: number): Promise<void>;
71
64
  autoScroll(options?: {
72
65
  times?: number;
73
66
  delayMs?: number;
74
67
  }): Promise<void>;
75
68
  installInterceptor(pattern: string): Promise<void>;
76
- getInterceptedRequests(): Promise<any[]>;
69
+ getInterceptedRequests(): Promise<unknown[]>;
77
70
  }
@@ -12,6 +12,7 @@
12
12
  import { formatSnapshot } from '../snapshotFormatter.js';
13
13
  import { sendCommand } from './daemon-client.js';
14
14
  import { wrapForEval } from './utils.js';
15
+ import { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
15
16
  import { clickJs, typeTextJs, pressKeyJs, waitForTextJs, scrollJs, autoScrollJs, networkRequestsJs, } from './dom-helpers.js';
16
17
  /**
17
18
  * Page — implements IPage by talking to the daemon via HTTP.
@@ -30,7 +31,7 @@ export class Page {
30
31
  _workspaceOpt() {
31
32
  return { workspace: this.workspace };
32
33
  }
33
- async goto(url) {
34
+ async goto(url, options) {
34
35
  const result = await sendCommand('navigate', {
35
36
  url,
36
37
  ...this._workspaceOpt(),
@@ -40,6 +41,12 @@ export class Page {
40
41
  if (result?.tabId) {
41
42
  this._tabId = result.tabId;
42
43
  }
44
+ // Post-load settle: the extension already waits for tab.status === 'complete',
45
+ // but SPA frameworks (React/Vue) need extra time to render after DOM load.
46
+ if (options?.waitUntil !== 'none') {
47
+ const settleMs = options?.settleMs ?? 1000;
48
+ await new Promise(resolve => setTimeout(resolve, settleMs));
49
+ }
43
50
  }
44
51
  /** Close the automation window in the extension */
45
52
  async closeWindow() {
@@ -59,6 +66,28 @@ export class Page {
59
66
  return Array.isArray(result) ? result : [];
60
67
  }
61
68
  async snapshot(opts = {}) {
69
+ // Primary: use the advanced DOM snapshot engine with multi-layer pruning
70
+ const snapshotJs = generateSnapshotJs({
71
+ viewportExpand: opts.viewportExpand ?? 800,
72
+ maxDepth: Math.max(1, Math.min(Number(opts.maxDepth) || 50, 200)),
73
+ interactiveOnly: opts.interactive ?? false,
74
+ maxTextLength: opts.maxTextLength ?? 120,
75
+ includeScrollInfo: true,
76
+ bboxDedup: true,
77
+ });
78
+ try {
79
+ const result = await sendCommand('exec', { code: snapshotJs, ...this._workspaceOpt(), ...this._tabOpt() });
80
+ // The advanced engine already produces a clean, pruned, LLM-friendly output.
81
+ // Do NOT pass through formatSnapshot — its format is incompatible.
82
+ return result;
83
+ }
84
+ catch {
85
+ // Fallback: basic DOM snapshot (original implementation)
86
+ return this._basicSnapshot(opts);
87
+ }
88
+ }
89
+ /** Fallback basic snapshot — original buildTree approach */
90
+ async _basicSnapshot(opts = {}) {
62
91
  const maxDepth = Math.max(1, Math.min(Number(opts.maxDepth) || 50, 200));
63
92
  const code = `
64
93
  (async () => {
@@ -72,7 +101,7 @@ export class Page {
72
101
 
73
102
  let indent = ' '.repeat(depth);
74
103
  let line = indent + role;
75
- if (name) line += ' "' + name.replace(/"/g, '\\\\"') + '"';
104
+ if (name) line += ' "' + name.replace(/"/g, '\\\\\\"') + '"';
76
105
  if (node.tagName?.toLowerCase() === 'a' && node.href) line += ' [' + node.href + ']';
77
106
  if (node.tagName?.toLowerCase() === 'input') line += ' [' + (node.type || 'text') + ']';
78
107
 
@@ -106,6 +135,14 @@ export class Page {
106
135
  const code = pressKeyJs(key);
107
136
  await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
108
137
  }
138
+ async scrollTo(ref) {
139
+ const code = scrollToRefJs(ref);
140
+ return sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
141
+ }
142
+ async getFormState() {
143
+ const code = getFormStateJs();
144
+ return (await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() }));
145
+ }
109
146
  async wait(options) {
110
147
  if (typeof options === 'number') {
111
148
  await new Promise(resolve => setTimeout(resolve, options * 1000));
@@ -122,7 +159,8 @@ export class Page {
122
159
  }
123
160
  }
124
161
  async tabs() {
125
- return sendCommand('tabs', { op: 'list', ...this._workspaceOpt() });
162
+ const result = await sendCommand('tabs', { op: 'list', ...this._workspaceOpt() });
163
+ return Array.isArray(result) ? result : [];
126
164
  }
127
165
  async closeTab(index) {
128
166
  await sendCommand('tabs', { op: 'close', ...this._workspaceOpt(), ...(index !== undefined ? { index } : {}) });
@@ -135,7 +173,8 @@ export class Page {
135
173
  }
136
174
  async networkRequests(includeStatic = false) {
137
175
  const code = networkRequestsJs(includeStatic);
138
- return sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
176
+ const result = await sendCommand('exec', { code, ...this._workspaceOpt(), ...this._tabOpt() });
177
+ return Array.isArray(result) ? result : [];
139
178
  }
140
179
  /**
141
180
  * Console messages are not available in lightweight daemon mode.
@@ -192,7 +231,7 @@ export class Page {
192
231
  const { generateReadInterceptedJs } = await import('../interceptor.js');
193
232
  // Same as installInterceptor: must go through evaluate() for IIFE wrapping
194
233
  const result = await this.evaluate(generateReadInterceptedJs('__opencli_xhr'));
195
- return result || [];
234
+ return Array.isArray(result) ? result : [];
196
235
  }
197
236
  }
198
237
  // (End of file)
@@ -8,7 +8,7 @@
8
8
  * Usage: npx tsx src/build-manifest.ts
9
9
  * Output: dist/cli-manifest.json
10
10
  */
11
- interface ManifestEntry {
11
+ export interface ManifestEntry {
12
12
  site: string;
13
13
  name: string;
14
14
  description: string;
@@ -18,20 +18,27 @@ interface ManifestEntry {
18
18
  args: Array<{
19
19
  name: string;
20
20
  type?: string;
21
- default?: any;
21
+ default?: unknown;
22
22
  required?: boolean;
23
23
  positional?: boolean;
24
24
  help?: string;
25
25
  choices?: string[];
26
26
  }>;
27
27
  columns?: string[];
28
- pipeline?: any[];
28
+ pipeline?: Record<string, unknown>[];
29
29
  timeout?: number;
30
30
  /** 'yaml' or 'ts' — determines how executeCommand loads the handler */
31
31
  type: 'yaml' | 'ts';
32
32
  /** Relative path from clis/ dir, e.g. 'bilibili/hot.yaml' or 'bilibili/search.js' */
33
33
  modulePath?: string;
34
+ /** Pre-navigation control — see CliCommand.navigateBefore */
35
+ navigateBefore?: boolean | string;
34
36
  }
35
37
  export declare function parseTsArgsBlock(argsBlock: string): ManifestEntry['args'];
38
+ export declare function scanTs(filePath: string, site: string): ManifestEntry | null;
39
+ /**
40
+ * When both YAML and TS adapters exist for the same site/name,
41
+ * prefer the TS version (it self-registers and typically has richer logic).
42
+ */
43
+ export declare function shouldReplaceManifestEntry(current: ManifestEntry, next: ManifestEntry): boolean;
36
44
  export declare function buildManifest(): ManifestEntry[];
37
- export {};
@@ -15,6 +15,12 @@ import yaml from 'js-yaml';
15
15
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
16
  const CLIS_DIR = path.resolve(__dirname, 'clis');
17
17
  const OUTPUT = path.resolve(__dirname, '..', 'dist', 'cli-manifest.json');
18
+ function isRecord(value) {
19
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
20
+ }
21
+ function getErrorMessage(error) {
22
+ return error instanceof Error ? error.message : String(error);
23
+ }
18
24
  function extractBalancedBlock(source, startIndex, openChar, closeChar) {
19
25
  let depth = 0;
20
26
  let quote = null;
@@ -116,60 +122,66 @@ function scanYaml(filePath, site) {
116
122
  try {
117
123
  const raw = fs.readFileSync(filePath, 'utf-8');
118
124
  const def = yaml.load(raw);
119
- if (!def || typeof def !== 'object')
125
+ if (!isRecord(def))
120
126
  return null;
121
- const strategyStr = def.strategy ?? (def.browser === false ? 'public' : 'cookie');
127
+ const cliDef = def;
128
+ const strategyStr = cliDef.strategy ?? (cliDef.browser === false ? 'public' : 'cookie');
122
129
  const strategy = strategyStr.toUpperCase();
123
- const browser = def.browser ?? (strategy !== 'PUBLIC');
130
+ const browser = cliDef.browser ?? (strategy !== 'PUBLIC');
124
131
  const args = [];
125
- if (def.args && typeof def.args === 'object') {
126
- for (const [argName, argDef] of Object.entries(def.args)) {
132
+ if (cliDef.args && typeof cliDef.args === 'object') {
133
+ for (const [argName, argDef] of Object.entries(cliDef.args)) {
127
134
  args.push({
128
135
  name: argName,
129
136
  type: argDef?.type ?? 'str',
130
137
  default: argDef?.default,
131
138
  required: argDef?.required ?? false,
139
+ positional: argDef?.positional === true || undefined,
132
140
  help: argDef?.description ?? argDef?.help ?? '',
133
141
  choices: argDef?.choices,
134
142
  });
135
143
  }
136
144
  }
137
145
  return {
138
- site: def.site ?? site,
139
- name: def.name ?? path.basename(filePath, path.extname(filePath)),
140
- description: def.description ?? '',
141
- domain: def.domain,
146
+ site: cliDef.site ?? site,
147
+ name: cliDef.name ?? path.basename(filePath, path.extname(filePath)),
148
+ description: cliDef.description ?? '',
149
+ domain: cliDef.domain,
142
150
  strategy: strategy.toLowerCase(),
143
151
  browser,
144
152
  args,
145
- columns: def.columns,
146
- pipeline: def.pipeline,
147
- timeout: def.timeout,
153
+ columns: cliDef.columns,
154
+ pipeline: cliDef.pipeline,
155
+ timeout: cliDef.timeout,
148
156
  type: 'yaml',
157
+ navigateBefore: cliDef.navigateBefore,
149
158
  };
150
159
  }
151
160
  catch (err) {
152
- process.stderr.write(`Warning: failed to parse ${filePath}: ${err.message}\n`);
161
+ process.stderr.write(`Warning: failed to parse ${filePath}: ${getErrorMessage(err)}\n`);
153
162
  return null;
154
163
  }
155
164
  }
156
- function scanTs(filePath, site) {
165
+ export function scanTs(filePath, site) {
157
166
  // TS adapters self-register via cli() at import time.
158
167
  // We statically parse the source to extract metadata for the manifest stub.
159
168
  const baseName = path.basename(filePath, path.extname(filePath));
160
169
  const relativePath = `${site}/${baseName}.js`;
161
- const entry = {
162
- site,
163
- name: baseName,
164
- description: '',
165
- strategy: 'cookie',
166
- browser: true,
167
- args: [],
168
- type: 'ts',
169
- modulePath: relativePath,
170
- };
171
170
  try {
172
171
  const src = fs.readFileSync(filePath, 'utf-8');
172
+ // Helper/test modules should not appear as CLI commands in the manifest.
173
+ if (!/\bcli\s*\(/.test(src))
174
+ return null;
175
+ const entry = {
176
+ site,
177
+ name: baseName,
178
+ description: '',
179
+ strategy: 'cookie',
180
+ browser: true,
181
+ args: [],
182
+ type: 'ts',
183
+ modulePath: relativePath,
184
+ };
173
185
  // Extract description
174
186
  const descMatch = src.match(/description\s*:\s*['"`]([^'"`]*)['"`]/);
175
187
  if (descMatch)
@@ -198,14 +210,29 @@ function scanTs(filePath, site) {
198
210
  if (argsBlock) {
199
211
  entry.args = parseTsArgsBlock(argsBlock);
200
212
  }
213
+ // Extract navigateBefore: false
214
+ const navMatch = src.match(/navigateBefore\s*:\s*(true|false)/);
215
+ if (navMatch)
216
+ entry.navigateBefore = navMatch[1] === 'true' ? true : false;
217
+ return entry;
201
218
  }
202
- catch {
203
- // If parsing fails, fall back to empty metadata module will self-register at runtime
219
+ catch (err) {
220
+ // If parsing fails, log a warning (matching scanYaml behaviour) and skip the entry.
221
+ process.stderr.write(`Warning: failed to scan ${filePath}: ${getErrorMessage(err)}\n`);
222
+ return null;
204
223
  }
205
- return entry;
224
+ }
225
+ /**
226
+ * When both YAML and TS adapters exist for the same site/name,
227
+ * prefer the TS version (it self-registers and typically has richer logic).
228
+ */
229
+ export function shouldReplaceManifestEntry(current, next) {
230
+ if (current.type === next.type)
231
+ return true;
232
+ return current.type === 'yaml' && next.type === 'ts';
206
233
  }
207
234
  export function buildManifest() {
208
- const manifest = [];
235
+ const manifest = new Map();
209
236
  if (fs.existsSync(CLIS_DIR)) {
210
237
  for (const site of fs.readdirSync(CLIS_DIR)) {
211
238
  const siteDir = path.join(CLIS_DIR, site);
@@ -215,17 +242,35 @@ export function buildManifest() {
215
242
  const filePath = path.join(siteDir, file);
216
243
  if (file.endsWith('.yaml') || file.endsWith('.yml')) {
217
244
  const entry = scanYaml(filePath, site);
218
- if (entry)
219
- manifest.push(entry);
245
+ if (entry) {
246
+ const key = `${entry.site}/${entry.name}`;
247
+ const existing = manifest.get(key);
248
+ if (!existing || shouldReplaceManifestEntry(existing, entry)) {
249
+ if (existing && existing.type !== entry.type) {
250
+ process.stderr.write(`⚠️ Duplicate adapter ${key}: ${existing.type} superseded by ${entry.type}\n`);
251
+ }
252
+ manifest.set(key, entry);
253
+ }
254
+ }
220
255
  }
221
- else if ((file.endsWith('.ts') && !file.endsWith('.d.ts') && file !== 'index.ts') ||
222
- (file.endsWith('.js') && !file.endsWith('.d.js') && file !== 'index.js')) {
223
- manifest.push(scanTs(filePath, site));
256
+ else if ((file.endsWith('.ts') && !file.endsWith('.d.ts') && !file.endsWith('.test.ts') && file !== 'index.ts') ||
257
+ (file.endsWith('.js') && !file.endsWith('.d.js') && !file.endsWith('.test.js') && file !== 'index.js')) {
258
+ const entry = scanTs(filePath, site);
259
+ if (entry) {
260
+ const key = `${entry.site}/${entry.name}`;
261
+ const existing = manifest.get(key);
262
+ if (!existing || shouldReplaceManifestEntry(existing, entry)) {
263
+ if (existing && existing.type !== entry.type) {
264
+ process.stderr.write(`⚠️ Duplicate adapter ${key}: ${existing.type} superseded by ${entry.type}\n`);
265
+ }
266
+ manifest.set(key, entry);
267
+ }
268
+ }
224
269
  }
225
270
  }
226
271
  }
227
272
  }
228
- return manifest;
273
+ return [...manifest.values()];
229
274
  }
230
275
  function main() {
231
276
  const manifest = buildManifest();