@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
package/src/cli.ts CHANGED
@@ -1,57 +1,91 @@
1
+ /**
2
+ * CLI entry point: registers built-in commands and wires up Commander.
3
+ *
4
+ * Built-in commands are registered inline here (list, validate, explore, etc.).
5
+ * Dynamic adapter commands are registered via commanderAdapter.ts.
6
+ */
7
+
1
8
  import { Command } from 'commander';
2
9
  import chalk from 'chalk';
3
- import { executeCommand } from './engine.js';
4
- import { Strategy, type CliCommand, fullName, getRegistry, strategyLabel } from './registry.js';
10
+ import { type CliCommand, fullName, getRegistry, strategyLabel } from './registry.js';
11
+ import { serializeCommand, formatArgSummary } from './serialization.js';
5
12
  import { render as renderOutput } from './output.js';
6
- import { BrowserBridge, CDPBridge } from './browser/index.js';
7
- import { browserSession, DEFAULT_BROWSER_COMMAND_TIMEOUT, runWithTimeout } from './runtime.js';
13
+ import { getBrowserFactory, browserSession } from './runtime.js';
8
14
  import { PKG_VERSION } from './version.js';
9
15
  import { printCompletionScript } from './completion.js';
10
- import { CliError } from './errors.js';
11
- import { shouldUseBrowserSession } from './capabilityRouting.js';
12
16
  import { loadExternalClis, executeExternalCli, installExternalCli, registerExternalCli, isBinaryInstalled } from './external.js';
17
+ import { registerAllCommands } from './commanderAdapter.js';
13
18
 
14
19
  export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
15
20
  const program = new Command();
16
- program.name('opencli').description('Make any website your CLI. Zero setup. AI-powered.').version(PKG_VERSION);
21
+ // enablePositionalOptions: prevents parent from consuming flags meant for subcommands;
22
+ // prerequisite for passThroughOptions to forward --help/--version to external binaries
23
+ program
24
+ .name('opencli')
25
+ .description('Make any website your CLI. Zero setup. AI-powered.')
26
+ .version(PKG_VERSION)
27
+ .enablePositionalOptions();
17
28
 
18
- // ── Built-in commands ──────────────────────────────────────────────────────
29
+ // ── Built-in: list ────────────────────────────────────────────────────────
19
30
 
