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