@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
@@ -19,3 +19,10 @@ function walk(src, dst) {
19
19
  }
20
20
 
21
21
  walk('src/clis', 'dist/clis');
22
+
23
+ // Copy external CLI registry to dist/
24
+ const extSrc = 'src/external-clis.yaml';
25
+ if (existsSync(extSrc)) {
26
+ mkdirSync('dist', { recursive: true });
27
+ copyFileSync(extSrc, 'dist/external-clis.yaml');
28
+ }
@@ -9,8 +9,9 @@
9
9
  */
10
10
 
11
11
  import { WebSocket, type RawData } from 'ws';
12
- import type { IPage } from '../types.js';
12
+ import type { BrowserCookie, IPage, ScreenshotOptions, SnapshotOptions, WaitOptions } from '../types.js';
13
13
  import { wrapForEval } from './utils.js';
14
+ import { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
14
15
  import {
15
16
  clickJs,
16
17
  typeTextJs,
@@ -28,13 +29,24 @@ export interface CDPTarget {
28
29
  webSocketDebuggerUrl?: string;
29
30
  }
30
31
 
32
+ interface RuntimeEvaluateResult {
33
+ result?: {
34
+ value?: unknown;
35
+ };
36
+ exceptionDetails?: {
37
+ exception?: {
38
+ description?: string;
39
+ };
40
+ };
41
+ }
42
+
31
43
  const CDP_SEND_TIMEOUT = 30_000; // 30s per command
32
44
 
33
45
  export class CDPBridge {
34
46
  private _ws: WebSocket | null = null;
35
47
  private _idCounter = 0;
36
- private _pending = new Map<number, { resolve: (val: any) => void; reject: (err: Error) => void; timer: ReturnType<typeof setTimeout> }>();
37
- private _eventListeners = new Map<string, Set<(params: any) => void>>();
48
+ private _pending = new Map<number, { resolve: (val: unknown) => void; reject: (err: Error) => void; timer: ReturnType<typeof setTimeout> }>();
49
+ private _eventListeners = new Map<string, Set<(params: unknown) => void>>();
38
50
 
39
51
  async connect(opts?: { timeout?: number; workspace?: string }): Promise<IPage> {
40
52
  const endpoint = process.env.OPENCLI_CDP_ENDPOINT;
@@ -55,7 +67,8 @@ export class CDPBridge {
55
67
 
56
68
  return new Promise((resolve, reject) => {
57
69
  const ws = new WebSocket(wsUrl);
58
- const timeout = setTimeout(() => reject(new Error('CDP connect timeout')), opts?.timeout ?? 10000);
70
+ const timeoutMs = (opts?.timeout ?? 10) * 1000; // opts.timeout is in seconds
71
+ const timeout = setTimeout(() => reject(new Error('CDP connect timeout')), timeoutMs);
59
72
 
60
73
  ws.on('open', () => {
61
74
  clearTimeout(timeout);
@@ -110,7 +123,7 @@ export class CDPBridge {
110
123
  }
111
124
 
112
125
  /** Send a CDP command with timeout guard (P0 fix #4) */
113
- async send(method: string, params: any = {}, timeoutMs: number = CDP_SEND_TIMEOUT): Promise<any> {
126
+ async send(method: string, params: Record<string, unknown> = {}, timeoutMs: number = CDP_SEND_TIMEOUT): Promise<unknown> {
114
127
  if (!this._ws || this._ws.readyState !== WebSocket.OPEN) {
115
128
  throw new Error('CDP connection is not open');
116
129
  }
@@ -126,25 +139,25 @@ export class CDPBridge {
126
139
  }
127
140
 
128
141
  /** Listen for a CDP event */
129
- on(event: string, handler: (params: any) => void): void {
142
+ on(event: string, handler: (params: unknown) => void): void {
130
143
  let set = this._eventListeners.get(event);
131
144
  if (!set) { set = new Set(); this._eventListeners.set(event, set); }
132
145
  set.add(handler);
133
146
  }
134
147
 
135
148
  /** Remove a CDP event listener */
136
- off(event: string, handler: (params: any) => void): void {
149
+ off(event: string, handler: (params: unknown) => void): void {
137
150
  this._eventListeners.get(event)?.delete(handler);
138
151
  }
139
152
 
140
153
  /** Wait for a CDP event to fire (one-shot) */
141
- waitForEvent(event: string, timeoutMs: number = 15_000): Promise<any> {
154
+ waitForEvent(event: string, timeoutMs: number = 15_000): Promise<unknown> {
142
155
  return new Promise((resolve, reject) => {
143
156
  const timer = setTimeout(() => {
144
157
  this.off(event, handler);
145
158
  reject(new Error(`Timed out waiting for CDP event '${event}'`));
146
159
  }, timeoutMs);
147
- const handler = (params: any) => {
160
+ const handler = (params: unknown) => {
148
161
  clearTimeout(timer);
149
162
  this.off(event, handler);
150
163
  resolve(params);
@@ -158,38 +171,51 @@ class CDPPage implements IPage {
158
171
  constructor(private bridge: CDPBridge) {}
159
172
 
160
173
  /** Navigate with proper load event waiting (P1 fix #3) */
161
- async goto(url: string): Promise<void> {
174
+ async goto(url: string, options?: { waitUntil?: 'load' | 'none'; settleMs?: number }): Promise<void> {
162
175
  await this.bridge.send('Page.enable');
163
176
  const loadPromise = this.bridge.waitForEvent('Page.loadEventFired', 30_000)
164
177
  .catch(() => {}); // Don't fail if event times out
165
178
  await this.bridge.send('Page.navigate', { url });
166
179
  await loadPromise;
180
+ // Post-load settle: SPA frameworks need extra time to render after load event
181
+ if (options?.waitUntil !== 'none') {
182
+ const settleMs = options?.settleMs ?? 1000;
183
+ await new Promise(resolve => setTimeout(resolve, settleMs));
184
+ }
167
185
  }
168
186
 
169
- async evaluate(js: string): Promise<any> {
187
+ async evaluate(js: string): Promise<unknown> {
170
188
  const expression = wrapForEval(js);
171
189
  const result = await this.bridge.send('Runtime.evaluate', {
172
190
  expression,
173
191
  returnByValue: true,
174
192
  awaitPromise: true
175
- });
193
+ }) as RuntimeEvaluateResult;
176
194
  if (result.exceptionDetails) {
177
195
  throw new Error('Evaluate error: ' + (result.exceptionDetails.exception?.description || 'Unknown exception'));
178
196
  }
179
197
  return result.result?.value;
180
198
  }
181
199
 
182
- async getCookies(opts: { domain?: string; url?: string } = {}): Promise<any[]> {
200
+ async getCookies(opts: { domain?: string; url?: string } = {}): Promise<BrowserCookie[]> {
183
201
  const result = await this.bridge.send('Network.getCookies', opts.url ? { urls: [opts.url] } : {});
184
- const cookies = Array.isArray(result?.cookies) ? result.cookies : [];
185
- return opts.domain
186
- ? cookies.filter((cookie: any) => typeof cookie.domain === 'string' && cookie.domain.includes(opts.domain!))
202
+ const cookies = isRecord(result) && Array.isArray(result.cookies) ? result.cookies : [];
203
+ const domain = opts.domain;
204
+ return domain
205
+ ? cookies.filter((cookie): cookie is BrowserCookie => isCookie(cookie) && cookie.domain.includes(domain))
187
206
  : cookies;
188
207
  }
189
208
 
190
- async snapshot(_opts?: any): Promise<any> {
191
- // CDP doesn't have a built-in accessibility tree equivalent without additional setup
192
- return '(snapshot not available in CDP mode)';
209
+ async snapshot(opts: SnapshotOptions = {}): Promise<unknown> {
210
+ const snapshotJs = generateSnapshotJs({
211
+ viewportExpand: opts.viewportExpand ?? 800,
212
+ maxDepth: Math.max(1, Math.min(Number(opts.maxDepth) || 50, 200)),
213
+ interactiveOnly: opts.interactive ?? false,
214
+ maxTextLength: opts.maxTextLength ?? 120,
215
+ includeScrollInfo: true,
216
+ bboxDedup: true,
217
+ });
218
+ return this.evaluate(snapshotJs);
193
219
  }
194
220
 
195
221
  // ── Shared DOM operations (P1 fix #5 — using dom-helpers.ts) ──
@@ -206,13 +232,22 @@ class CDPPage implements IPage {
206
232
  await this.evaluate(pressKeyJs(key));
207
233
  }
208
234
 
209
- async wait(options: any): Promise<void> {
235
+ async scrollTo(ref: string): Promise<unknown> {
236
+ return this.evaluate(scrollToRefJs(ref));
237
+ }
238
+
239
+ async getFormState(): Promise<Record<string, unknown>> {
240
+ return (await this.evaluate(getFormStateJs())) as Record<string, unknown>;
241
+ }
242
+
243
+ async wait(options: number | WaitOptions): Promise<void> {
210
244
  if (typeof options === 'number') {
211
245
  await new Promise(resolve => setTimeout(resolve, options * 1000));
212
246
  return;
213
247
  }
214
- if (options.time) {
215
- await new Promise(resolve => setTimeout(resolve, options.time * 1000));
248
+ if (typeof options.time === 'number') {
249
+ const waitTime = options.time;
250
+ await new Promise(resolve => setTimeout(resolve, waitTime * 1000));
216
251
  return;
217
252
  }
218
253
  if (options.text) {
@@ -233,13 +268,13 @@ class CDPPage implements IPage {
233
268
  await this.evaluate(autoScrollJs(times, delayMs));
234
269
  }
235
270
 
236
- async screenshot(options: any = {}): Promise<string> {
271
+ async screenshot(options: ScreenshotOptions = {}): Promise<string> {
237
272
  const result = await this.bridge.send('Page.captureScreenshot', {
238
273
  format: options.format ?? 'png',
239
274
  quality: options.format === 'jpeg' ? (options.quality ?? 80) : undefined,
240
275
  captureBeyondViewport: options.fullPage ?? false,
241
276
  });
242
- const base64 = result.data;
277
+ const base64 = isRecord(result) && typeof result.data === 'string' ? result.data : '';
243
278
  if (options.path) {
244
279
  const fs = await import('node:fs');
245
280
  const path = await import('node:path');
@@ -250,11 +285,12 @@ class CDPPage implements IPage {
250
285
  return base64;
251
286
  }
252
287
 
253
- async networkRequests(includeStatic: boolean = false): Promise<any> {
254
- return this.evaluate(networkRequestsJs(includeStatic));
288
+ async networkRequests(includeStatic: boolean = false): Promise<unknown[]> {
289
+ const result = await this.evaluate(networkRequestsJs(includeStatic));
290
+ return Array.isArray(result) ? result : [];
255
291
  }
256
292
 
257
- async tabs(): Promise<any> {
293
+ async tabs(): Promise<unknown[]> {
258
294
  return [];
259
295
  }
260
296
 
@@ -270,7 +306,7 @@ class CDPPage implements IPage {
270
306
  // Not supported in direct CDP mode
271
307
  }
272
308
 
273
- async consoleMessages(_level?: string): Promise<any> {
309
+ async consoleMessages(_level?: string): Promise<unknown[]> {
274
310
  return [];
275
311
  }
276
312
 
@@ -282,13 +318,24 @@ class CDPPage implements IPage {
282
318
  }));
283
319
  }
284
320
 
285
- async getInterceptedRequests(): Promise<any[]> {
321
+ async getInterceptedRequests(): Promise<unknown[]> {
286
322
  const { generateReadInterceptedJs } = await import('../interceptor.js');
287
323
  const result = await this.evaluate(generateReadInterceptedJs('__opencli_xhr'));
288
- return (result as any[]) || [];
324
+ return Array.isArray(result) ? result : [];
289
325
  }
290
326
  }
291
327
 
328
+ function isRecord(value: unknown): value is Record<string, unknown> {
329
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
330
+ }
331
+
332
+ function isCookie(value: unknown): value is BrowserCookie {
333
+ return isRecord(value)
334
+ && typeof value.name === 'string'
335
+ && typeof value.value === 'string'
336
+ && typeof value.domain === 'string';
337
+ }
338
+
292
339
  // ── CDP target selection (unchanged) ──
293
340
 
294
341
  function selectCDPTarget(targets: CDPTarget[]): CDPTarget | undefined {
@@ -338,7 +385,6 @@ function scoreCDPTarget(target: CDPTarget, preferredPattern?: RegExp): number {
338
385
  if (title.includes('chatwise')) score += 120;
339
386
  if (title.includes('notion')) score += 120;
340
387
  if (title.includes('discord')) score += 120;
341
- if (title.includes('netease')) score += 120;
342
388
 
343
389
  if (url.includes('antigravity')) score += 100;
344
390
  if (url.includes('codex')) score += 100;
@@ -346,7 +392,6 @@ function scoreCDPTarget(target: CDPTarget, preferredPattern?: RegExp): number {
346
392
  if (url.includes('chatwise')) score += 100;
347
393
  if (url.includes('notion')) score += 100;
348
394
  if (url.includes('discord')) score += 100;
349
- if (url.includes('netease')) score += 100;
350
395
 
351
396
  return score;
352
397
  }
@@ -113,7 +113,8 @@ export async function sendCommand(
113
113
  throw new Error('sendCommand: max retries exhausted');
114
114
  }
115
115
 
116
- export async function listSessions(): Promise<any[]> {
116
+ export async function listSessions(): Promise<BrowserSessionInfo[]> {
117
117
  const result = await sendCommand('sessions');
118
118
  return Array.isArray(result) ? result : [];
119
119
  }
120
+ import type { BrowserSessionInfo } from '../types.js';
@@ -11,8 +11,21 @@ export function clickJs(ref: string): string {
11
11
  return `
12
12
  (() => {
13
13
  const ref = ${safeRef};
14
- const el = document.querySelector('[data-ref="' + ref + '"]')
15
- || document.querySelectorAll('a, button, input, [role="button"], [tabindex]')[parseInt(ref, 10) || 0];
14
+ // 1. data-opencli-ref (set by snapshot engine)
15
+ let el = document.querySelector('[data-opencli-ref="' + ref + '"]');
16
+ // 2. data-ref (legacy)
17
+ if (!el) el = document.querySelector('[data-ref="' + ref + '"]');
18
+ // 3. CSS selector
19
+ if (!el && ref.match(/^[a-zA-Z#.\\[]/)) {
20
+ try { el = document.querySelector(ref); } catch {}
21
+ }
22
+ // 4. Numeric index into interactive elements
23
+ if (!el) {
24
+ const idx = parseInt(ref, 10);
25
+ if (!isNaN(idx)) {
26
+ el = document.querySelectorAll('a, button, input, select, textarea, [role="button"], [tabindex]:not([tabindex="-1"])')[idx];
27
+ }
28
+ }
16
29
  if (!el) throw new Error('Element not found: ' + ref);
17
30
  el.scrollIntoView({ behavior: 'instant', block: 'center' });
18
31
  el.click();
@@ -28,13 +41,31 @@ export function typeTextJs(ref: string, text: string): string {
28
41
  return `
29
42
  (() => {
30
43
  const ref = ${safeRef};
31
- const el = document.querySelector('[data-ref="' + ref + '"]')
32
- || document.querySelectorAll('input, textarea, [contenteditable]')[parseInt(ref, 10) || 0];
44
+ // 1. data-opencli-ref (set by snapshot engine)
45
+ let el = document.querySelector('[data-opencli-ref="' + ref + '"]');
46
+ // 2. data-ref (legacy)
47
+ if (!el) el = document.querySelector('[data-ref="' + ref + '"]');
48
+ // 3. CSS selector
49
+ if (!el && ref.match(/^[a-zA-Z#.\\[]/)) {
50
+ try { el = document.querySelector(ref); } catch {}
51
+ }
52
+ // 4. Numeric index into typeable elements
53
+ if (!el) {
54
+ const idx = parseInt(ref, 10);
55
+ if (!isNaN(idx)) {
56
+ el = document.querySelectorAll('input, textarea, [contenteditable="true"]')[idx];
57
+ }
58
+ }
33
59
  if (!el) throw new Error('Element not found: ' + ref);
34
60
  el.focus();
35
- el.value = ${safeText};
36
- el.dispatchEvent(new Event('input', { bubbles: true }));
37
- el.dispatchEvent(new Event('change', { bubbles: true }));
61
+ if (el.isContentEditable) {
62
+ el.textContent = ${safeText};
63
+ el.dispatchEvent(new Event('input', { bubbles: true }));
64
+ } else {
65
+ el.value = ${safeText};
66
+ el.dispatchEvent(new Event('input', { bubbles: true }));
67
+ el.dispatchEvent(new Event('change', { bubbles: true }));
68
+ }
38
69
  return 'typed';
39
70
  })()
40
71
  `;
@@ -0,0 +1,249 @@
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
+
12
+ import { describe, it, expect } from 'vitest';
13
+ import { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
14
+
15
+ describe('generateSnapshotJs', () => {
16
+ it('returns a non-empty string', () => {
17
+ const js = generateSnapshotJs();
18
+ expect(typeof js).toBe('string');
19
+ expect(js.length).toBeGreaterThan(100);
20
+ });
21
+
22
+ it('generates syntactically valid JS (can be parsed)', () => {
23
+ const js = generateSnapshotJs();
24
+ expect(() => new Function(js)).not.toThrow();
25
+ });
26
+
27
+ it('embeds default options correctly', () => {
28
+ const js = generateSnapshotJs();
29
+ expect(js).toContain('VIEWPORT_EXPAND = 800');
30
+ expect(js).toContain('MAX_DEPTH = 50');
31
+ expect(js).toContain('INTERACTIVE_ONLY = false');
32
+ expect(js).toContain('MAX_TEXT_LEN = 120');
33
+ expect(js).toContain('INCLUDE_SCROLL_INFO = true');
34
+ expect(js).toContain('BBOX_DEDUP = true');
35
+ expect(js).toContain('INCLUDE_SHADOW_DOM = true');
36
+ expect(js).toContain('INCLUDE_IFRAMES = true');
37
+ expect(js).toContain('PAINT_ORDER_CHECK = true');
38
+ expect(js).toContain('ANNOTATE_REFS = true');
39
+ expect(js).toContain('REPORT_HIDDEN = true');
40
+ expect(js).toContain('FILTER_ADS = true');
41
+ expect(js).toContain('MARKDOWN_TABLES = true');
42
+ expect(js).toContain('PREV_HASHES = null');
43
+ });
44
+
45
+ it('embeds custom options correctly', () => {
46
+ const js = generateSnapshotJs({
47
+ viewportExpand: 2000,
48
+ maxDepth: 30,
49
+ interactiveOnly: true,
50
+ maxTextLength: 200,
51
+ includeScrollInfo: false,
52
+ bboxDedup: false,
53
+ includeShadowDom: false,
54
+ includeIframes: false,
55
+ maxIframes: 3,
56
+ paintOrderCheck: false,
57
+ annotateRefs: false,
58
+ reportHidden: false,
59
+ filterAds: false,
60
+ markdownTables: false,
61
+ });
62
+ expect(js).toContain('VIEWPORT_EXPAND = 2000');
63
+ expect(js).toContain('MAX_DEPTH = 30');
64
+ expect(js).toContain('INTERACTIVE_ONLY = true');
65
+ expect(js).toContain('MAX_TEXT_LEN = 200');
66
+ expect(js).toContain('INCLUDE_SCROLL_INFO = false');
67
+ expect(js).toContain('BBOX_DEDUP = false');
68
+ expect(js).toContain('INCLUDE_SHADOW_DOM = false');
69
+ expect(js).toContain('INCLUDE_IFRAMES = false');
70
+ expect(js).toContain('MAX_IFRAMES = 3');
71
+ expect(js).toContain('PAINT_ORDER_CHECK = false');
72
+ expect(js).toContain('ANNOTATE_REFS = false');
73
+ expect(js).toContain('REPORT_HIDDEN = false');
74
+ expect(js).toContain('FILTER_ADS = false');
75
+ expect(js).toContain('MARKDOWN_TABLES = false');
76
+ });
77
+
78
+ it('clamps maxDepth between 1 and 200', () => {
79
+ expect(generateSnapshotJs({ maxDepth: -5 })).toContain('MAX_DEPTH = 1');
80
+ expect(generateSnapshotJs({ maxDepth: 999 })).toContain('MAX_DEPTH = 200');
81
+ expect(generateSnapshotJs({ maxDepth: 75 })).toContain('MAX_DEPTH = 75');
82
+ });
83
+
84
+ it('wraps output as an IIFE', () => {
85
+ const js = generateSnapshotJs();
86
+ expect(js.startsWith('(() =>')).toBe(true);
87
+ expect(js.trimEnd().endsWith(')()')).toBe(true);
88
+ });
89
+
90
+ it('embeds previousHashes for incremental diff', () => {
91
+ const hashes = JSON.stringify(['12345', '67890']);
92
+ const js = generateSnapshotJs({ previousHashes: hashes });
93
+ expect(js).toContain('new Set(["12345","67890"])');
94
+ });
95
+
96
+ it('includes all core features in generated code', () => {
97
+ const js = generateSnapshotJs();
98
+
99
+ // Tag filtering
100
+ expect(js).toContain('SKIP_TAGS');
101
+ expect(js).toContain("'script'");
102
+ expect(js).toContain("'style'");
103
+
104
+ // SVG collapsing
105
+ expect(js).toContain('SVG_CHILDREN');
106
+
107
+ // Interactive detection
108
+ expect(js).toContain('INTERACTIVE_TAGS');
109
+ expect(js).toContain('INTERACTIVE_ROLES');
110
+ expect(js).toContain('isInteractive');
111
+
112
+ // Visibility
113
+ expect(js).toContain('isVisibleByCSS');
114
+ expect(js).toContain('isInExpandedViewport');
115
+
116
+ // BBox dedup
117
+ expect(js).toContain('isContainedBy');
118
+ expect(js).toContain('PROPAGATING_TAGS');
119
+
120
+ // Shadow DOM
121
+ expect(js).toContain('shadowRoot');
122
+ expect(js).toContain('|shadow|');
123
+
124
+ // iframe
125
+ expect(js).toContain('walkIframe');
126
+ expect(js).toContain('|iframe|');
127
+
128
+ // Paint order
129
+ expect(js).toContain('isOccludedByOverlay');
130
+ expect(js).toContain('elementFromPoint');
131
+
132
+ // Ad filtering
133
+ expect(js).toContain('isAdElement');
134
+ expect(js).toContain('AD_PATTERNS');
135
+
136
+ // data-ref annotation
137
+ expect(js).toContain('data-opencli-ref');
138
+
139
+ // Hidden elements report
140
+ expect(js).toContain('hiddenInteractives');
141
+ expect(js).toContain('hidden_interactive');
142
+
143
+ // Incremental diff
144
+ expect(js).toContain('hashElement');
145
+ expect(js).toContain('currentHashes');
146
+ expect(js).toContain('__opencli_prev_hashes');
147
+
148
+ // Table serialization
149
+ expect(js).toContain('serializeTable');
150
+ expect(js).toContain('|table|');
151
+
152
+ // Synthetic attributes
153
+ expect(js).toContain("'YYYY-MM-DD'");
154
+ expect(js).toContain('value=••••');
155
+
156
+ // Page metadata
157
+ expect(js).toContain('location.href');
158
+ expect(js).toContain('document.title');
159
+ });
160
+
161
+ it('contains proper attribute whitelist', () => {
162
+ const js = generateSnapshotJs();
163
+ const expectedAttrs = [
164
+ 'aria-label', 'aria-expanded', 'aria-checked', 'aria-selected',
165
+ 'placeholder', 'href', 'role', 'data-testid', 'autocomplete',
166
+ ];
167
+ for (const attr of expectedAttrs) {
168
+ expect(js).toContain(`'${attr}'`);
169
+ }
170
+ });
171
+
172
+ it('includes scroll info formatting', () => {
173
+ const js = generateSnapshotJs();
174
+ expect(js).toContain('scrollHeight');
175
+ expect(js).toContain('scrollTop');
176
+ expect(js).toContain('|scroll|');
177
+ expect(js).toContain('page_scroll');
178
+ });
179
+ });
180
+
181
+ describe('scrollToRefJs', () => {
182
+ it('generates valid JS', () => {
183
+ const js = scrollToRefJs('42');
184
+ expect(() => new Function(js)).not.toThrow();
185
+ });
186
+
187
+ it('targets data-opencli-ref', () => {
188
+ const js = scrollToRefJs('7');
189
+ expect(js).toContain('data-opencli-ref');
190
+ expect(js).toContain('scrollIntoView');
191
+ expect(js).toContain('"7"');
192
+ });
193
+
194
+ it('falls back to data-ref', () => {
195
+ const js = scrollToRefJs('3');
196
+ expect(js).toContain('data-ref');
197
+ });
198
+
199
+ it('returns scrolled info', () => {
200
+ const js = scrollToRefJs('1');
201
+ expect(js).toContain('scrolled: true');
202
+ expect(js).toContain('tag:');
203
+ });
204
+ });
205
+
206
+ describe('getFormStateJs', () => {
207
+ it('generates valid JS', () => {
208
+ const js = getFormStateJs();
209
+ expect(() => new Function(js)).not.toThrow();
210
+ });
211
+
212
+ it('collects form elements', () => {
213
+ const js = getFormStateJs();
214
+ expect(js).toContain('document.forms');
215
+ expect(js).toContain('form.elements');
216
+ });
217
+
218
+ it('collects orphan fields', () => {
219
+ const js = getFormStateJs();
220
+ expect(js).toContain('orphanFields');
221
+ expect(js).toContain('el.form');
222
+ });
223
+
224
+ it('handles different input types', () => {
225
+ const js = getFormStateJs();
226
+ expect(js).toContain('checkbox');
227
+ expect(js).toContain('radio');
228
+ expect(js).toContain('password');
229
+ expect(js).toContain('contenteditable');
230
+ });
231
+
232
+ it('extracts labels', () => {
233
+ const js = getFormStateJs();
234
+ expect(js).toContain('aria-label');
235
+ expect(js).toContain('label[for=');
236
+ expect(js).toContain('closest');
237
+ expect(js).toContain('placeholder');
238
+ });
239
+
240
+ it('masks passwords', () => {
241
+ const js = getFormStateJs();
242
+ expect(js).toContain('••••');
243
+ });
244
+
245
+ it('includes data-opencli-ref in output', () => {
246
+ const js = getFormStateJs();
247
+ expect(js).toContain('data-opencli-ref');
248
+ });
249
+ });