20
- program.command('list').description('List all available CLI commands').option('-f, --format <fmt>', 'Output format: table, json, yaml, md, csv', 'table').option('--json', 'JSON output (deprecated)')
31
+ program
32
+ .command('list')
33
+ .description('List all available CLI commands')
34
+ .option('-f, --format <fmt>', 'Output format: table, json, yaml, md, csv', 'table')
35
+ .option('--json', 'JSON output (deprecated)')
21
36
  .action((opts) => {
22
37
  const registry = getRegistry();
23
38
  const commands = [...registry.values()].sort((a, b) => fullName(a).localeCompare(fullName(b)));
24
- const rows = commands.map(c => ({
25
- command: fullName(c),
26
- site: c.site,
27
- name: c.name,
28
- description: c.description,
29
- strategy: strategyLabel(c),
30
- browser: c.browser,
31
- args: c.args.map(a => a.name).join(', '),
32
- }));
33
39
  const fmt = opts.json && opts.format === 'table' ? 'json' : opts.format;
40
+ const isStructured = fmt === 'json' || fmt === 'yaml';
41
+
34
42
  if (fmt !== 'table') {
43
+ const rows = isStructured
44
+ ? commands.map(serializeCommand)
45
+ : commands.map(c => ({
46
+ command: fullName(c),
47
+ site: c.site,
48
+ name: c.name,
49
+ description: c.description,
50
+ strategy: strategyLabel(c),
51
+ browser: !!c.browser,
52
+ args: formatArgSummary(c.args),
53
+ }));
35
54
  renderOutput(rows, {
36
55
  fmt,
37
- columns: ['command', 'site', 'name', 'description', 'strategy', 'browser', 'args'],
56
+ columns: ['command', 'site', 'name', 'description', 'strategy', 'browser', 'args',
57
+ ...(isStructured ? ['columns', 'domain'] : [])],
38
58
  title: 'opencli/list',
39
59
  source: 'opencli list',
40
60
  });
41
61
  return;
42
62
  }
63
+
64
+ // Table (default) — grouped by site
43
65
  const sites = new Map<string, CliCommand[]>();
44
- for (const cmd of commands) { const g = sites.get(cmd.site) ?? []; g.push(cmd); sites.set(cmd.site, g); }
45
- console.log(); console.log(chalk.bold(' opencli') + chalk.dim(' — available commands')); console.log();
66
+ for (const cmd of commands) {
67
+ const g = sites.get(cmd.site) ?? [];
68
+ g.push(cmd);
69
+ sites.set(cmd.site, g);
70
+ }
71
+
72
+ console.log();
73
+ console.log(chalk.bold(' opencli') + chalk.dim(' — available commands'));
74
+ console.log();
46
75
  for (const [site, cmds] of sites) {
47
76
  console.log(chalk.bold.cyan(` ${site}`));
48
- for (const cmd of cmds) { const tag = strategyLabel(cmd) === 'public' ? chalk.green('[public]') : chalk.yellow(`[${strategyLabel(cmd)}]`); console.log(` ${cmd.name} ${tag}${cmd.description ? chalk.dim(` — ${cmd.description}`) : ''}`); }
77
+ for (const cmd of cmds) {
78
+ const tag = strategyLabel(cmd) === 'public'
79
+ ? chalk.green('[public]')
80
+ : chalk.yellow(`[${strategyLabel(cmd)}]`);
81
+ console.log(` ${cmd.name} ${tag}${cmd.description ? chalk.dim(` — ${cmd.description}`) : ''}`);
82
+ }
49
83
  console.log();
50
84
  }
51
-
85
+
52
86
  const externalClis = loadExternalClis();
53
87
  if (externalClis.length > 0) {
54
- console.log(chalk.bold.cyan(` external CLIs`));
88
+ console.log(chalk.bold.cyan(' external CLIs'));
55
89
  for (const ext of externalClis) {
56
90
  const isInstalled = isBinaryInstalled(ext.binary);
57
91
  const tag = isInstalled ? chalk.green('[installed]') : chalk.yellow('[auto-install]');
@@ -59,17 +93,27 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
59
93
  }
60
94
  console.log();
61
95
  }
62
-
63
- console.log(chalk.dim(` ${commands.length} built-in commands across ${sites.size} sites, ${externalClis.length} external CLIs`)); console.log();
96
+
97
+ console.log(chalk.dim(` ${commands.length} built-in commands across ${sites.size} sites, ${externalClis.length} external CLIs`));
98
+ console.log();
64
99
  });
65
100
 
66
- program.command('validate').description('Validate CLI definitions').argument('[target]', 'site or site/name')
101
+ // ── Built-in: validate / verify ───────────────────────────────────────────
102
+
103
+ program
104
+ .command('validate')
105
+ .description('Validate CLI definitions')
106
+ .argument('[target]', 'site or site/name')
67
107
  .action(async (target) => {
68
108
  const { validateClisWithTarget, renderValidationReport } = await import('./validate.js');
69
109
  console.log(renderValidationReport(validateClisWithTarget([BUILTIN_CLIS, USER_CLIS], target)));
70
110
  });
71
111
 
72
- program.command('verify').description('Validate + smoke test').argument('[target]').option('--smoke', 'Run smoke tests', false)
112
+ program
113
+ .command('verify')
114
+ .description('Validate + smoke test')
115
+ .argument('[target]')
116
+ .option('--smoke', 'Run smoke tests', false)
73
117
  .action(async (target, opts) => {
74
118
  const { verifyClis, renderVerifyReport } = await import('./verify.js');
75
119
  const r = await verifyClis({ builtinClis: BUILTIN_CLIS, userClis: USER_CLIS, target, smoke: opts.smoke });
@@ -77,28 +121,91 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
77
121
  process.exitCode = r.ok ? 0 : 1;
78
122
  });
79
123
 
80
- program.command('explore').alias('probe').description('Explore a website: discover APIs, stores, and recommend strategies').argument('<url>').option('--site <name>').option('--goal <text>').option('--wait <s>', '', '3').option('--auto', 'Enable interactive fuzzing (simulate clicks to trigger lazy APIs)').option('--click <labels>', 'Comma-separated labels to click before fuzzing (e.g. "字幕,CC,评论")')
81
- .action(async (url, opts) => { const { exploreUrl, renderExploreSummary } = await import('./explore.js'); const clickLabels = opts.click ? opts.click.split(',').map((s: string) => s.trim()) : undefined; const BrowserFactory = process.env.OPENCLI_CDP_ENDPOINT ? CDPBridge : BrowserBridge; const workspace = `explore:${opts.site ?? (() => { try { return new URL(url).host; } catch { return 'default'; } })()}`; console.log(renderExploreSummary(await exploreUrl(url, { BrowserFactory: BrowserFactory as any, site: opts.site, goal: opts.goal, waitSeconds: parseFloat(opts.wait), auto: opts.auto, clickLabels, workspace }))); });
124
+ // ── Built-in: explore / synthesize / generate / cascade ───────────────────
82
125
 
83
- program.command('synthesize').description('Synthesize CLIs from explore').argument('<target>').option('--top <n>', '', '3')
84
- .action(async (target, opts) => { const { synthesizeFromExplore, renderSynthesizeSummary } = await import('./synthesize.js'); console.log(renderSynthesizeSummary(synthesizeFromExplore(target, { top: parseInt(opts.top) }))); });
126
+ program
127
+ .command('explore')
128
+ .alias('probe')
129
+ .description('Explore a website: discover APIs, stores, and recommend strategies')
130
+ .argument('<url>')
131
+ .option('--site <name>')
132
+ .option('--goal <text>')
133
+ .option('--wait <s>', '', '3')
134
+ .option('--auto', 'Enable interactive fuzzing')
135
+ .option('--click <labels>', 'Comma-separated labels to click before fuzzing')
136
+ .action(async (url, opts) => {
137
+ const { exploreUrl, renderExploreSummary } = await import('./explore.js');
138
+ const clickLabels = opts.click
139
+ ? opts.click.split(',').map((s: string) => s.trim())
140
+ : undefined;
141
+ const workspace = `explore:${inferHost(url, opts.site)}`;
142
+ const result = await exploreUrl(url, {
143
+ BrowserFactory: getBrowserFactory(),
144
+ site: opts.site,
145
+ goal: opts.goal,
146
+ waitSeconds: parseFloat(opts.wait),
147
+ auto: opts.auto,
148
+ clickLabels,
149
+ workspace,
150
+ });
151
+ console.log(renderExploreSummary(result));
152
+ });
85
153
 
86
- program.command('generate').description('One-shot: explore → synthesize → register').argument('<url>').option('--goal <text>').option('--site <name>')
87
- .action(async (url, opts) => { const { generateCliFromUrl, renderGenerateSummary } = await import('./generate.js'); const BrowserFactory = process.env.OPENCLI_CDP_ENDPOINT ? CDPBridge : BrowserBridge; const workspace = `generate:${opts.site ?? (() => { try { return new URL(url).host; } catch { return 'default'; } })()}`; const r = await generateCliFromUrl({ url, BrowserFactory: BrowserFactory as any, builtinClis: BUILTIN_CLIS, userClis: USER_CLIS, goal: opts.goal, site: opts.site, workspace }); console.log(renderGenerateSummary(r)); process.exitCode = r.ok ? 0 : 1; });
154
+ program
155
+ .command('synthesize')
156
+ .description('Synthesize CLIs from explore')
157
+ .argument('<target>')
158
+ .option('--top <n>', '', '3')
159
+ .action(async (target, opts) => {
160
+ const { synthesizeFromExplore, renderSynthesizeSummary } = await import('./synthesize.js');
161
+ console.log(renderSynthesizeSummary(synthesizeFromExplore(target, { top: parseInt(opts.top) })));
162
+ });
88
163
 
89
- program.command('cascade').description('Strategy cascade: find simplest working strategy').argument('<url>').option('--site <name>')
164
+ program
165
+ .command('generate')
166
+ .description('One-shot: explore → synthesize → register')
167
+ .argument('<url>')
168
+ .option('--goal <text>')
169
+ .option('--site <name>')
170
+ .action(async (url, opts) => {
171
+ const { generateCliFromUrl, renderGenerateSummary } = await import('./generate.js');
172
+ const workspace = `generate:${inferHost(url, opts.site)}`;
173
+ const r = await generateCliFromUrl({
174
+ url,
175
+ BrowserFactory: getBrowserFactory(),
176
+ builtinClis: BUILTIN_CLIS,
177
+ userClis: USER_CLIS,
178
+ goal: opts.goal,
179
+ site: opts.site,
180
+ workspace,
181
+ });
182
+ console.log(renderGenerateSummary(r));
183
+ process.exitCode = r.ok ? 0 : 1;
184
+ });
185
+
186
+ program
187
+ .command('cascade')
188
+ .description('Strategy cascade: find simplest working strategy')
189
+ .argument('<url>')
190
+ .option('--site <name>')
90
191
  .action(async (url, opts) => {
91
192
  const { cascadeProbe, renderCascadeResult } = await import('./cascade.js');
92
- const BrowserFactory = process.env.OPENCLI_CDP_ENDPOINT ? CDPBridge : BrowserBridge;
93
- const result = await browserSession(BrowserFactory as any, async (page) => {
94
- // Navigate to the site first for cookie context
95
- try { const siteUrl = new URL(url); await page.goto(`${siteUrl.protocol}//${siteUrl.host}`); await page.wait(2); } catch {}
193
+ const workspace = `cascade:${inferHost(url, opts.site)}`;
194
+ const result = await browserSession(getBrowserFactory(), async (page) => {
195
+ try {
196
+ const siteUrl = new URL(url);
197
+ await page.goto(`${siteUrl.protocol}//${siteUrl.host}`);
198
+ await page.wait(2);
199
+ } catch {}
96
200
  return cascadeProbe(page, url);
97
- }, { workspace: `cascade:${opts.site ?? (() => { try { return new URL(url).host; } catch { return 'default'; } })()}` });
201
+ }, { workspace });
98
202
  console.log(renderCascadeResult(result));
99
203
  });
100
204
 
101
- program.command('doctor')
205
+ // ── Built-in: doctor / setup / completion ─────────────────────────────────
206
+
207
+ program
208
+ .command('doctor')
102
209
  .description('Diagnose opencli browser bridge connectivity')
103
210
  .option('--live', 'Test browser connectivity (requires Chrome running)', false)
104
211
  .option('--sessions', 'Show active automation sessions', false)
@@ -108,66 +215,150 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
108
215
  console.log(renderBrowserDoctorReport(report));
109
216
  });
110
217
 
111
- program.command('setup')
218
+ program
219
+ .command('setup')
112
220
  .description('Interactive setup: verify browser bridge connectivity')
113
221
  .action(async () => {
114
222
  const { runSetup } = await import('./setup.js');
115
223
  await runSetup({ cliVersion: PKG_VERSION });
116
224
  });
117
225
 
118
- program.command('completion')
226
+ program
227
+ .command('completion')
119
228
  .description('Output shell completion script')
120
229
  .argument('<shell>', 'Shell type: bash, zsh, or fish')
121
230
  .action((shell) => {
122
231
  printCompletionScript(shell);
123
232
  });
124
233
 
234
+ // ── Plugin management ──────────────────────────────────────────────────────
235
+
236
+ const pluginCmd = program.command('plugin').description('Manage opencli plugins');
237
+
238
+ pluginCmd
239
+ .command('install')
240
+ .description('Install a plugin from GitHub')
241
+ .argument('<source>', 'Plugin source (e.g. github:user/repo)')
242
+ .action(async (source: string) => {
243
+ const { installPlugin } = await import('./plugin.js');
244
+ try {
245
+ const name = installPlugin(source);
246
+ console.log(chalk.green(`✅ Plugin "${name}" installed successfully.`));
247
+ console.log(chalk.dim(` Restart opencli to use the new commands.`));
248
+ } catch (err: any) {
249
+ console.error(chalk.red(`Error: ${err.message}`));
250
+ process.exitCode = 1;
251
+ }
252
+ });
253
+
254
+ pluginCmd
255
+ .command('uninstall')
256
+ .description('Uninstall a plugin')
257
+ .argument('<name>', 'Plugin name')
258
+ .action(async (name: string) => {
259
+ const { uninstallPlugin } = await import('./plugin.js');
260
+ try {
261
+ uninstallPlugin(name);
262
+ console.log(chalk.green(`✅ Plugin "${name}" uninstalled.`));
263
+ } catch (err: any) {
264
+ console.error(chalk.red(`Error: ${err.message}`));
265
+ process.exitCode = 1;
266
+ }
267
+ });
268
+
269
+ pluginCmd
270
+ .command('list')
271
+ .description('List installed plugins')
272
+ .option('-f, --format <fmt>', 'Output format: table, json', 'table')
273
+ .action(async (opts) => {
274
+ const { listPlugins } = await import('./plugin.js');
275
+ const plugins = listPlugins();
276
+ if (plugins.length === 0) {
277
+ console.log(chalk.dim(' No plugins installed.'));
278
+ console.log(chalk.dim(` Install one with: opencli plugin install github:user/repo`));
279
+ return;
280
+ }
281
+ if (opts.format === 'json') {
282
+ renderOutput(plugins, {
283
+ fmt: 'json',
284
+ columns: ['name', 'commands', 'source'],
285
+ title: 'opencli/plugins',
286
+ source: 'opencli plugin list',
287
+ });
288
+ return;
289
+ }
290
+ console.log();
291
+ console.log(chalk.bold(' Installed plugins'));
292
+ console.log();
293
+ for (const p of plugins) {
294
+ const cmds = p.commands.length > 0 ? chalk.dim(` (${p.commands.join(', ')})`) : '';
295
+ const src = p.source ? chalk.dim(` ← ${p.source}`) : '';
296
+ console.log(` ${chalk.cyan(p.name)}${cmds}${src}`);
297
+ }
298
+ console.log();
299
+ console.log(chalk.dim(` ${plugins.length} plugin(s) installed`));
300
+ console.log();
301
+ });
302
+
303
+ // ── External CLIs ─────────────────────────────────────────────────────────
304
+
125
305
  const externalClis = loadExternalClis();
126
306
 
127
- program.command('install')
307
+ program
308
+ .command('install')
128
309
  .description('Install an external CLI')
129
310
  .argument('<name>', 'Name of the external CLI')
130
311
  .action((name: string) => {
131
312
  const ext = externalClis.find(e => e.name === name);
132
313
  if (!ext) {
133
- console.error(chalk.red(`External CLI '${name}' not found in registry.`));
134
- process.exitCode = 1;
135
- return;
314
+ console.error(chalk.red(`External CLI '${name}' not found in registry.`));
315
+ process.exitCode = 1;
316
+ return;
136
317
  }
137
318
  installExternalCli(ext);
138
319
  });
139
320
 
140
- program.command('register')
321
+ program
322
+ .command('register')
141
323
  .description('Register an external CLI')
142
324
  .argument('<name>', 'Name of the CLI')
143
325
  .option('--binary <bin>', 'Binary name if different from name')
144
326
  .option('--install <cmd>', 'Auto-install command')
145
327
  .option('--desc <text>', 'Description')
146
328
  .action((name, opts) => {
147
- registerExternalCli(name, opts.binary, opts.install, opts.desc);
329
+ registerExternalCli(name, { binary: opts.binary, install: opts.install, description: opts.desc });
148
330
  });
149
331
 
332
+ function passthroughExternal(name: string, parsedArgs?: string[]) {
333
+ const args = parsedArgs ?? (() => {
334
+ const idx = process.argv.indexOf(name);
335
+ return process.argv.slice(idx + 1);
336
+ })();
337
+ try {
338
+ executeExternalCli(name, args, externalClis);
339
+ } catch (err: any) {
340
+ console.error(chalk.red(`Error: ${err.message}`));
341
+ process.exitCode = 1;
342
+ }
343
+ }
344
+
150
345
  for (const ext of externalClis) {
151
346
  if (program.commands.some(c => c.name() === ext.name)) continue;
152
- program.command(ext.name)
347
+ program
348
+ .command(ext.name)
153
349
  .description(`(External) ${ext.description || ext.name}`)
350
+ .argument('[args...]')
154
351
  .allowUnknownOption()
155
- .action(() => {
156
- // Retrieve args passed to the external CLI
157
- // Commander consumes standard args before the action, so we must slice process.argv directly.
158
- const extIndex = process.argv.indexOf(ext.name);
159
- const args = process.argv.slice(extIndex + 1);
160
- executeExternalCli(ext.name, args).catch(err => {
161
- console.error(chalk.red(`Error: ${err.message}`));
162
- process.exitCode = 1;
163
- });
164
- });
352
+ .passThroughOptions()
353
+ .helpOption(false)
354
+ .action((args: string[]) => passthroughExternal(ext.name, args));
165
355
  }
166
356
 
167
- // ── Antigravity serve (built-in, long-running) ──────────────────────────────
357
+ // ── Antigravity serve (long-running, special case) ────────────────────────
168
358
 
169
359
  const antigravityCmd = program.command('antigravity').description('antigravity commands');
170
- antigravityCmd.command('serve')
360
+ antigravityCmd
361
+ .command('serve')
171
362
  .description('Start Anthropic-compatible API proxy for Antigravity')
172
363
  .option('--port <port>', 'Server port (default: 8082)', '8082')
173
364
  .action(async (opts) => {
@@ -175,88 +366,45 @@ export function runCli(BUILTIN_CLIS: string, USER_CLIS: string): void {
175
366
  await startServe({ port: parseInt(opts.port) });
176
367
  });
177
368
 
178
- // ── Dynamic site commands ──────────────────────────────────────────────────
369
+ // ── Dynamic adapter commands ──────────────────────────────────────────────
179
370
 
180
- const registry = getRegistry();
181
371
  const siteGroups = new Map<string, Command>();
182
- // Pre-seed with the antigravity command registered above to avoid duplicates
183
372
  siteGroups.set('antigravity', antigravityCmd);
373
+ registerAllCommands(program, siteGroups);
184
374
 
185
- for (const [, cmd] of registry) {
186
- let siteCmd = siteGroups.get(cmd.site);
187
- if (!siteCmd) { siteCmd = program.command(cmd.site).description(`${cmd.site} commands`); siteGroups.set(cmd.site, siteCmd); }
188
- // Skip if this subcommand was already hardcoded (e.g. antigravity serve)
189
- if (siteCmd.commands.some((c: Command) => c.name() === cmd.name)) continue;
190
- const subCmd = siteCmd.command(cmd.name).description(cmd.description);
191
-
192
- // Register positional args first, then named options
193
- const positionalArgs: typeof cmd.args = [];
194
- for (const arg of cmd.args) {
195
- if (arg.positional) {
196
- const bracket = arg.required ? `<${arg.name}>` : `[${arg.name}]`;
197
- subCmd.argument(bracket, arg.help ?? '');
198
- positionalArgs.push(arg);
199
- } else {
200
- const flag = arg.required ? `--${arg.name} <value>` : `--${arg.name} [value]`;
201
- if (arg.required) subCmd.requiredOption(flag, arg.help ?? '');
202
- else if (arg.default != null) subCmd.option(flag, arg.help ?? '', String(arg.default));
203
- else subCmd.option(flag, arg.help ?? '');
204
- }
205
- }
206
- subCmd.option('-f, --format <fmt>', 'Output format: table, json, yaml, md, csv', 'table').option('-v, --verbose', 'Debug output', false);
207
-
208
- subCmd.action(async (...actionArgs: any[]) => {
209
- // Commander passes positional args first, then options object, then the Command
210
- const actionOpts = actionArgs[positionalArgs.length] ?? {};
211
- const startTime = Date.now();
212
- const kwargs: Record<string, any> = {};
213
-
214
- // Collect positional args
215
- for (let i = 0; i < positionalArgs.length; i++) {
216
- const arg = positionalArgs[i];
217
- const v = actionArgs[i];
218
- if (v !== undefined) kwargs[arg.name] = v;
219
- }
220
-
221
- // Collect named options
222
- for (const arg of cmd.args) {
223
- if (arg.positional) continue;
224
- const camelName = arg.name.replace(/-([a-z])/g, (_m, ch: string) => ch.toUpperCase());
225
- const v = actionOpts[arg.name] ?? actionOpts[camelName];
226
- if (v !== undefined) kwargs[arg.name] = v;
227
- }
375
+ // ── Unknown command fallback ──────────────────────────────────────────────
228
376
 
229
- try {
230
- if (actionOpts.verbose) process.env.OPENCLI_VERBOSE = '1';
231
- let result: any;
232
- if (shouldUseBrowserSession(cmd)) {
233
- const BrowserFactory = process.env.OPENCLI_CDP_ENDPOINT ? CDPBridge : BrowserBridge;
234
- result = await browserSession(BrowserFactory as any, async (page) => {
235
- // Cookie/header strategies require same-origin context for credentialed fetch.
236
- if ((cmd.strategy === Strategy.COOKIE || cmd.strategy === Strategy.HEADER) && cmd.domain) {
237
- try { await page.goto(`https://${cmd.domain}`); await page.wait(2); } catch {}
238
- }
239
- return runWithTimeout(executeCommand(cmd, page, kwargs, actionOpts.verbose), { timeout: cmd.timeoutSeconds ?? DEFAULT_BROWSER_COMMAND_TIMEOUT, label: fullName(cmd) });
240
- }, { workspace: `site:${cmd.site}` });
241
- } else { result = await executeCommand(cmd, null, kwargs, actionOpts.verbose); }
242
- if (actionOpts.verbose && (!result || (Array.isArray(result) && result.length === 0))) {
243
- console.error(chalk.yellow(`[Verbose] Warning: Command returned an empty result. If the website structural API changed or requires authentication, check the network or update the adapter.`));
244
- }
245
- const resolved = getRegistry().get(fullName(cmd)) ?? cmd;
246
- renderOutput(result, { fmt: actionOpts.format, columns: resolved.columns, title: `${resolved.site}/${resolved.name}`, elapsed: (Date.now() - startTime) / 1000, source: fullName(resolved), footerExtra: resolved.footerExtra?.(kwargs) });
247
- } catch (err: any) {
248
- if (err instanceof CliError) {
249
- console.error(chalk.red(`Error [${err.code}]: ${err.message}`));
250
- if (err.hint) console.error(chalk.yellow(`Hint: ${err.hint}`));
251
- } else if (actionOpts.verbose && err.stack) {
252
- console.error(chalk.red(err.stack));
253
- } else {
254
- console.error(chalk.red(`Error: ${err.message ?? err}`));
255
- }
256
- process.exitCode = 1;
257
- }
258
- });
259
- }
377
+ const DENY_LIST = new Set([
378
+ 'rm', 'sudo', 'dd', 'mkfs', 'fdisk', 'shutdown', 'reboot',
379
+ 'kill', 'killall', 'chmod', 'chown', 'passwd', 'su', 'mount',
380
+ 'umount', 'format', 'diskutil',
381
+ ]);
382
+
383
+ program.on('command:*', (operands: string[]) => {
384
+ const binary = operands[0];
385
+ if (DENY_LIST.has(binary)) {
386
+ console.error(chalk.red(`Refusing to register system command '${binary}'.`));
387
+ process.exitCode = 1;
388
+ return;
389
+ }
390
+ if (isBinaryInstalled(binary)) {
391
+ console.log(chalk.cyan(`🔹 Auto-discovered local CLI '${binary}'. Registering...`));
392
+ registerExternalCli(binary);
393
+ passthroughExternal(binary);
394
+ } else {
395
+ console.error(chalk.red(`error: unknown command '${binary}'`));
396
+ program.outputHelp();
397
+ process.exitCode = 1;
398
+ }
399
+ });
260
400
 
261
401
  program.parse();
262
402
  }
403
+
404
+ // ── Helpers ─────────────────────────────────────────────────────────────────
405
+
406
+ /** Infer a workspace-friendly hostname from a URL, with site override. */
407
+ function inferHost(url: string, site?: string): string {
408
+ if (site) return site;
409
+ try { return new URL(url).host; } catch { return 'default'; }
410
+ }
@@ -0,0 +1,95 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { getRegistry } from '../../registry.js';
3
+ import './search.js';
4
+ import './top.js';
5
+
6
+ describe('apple-podcasts search command', () => {
7
+ beforeEach(() => {
8
+ vi.restoreAllMocks();
9
+ });
10
+
11
+ it('uses the positional query argument for the iTunes search request', async () => {
12
+ const cmd = getRegistry().get('apple-podcasts/search');
13
+ expect(cmd?.func).toBeTypeOf('function');
14
+
15
+ const fetchMock = vi.fn().mockResolvedValue({
16
+ ok: true,
17
+ json: () => Promise.resolve({
18
+ results: [
19
+ {
20
+ collectionId: 42,
21
+ collectionName: 'Machine Learning Guide',
22
+ artistName: 'OpenCLI',
23
+ trackCount: 12,
24
+ primaryGenreName: 'Technology',
25
+ },
26
+ ],
27
+ }),
28
+ });
29
+ vi.stubGlobal('fetch', fetchMock);
30
+
31
+ const result = await cmd!.func!(null as any, {
32
+ query: 'machine learning',
33
+ keyword: 'sports',
34
+ limit: 5,
35
+ });
36
+
37
+ expect(fetchMock).toHaveBeenCalledWith(
38
+ 'https://itunes.apple.com/search?term=machine%20learning&media=podcast&limit=5',
39
+ );
40
+ expect(result).toEqual([
41
+ {
42
+ id: 42,
43
+ title: 'Machine Learning Guide',
44
+ author: 'OpenCLI',
45
+ episodes: 12,
46
+ genre: 'Technology',
47
+ },
48
+ ]);
49
+ });
50
+ });
51
+
52
+ describe('apple-podcasts top command', () => {
53
+ beforeEach(() => {
54
+ vi.restoreAllMocks();
55
+ });
56
+
57
+ it('uses the canonical Apple charts host and maps ranked results', async () => {
58
+ const cmd = getRegistry().get('apple-podcasts/top');
59
+ expect(cmd?.func).toBeTypeOf('function');
60
+
61
+ const fetchMock = vi.fn().mockResolvedValue({
62
+ ok: true,
63
+ json: () => Promise.resolve({
64
+ feed: {
65
+ results: [
66
+ { id: '100', name: 'Top Show', artistName: 'Host A' },
67
+ { id: '101', name: 'Second Show', artistName: 'Host B' },
68
+ ],
69
+ },
70
+ }),
71
+ });
72
+ vi.stubGlobal('fetch', fetchMock);
73
+
74
+ const result = await cmd!.func!(null as any, { country: 'US', limit: 2 });
75
+
76
+ expect(fetchMock).toHaveBeenCalledWith(
77
+ 'https://rss.marketingtools.apple.com/api/v2/us/podcasts/top/2/podcasts.json',
78
+ );
79
+ expect(result).toEqual([
80
+ { rank: 1, title: 'Top Show', author: 'Host A', id: '100' },
81
+ { rank: 2, title: 'Second Show', author: 'Host B', id: '101' },
82
+ ]);
83
+ });
84
+
85
+ it('normalizes network failures into CliError output', async () => {
86
+ const cmd = getRegistry().get('apple-podcasts/top');
87
+ expect(cmd?.func).toBeTypeOf('function');
88
+
89
+ vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('socket hang up')));
90
+
91
+ await expect(cmd!.func!(null as any, { country: 'us', limit: 3 })).rejects.toThrow(
92
+ 'Unable to reach Apple Podcasts charts for US',
93
+ );
94
+ });
95
+ });
@@ -9,12 +9,12 @@ cli({
9
9
  strategy: Strategy.PUBLIC,
10
10
  browser: false,
11
11
  args: [
12
- { name: 'keyword', positional: true, required: true, help: 'Search keyword' },
12
+ { name: 'query', positional: true, required: true, help: 'Search keyword' },
13
13
  { name: 'limit', type: 'int', default: 10, help: 'Max results' },
14
14
  ],
15
15
  columns: ['id', 'title', 'author', 'episodes', 'genre'],
16
16
  func: async (_page, args) => {
17
- const term = encodeURIComponent(args.keyword);
17
+ const term = encodeURIComponent(args.query);
18
18
  const limit = Math.max(1, Math.min(Number(args.limit), 25));
19
19
  const data = await itunesFetch(`/search?term=${term}&media=podcast&limit=${limit}`);
20
20
  if (!data.results?.length) throw new CliError('NOT_FOUND', 'No podcasts found', `Try a different keyword`);