@jackwener/opencli 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (769) hide show
  1. package/.agents/skills/cross-project-adapter-migration/SKILL.md +2 -2
  2. package/.github/pull_request_template.md +7 -0
  3. package/.github/workflows/doc-check.yml +36 -0
  4. package/.github/workflows/docs.yml +7 -42
  5. package/CHANGELOG.md +23 -0
  6. package/CLI-EXPLORER.md +9 -8
  7. package/CONTRIBUTING.md +39 -1
  8. package/README.md +33 -19
  9. package/README.zh-CN.md +64 -27
  10. package/SKILL.md +102 -33
  11. package/dist/browser/cdp.d.ts +4 -4
  12. package/dist/browser/cdp.js +45 -17
  13. package/dist/browser/daemon-client.d.ts +2 -1
  14. package/dist/browser/dom-helpers.js +38 -7
  15. package/dist/browser/dom-snapshot.d.ts +86 -0
  16. package/dist/browser/dom-snapshot.js +729 -0
  17. package/dist/browser/dom-snapshot.test.d.ts +11 -0
  18. package/dist/browser/dom-snapshot.test.js +212 -0
  19. package/dist/browser/index.d.ts +2 -0
  20. package/dist/browser/index.js +1 -0
  21. package/dist/browser/page.d.ts +18 -25
  22. package/dist/browser/page.js +44 -5
  23. package/dist/build-manifest.d.ts +11 -4
  24. package/dist/build-manifest.js +79 -34
  25. package/dist/build-manifest.test.js +58 -2
  26. package/dist/cli-manifest.json +4273 -1771
  27. package/dist/cli.d.ts +6 -0
  28. package/dist/cli.js +255 -162
  29. package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
  30. package/dist/clis/apple-podcasts/commands.test.js +76 -0
  31. package/dist/clis/apple-podcasts/search.js +2 -2
  32. package/dist/clis/apple-podcasts/top.js +9 -2
  33. package/dist/clis/arxiv/search.js +1 -1
  34. package/dist/clis/barchart/greeks.js +1 -1
  35. package/dist/clis/barchart/options.js +1 -1
  36. package/dist/clis/barchart/quote.js +1 -1
  37. package/dist/clis/bilibili/download.js +1 -1
  38. package/dist/clis/bilibili/dynamic.js +1 -1
  39. package/dist/clis/bilibili/favorite.js +1 -1
  40. package/dist/clis/bilibili/feed.js +1 -1
  41. package/dist/clis/bilibili/following.js +2 -2
  42. package/dist/clis/bilibili/history.js +1 -1
  43. package/dist/clis/bilibili/me.js +1 -1
  44. package/dist/clis/bilibili/ranking.js +1 -1
  45. package/dist/clis/bilibili/search.js +3 -3
  46. package/dist/clis/bilibili/subtitle.js +2 -2
  47. package/dist/clis/bilibili/user-videos.js +2 -2
  48. package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
  49. package/dist/clis/bloomberg/businessweek.js +17 -0
  50. package/dist/clis/bloomberg/economics.js +17 -0
  51. package/dist/clis/bloomberg/feeds.d.ts +1 -0
  52. package/dist/clis/bloomberg/feeds.js +15 -0
  53. package/dist/clis/bloomberg/industries.d.ts +1 -0
  54. package/dist/clis/bloomberg/industries.js +17 -0
  55. package/dist/clis/bloomberg/main.d.ts +1 -0
  56. package/dist/clis/bloomberg/main.js +17 -0
  57. package/dist/clis/bloomberg/markets.d.ts +1 -0
  58. package/dist/clis/bloomberg/markets.js +17 -0
  59. package/dist/clis/bloomberg/news.d.ts +1 -0
  60. package/dist/clis/bloomberg/news.js +105 -0
  61. package/dist/clis/bloomberg/opinions.d.ts +1 -0
  62. package/dist/clis/bloomberg/opinions.js +17 -0
  63. package/dist/clis/bloomberg/politics.d.ts +1 -0
  64. package/dist/clis/bloomberg/politics.js +17 -0
  65. package/dist/clis/bloomberg/tech.d.ts +1 -0
  66. package/dist/clis/bloomberg/tech.js +17 -0
  67. package/dist/clis/bloomberg/utils.d.ts +34 -0
  68. package/dist/clis/bloomberg/utils.js +364 -0
  69. package/dist/clis/bloomberg/utils.test.d.ts +1 -0
  70. package/dist/clis/bloomberg/utils.test.js +129 -0
  71. package/dist/clis/boss/batchgreet.js +12 -99
  72. package/dist/clis/boss/chatlist.js +9 -26
  73. package/dist/clis/boss/chatmsg.js +11 -42
  74. package/dist/clis/boss/common.d.ts +92 -0
  75. package/dist/clis/boss/common.js +223 -0
  76. package/dist/clis/boss/detail.js +8 -50
  77. package/dist/clis/boss/exchange.js +13 -79
  78. package/dist/clis/boss/greet.js +20 -147
  79. package/dist/clis/boss/invite.js +26 -121
  80. package/dist/clis/boss/joblist.js +6 -31
  81. package/dist/clis/boss/mark.js +12 -85
  82. package/dist/clis/boss/recommend.js +10 -49
  83. package/dist/clis/boss/resume.js +18 -118
  84. package/dist/clis/boss/search.js +13 -61
  85. package/dist/clis/boss/send.js +18 -152
  86. package/dist/clis/boss/stats.js +20 -71
  87. package/dist/clis/chaoxing/assignments.js +1 -1
  88. package/dist/clis/chaoxing/exams.js +1 -1
  89. package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
  90. package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
  91. package/dist/clis/chaoxing/utils.test.d.ts +1 -0
  92. package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
  93. package/dist/clis/chatgpt/read.js +1 -1
  94. package/dist/clis/chatwise/export.js +1 -1
  95. package/dist/clis/chatwise/model.js +2 -2
  96. package/dist/clis/chatwise/screenshot.js +1 -1
  97. package/dist/clis/codex/export.js +1 -1
  98. package/dist/clis/codex/model.js +2 -2
  99. package/dist/clis/codex/screenshot.js +1 -1
  100. package/dist/clis/coupang/add-to-cart.js +3 -4
  101. package/dist/clis/coupang/search.js +2 -4
  102. package/dist/clis/coupang/utils.test.d.ts +1 -0
  103. package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
  104. package/dist/clis/ctrip/search.js +1 -1
  105. package/dist/clis/cursor/export.js +1 -1
  106. package/dist/clis/cursor/model.js +2 -2
  107. package/dist/clis/cursor/screenshot.js +1 -1
  108. package/dist/clis/devto/tag.yaml +34 -0
  109. package/dist/clis/devto/top.yaml +29 -0
  110. package/dist/clis/devto/user.yaml +33 -0
  111. package/dist/clis/douban/book-hot.d.ts +1 -0
  112. package/dist/clis/douban/book-hot.js +14 -0
  113. package/dist/clis/douban/marks.d.ts +1 -0
  114. package/dist/clis/douban/marks.js +115 -0
  115. package/dist/clis/douban/movie-hot.d.ts +1 -0
  116. package/dist/clis/douban/movie-hot.js +14 -0
  117. package/dist/clis/douban/reviews.d.ts +1 -0
  118. package/dist/clis/douban/reviews.js +106 -0
  119. package/dist/clis/douban/search.d.ts +1 -0
  120. package/dist/clis/douban/search.js +16 -0
  121. package/dist/clis/douban/shared.d.ts +4 -0
  122. package/dist/clis/douban/shared.js +155 -0
  123. package/dist/clis/douban/subject.yaml +76 -0
  124. package/dist/clis/douban/top250.yaml +70 -0
  125. package/dist/clis/douban/utils.d.ts +35 -0
  126. package/dist/clis/douban/utils.js +48 -0
  127. package/dist/clis/facebook/add-friend.yaml +43 -0
  128. package/dist/clis/facebook/events.yaml +44 -0
  129. package/dist/clis/facebook/feed.yaml +63 -0
  130. package/dist/clis/facebook/friends.yaml +42 -0
  131. package/dist/clis/facebook/groups.yaml +50 -0
  132. package/dist/clis/facebook/join-group.yaml +44 -0
  133. package/dist/clis/facebook/memories.yaml +39 -0
  134. package/dist/clis/facebook/notifications.yaml +40 -0
  135. package/dist/clis/facebook/profile.yaml +37 -0
  136. package/dist/clis/facebook/search.yaml +46 -0
  137. package/dist/clis/google/news.d.ts +5 -0
  138. package/dist/clis/google/news.js +58 -0
  139. package/dist/clis/google/search.d.ts +10 -0
  140. package/dist/clis/google/search.js +127 -0
  141. package/dist/clis/google/suggest.d.ts +5 -0
  142. package/dist/clis/google/suggest.js +34 -0
  143. package/dist/clis/google/trends.d.ts +5 -0
  144. package/dist/clis/google/trends.js +38 -0
  145. package/dist/clis/google/utils.d.ts +9 -0
  146. package/dist/clis/google/utils.js +23 -0
  147. package/dist/clis/google/utils.test.d.ts +1 -0
  148. package/dist/clis/google/utils.test.js +75 -0
  149. package/dist/clis/grok/ask.d.ts +14 -0
  150. package/dist/clis/grok/ask.js +257 -65
  151. package/dist/clis/grok/ask.test.d.ts +1 -0
  152. package/dist/clis/grok/ask.test.js +36 -0
  153. package/dist/clis/instagram/comment.yaml +52 -0
  154. package/dist/clis/instagram/explore.yaml +43 -0
  155. package/dist/clis/instagram/follow.yaml +41 -0
  156. package/dist/clis/instagram/followers.yaml +51 -0
  157. package/dist/clis/instagram/following.yaml +51 -0
  158. package/dist/clis/instagram/like.yaml +46 -0
  159. package/dist/clis/instagram/profile.yaml +42 -0
  160. package/dist/clis/instagram/save.yaml +46 -0
  161. package/dist/clis/instagram/saved.yaml +40 -0
  162. package/dist/clis/instagram/search.yaml +43 -0
  163. package/dist/clis/instagram/unfollow.yaml +38 -0
  164. package/dist/clis/instagram/unlike.yaml +46 -0
  165. package/dist/clis/instagram/unsave.yaml +46 -0
  166. package/dist/clis/instagram/user.yaml +54 -0
  167. package/dist/clis/jike/comment.js +2 -3
  168. package/dist/clis/jike/create.js +1 -2
  169. package/dist/clis/jike/feed.js +0 -1
  170. package/dist/clis/jike/like.js +1 -2
  171. package/dist/clis/jike/notifications.js +0 -1
  172. package/dist/clis/jike/post.yaml +1 -0
  173. package/dist/clis/jike/repost.js +2 -3
  174. package/dist/clis/jike/search.js +2 -3
  175. package/dist/clis/jike/topic.yaml +1 -0
  176. package/dist/clis/jike/user.yaml +1 -0
  177. package/dist/clis/jimeng/generate.yaml +1 -0
  178. package/dist/clis/jimeng/history.yaml +0 -1
  179. package/dist/clis/linkedin/search.js +7 -7
  180. package/dist/clis/linux-do/category.yaml +2 -0
  181. package/dist/clis/linux-do/search.yaml +4 -3
  182. package/dist/clis/linux-do/topic.yaml +1 -0
  183. package/dist/clis/lobsters/active.yaml +29 -0
  184. package/dist/clis/lobsters/hot.yaml +29 -0
  185. package/dist/clis/lobsters/newest.yaml +29 -0
  186. package/dist/clis/lobsters/tag.yaml +34 -0
  187. package/dist/clis/medium/feed.d.ts +1 -0
  188. package/dist/clis/medium/feed.js +15 -0
  189. package/dist/clis/medium/search.d.ts +1 -0
  190. package/dist/clis/medium/search.js +15 -0
  191. package/dist/clis/medium/shared.d.ts +5 -0
  192. package/dist/clis/medium/shared.js +78 -0
  193. package/dist/clis/medium/user.d.ts +1 -0
  194. package/dist/clis/medium/user.js +15 -0
  195. package/dist/clis/notion/export.js +1 -1
  196. package/dist/clis/reddit/comment.js +3 -4
  197. package/dist/clis/reddit/read.js +4 -5
  198. package/dist/clis/reddit/save.js +2 -3
  199. package/dist/clis/reddit/saved.js +0 -1
  200. package/dist/clis/reddit/search.yaml +1 -0
  201. package/dist/clis/reddit/subreddit.yaml +1 -0
  202. package/dist/clis/reddit/subscribe.js +1 -2
  203. package/dist/clis/reddit/upvote.js +2 -3
  204. package/dist/clis/reddit/upvoted.js +0 -1
  205. package/dist/clis/reddit/user-comments.yaml +1 -0
  206. package/dist/clis/reddit/user-posts.yaml +1 -0
  207. package/dist/clis/reddit/user.yaml +1 -0
  208. package/dist/clis/reuters/search.js +1 -1
  209. package/dist/clis/sinablog/article.d.ts +1 -0
  210. package/dist/clis/sinablog/article.js +14 -0
  211. package/dist/clis/sinablog/hot.d.ts +1 -0
  212. package/dist/clis/sinablog/hot.js +14 -0
  213. package/dist/clis/sinablog/search.d.ts +1 -0
  214. package/dist/clis/sinablog/search.js +51 -0
  215. package/dist/clis/sinablog/shared.d.ts +7 -0
  216. package/dist/clis/sinablog/shared.js +187 -0
  217. package/dist/clis/sinablog/user.d.ts +1 -0
  218. package/dist/clis/sinablog/user.js +15 -0
  219. package/dist/clis/smzdm/search.js +2 -3
  220. package/dist/clis/stackoverflow/search.yaml +1 -0
  221. package/dist/clis/steam/top-sellers.yaml +29 -0
  222. package/dist/clis/substack/feed.d.ts +1 -0
  223. package/dist/clis/substack/feed.js +15 -0
  224. package/dist/clis/substack/publication.d.ts +1 -0
  225. package/dist/clis/substack/publication.js +15 -0
  226. package/dist/clis/substack/search.d.ts +1 -0
  227. package/dist/clis/substack/search.js +77 -0
  228. package/dist/clis/substack/shared.d.ts +4 -0
  229. package/dist/clis/substack/shared.js +129 -0
  230. package/dist/clis/tiktok/comment.yaml +66 -0
  231. package/dist/clis/tiktok/explore.yaml +39 -0
  232. package/dist/clis/tiktok/follow.yaml +39 -0
  233. package/dist/clis/tiktok/following.yaml +46 -0
  234. package/dist/clis/tiktok/friends.yaml +47 -0
  235. package/dist/clis/tiktok/like.yaml +38 -0
  236. package/dist/clis/tiktok/live.yaml +51 -0
  237. package/dist/clis/tiktok/notifications.yaml +52 -0
  238. package/dist/clis/tiktok/profile.yaml +45 -0
  239. package/dist/clis/tiktok/save.yaml +34 -0
  240. package/dist/clis/tiktok/search.yaml +46 -0
  241. package/dist/clis/tiktok/unfollow.yaml +44 -0
  242. package/dist/clis/tiktok/unlike.yaml +38 -0
  243. package/dist/clis/tiktok/unsave.yaml +36 -0
  244. package/dist/clis/tiktok/user.yaml +44 -0
  245. package/dist/clis/twitter/accept.js +2 -2
  246. package/dist/clis/twitter/article.js +2 -2
  247. package/dist/clis/twitter/block.d.ts +1 -0
  248. package/dist/clis/twitter/block.js +88 -0
  249. package/dist/clis/twitter/delete.js +1 -1
  250. package/dist/clis/twitter/download.d.ts +1 -1
  251. package/dist/clis/twitter/download.js +3 -3
  252. package/dist/clis/twitter/followers.js +1 -1
  253. package/dist/clis/twitter/following.js +1 -1
  254. package/dist/clis/twitter/hide-reply.d.ts +1 -0
  255. package/dist/clis/twitter/hide-reply.js +66 -0
  256. package/dist/clis/twitter/like.js +1 -1
  257. package/dist/clis/twitter/post.js +1 -1
  258. package/dist/clis/twitter/reply-dm.js +1 -1
  259. package/dist/clis/twitter/reply.js +2 -2
  260. package/dist/clis/twitter/search.js +1 -1
  261. package/dist/clis/twitter/thread.js +2 -2
  262. package/dist/clis/twitter/timeline.d.ts +23 -0
  263. package/dist/clis/twitter/timeline.js +42 -14
  264. package/dist/clis/twitter/timeline.test.d.ts +1 -0
  265. package/dist/clis/twitter/timeline.test.js +102 -0
  266. package/dist/clis/twitter/trending.d.ts +1 -0
  267. package/dist/clis/twitter/trending.js +91 -0
  268. package/dist/clis/twitter/unblock.d.ts +1 -0
  269. package/dist/clis/twitter/unblock.js +71 -0
  270. package/dist/clis/v2ex/topic.yaml +1 -0
  271. package/dist/clis/weibo/hot.js +0 -1
  272. package/dist/clis/weread/book.js +1 -1
  273. package/dist/clis/weread/highlights.js +1 -1
  274. package/dist/clis/weread/notes.js +1 -1
  275. package/dist/clis/weread/search.js +1 -1
  276. package/dist/clis/wikipedia/random.d.ts +1 -0
  277. package/dist/clis/wikipedia/random.js +19 -0
  278. package/dist/clis/wikipedia/search.js +4 -4
  279. package/dist/clis/wikipedia/summary.js +4 -9
  280. package/dist/clis/wikipedia/trending.d.ts +1 -0
  281. package/dist/clis/wikipedia/trending.js +35 -0
  282. package/dist/clis/wikipedia/utils.d.ts +28 -0
  283. package/dist/clis/wikipedia/utils.js +13 -0
  284. package/dist/clis/xiaohongshu/creator-note-detail.d.ts +15 -0
  285. package/dist/clis/xiaohongshu/creator-note-detail.js +69 -5
  286. package/dist/clis/xiaohongshu/creator-note-detail.test.js +82 -33
  287. package/dist/clis/xiaohongshu/creator-notes.js +35 -5
  288. package/dist/clis/xiaohongshu/creator-notes.test.js +37 -6
  289. package/dist/clis/xiaohongshu/creator-profile.js +0 -1
  290. package/dist/clis/xiaohongshu/creator-stats.js +0 -1
  291. package/dist/clis/xiaohongshu/download.js +2 -3
  292. package/dist/clis/xiaohongshu/feed.yaml +0 -1
  293. package/dist/clis/xiaohongshu/notifications.yaml +0 -1
  294. package/dist/clis/xiaohongshu/search.js +2 -2
  295. package/dist/clis/xiaohongshu/user.js +1 -2
  296. package/dist/clis/xueqiu/earnings-date.yaml +69 -0
  297. package/dist/clis/xueqiu/search.yaml +2 -1
  298. package/dist/clis/xueqiu/stock.yaml +2 -0
  299. package/dist/clis/yahoo-finance/quote.js +1 -2
  300. package/dist/clis/youtube/search.js +1 -1
  301. package/dist/clis/youtube/transcript.js +1 -1
  302. package/dist/clis/youtube/video.js +1 -1
  303. package/dist/clis/zhihu/download.js +1 -2
  304. package/dist/clis/zhihu/question.js +1 -1
  305. package/dist/clis/zhihu/search.yaml +4 -3
  306. package/dist/commanderAdapter.d.ts +21 -0
  307. package/dist/commanderAdapter.js +117 -0
  308. package/dist/{engine.d.ts → discovery.d.ts} +6 -4
  309. package/dist/{engine.js → discovery.js} +93 -104
  310. package/dist/doctor.js +3 -1
  311. package/dist/doctor.test.js +46 -2
  312. package/dist/download/index.d.ts +2 -6
  313. package/dist/download/index.js +19 -46
  314. package/dist/engine.test.d.ts +0 -3
  315. package/dist/engine.test.js +80 -11
  316. package/dist/execution.d.ts +24 -0
  317. package/dist/execution.js +153 -0
  318. package/dist/explore.d.ts +76 -3
  319. package/dist/explore.js +132 -111
  320. package/dist/external-clis.yaml +48 -0
  321. package/dist/external.d.ts +7 -2
  322. package/dist/external.js +11 -14
  323. package/dist/generate.d.ts +41 -2
  324. package/dist/generate.js +5 -4
  325. package/dist/main.js +2 -1
  326. package/dist/pipeline/executor.d.ts +2 -2
  327. package/dist/pipeline/executor.js +2 -2
  328. package/dist/pipeline/executor.test.js +33 -6
  329. package/dist/pipeline/registry.d.ts +1 -1
  330. package/dist/pipeline/steps/browser.d.ts +7 -7
  331. package/dist/pipeline/steps/browser.js +21 -7
  332. package/dist/pipeline/steps/fetch.d.ts +1 -1
  333. package/dist/pipeline/steps/fetch.js +11 -7
  334. package/dist/pipeline/steps/transform.d.ts +6 -5
  335. package/dist/pipeline/steps/transform.js +30 -9
  336. package/dist/pipeline/template.d.ts +6 -6
  337. package/dist/pipeline/template.js +43 -5
  338. package/dist/pipeline/template.test.js +18 -0
  339. package/dist/pipeline/transform.test.js +11 -0
  340. package/dist/plugin.d.ts +31 -0
  341. package/dist/plugin.js +216 -0
  342. package/dist/plugin.test.d.ts +4 -0
  343. package/dist/plugin.test.js +76 -0
  344. package/dist/registry-api.d.ts +11 -0
  345. package/dist/registry-api.js +9 -0
  346. package/dist/registry.d.ts +13 -0
  347. package/dist/registry.js +8 -1
  348. package/dist/runtime.d.ts +5 -0
  349. package/dist/runtime.js +8 -0
  350. package/dist/serialization.d.ts +34 -0
  351. package/dist/serialization.js +63 -0
  352. package/dist/synthesize.d.ts +94 -4
  353. package/dist/synthesize.js +5 -4
  354. package/dist/types.d.ts +43 -27
  355. package/dist/validate.js +8 -2
  356. package/docs/.vitepress/config.mts +20 -7
  357. package/docs/adapters/browser/arxiv.md +27 -0
  358. package/docs/adapters/browser/barchart.md +33 -0
  359. package/docs/adapters/browser/bilibili.md +9 -0
  360. package/docs/adapters/browser/bloomberg.md +70 -0
  361. package/docs/adapters/browser/chaoxing.md +39 -0
  362. package/docs/adapters/browser/devto.md +35 -0
  363. package/docs/adapters/browser/douban.md +38 -0
  364. package/docs/adapters/browser/facebook.md +36 -0
  365. package/docs/adapters/browser/google.md +62 -0
  366. package/docs/adapters/browser/grok.md +53 -0
  367. package/docs/adapters/browser/hf.md +42 -0
  368. package/docs/adapters/browser/instagram.md +46 -0
  369. package/docs/adapters/browser/jike.md +45 -0
  370. package/docs/adapters/browser/jimeng.md +39 -0
  371. package/docs/adapters/browser/linux-do.md +45 -0
  372. package/docs/adapters/browser/lobsters.md +32 -0
  373. package/docs/adapters/browser/medium.md +32 -0
  374. package/docs/adapters/browser/reddit.md +9 -0
  375. package/docs/adapters/browser/sinablog.md +36 -0
  376. package/docs/adapters/browser/sinafinance.md +35 -0
  377. package/docs/adapters/browser/stackoverflow.md +35 -0
  378. package/docs/adapters/browser/steam.md +26 -0
  379. package/docs/adapters/browser/substack.md +38 -0
  380. package/docs/adapters/browser/tiktok.md +68 -0
  381. package/docs/adapters/browser/twitter.md +3 -0
  382. package/docs/adapters/browser/weread.md +48 -0
  383. package/docs/adapters/browser/wikipedia.md +39 -0
  384. package/docs/adapters/browser/xiaohongshu.md +5 -1
  385. package/docs/adapters/browser/xueqiu.md +10 -0
  386. package/docs/adapters/browser/yahoo-finance.md +6 -5
  387. package/docs/adapters/desktop/antigravity.md +6 -0
  388. package/docs/adapters/desktop/chatgpt.md +5 -4
  389. package/docs/adapters/desktop/codex.md +5 -1
  390. package/docs/adapters/desktop/cursor.md +4 -0
  391. package/docs/adapters/desktop/discord.md +7 -7
  392. package/docs/adapters/index.md +14 -4
  393. package/docs/advanced/download.md +4 -4
  394. package/docs/developer/architecture.md +17 -4
  395. package/docs/guide/getting-started.md +1 -0
  396. package/docs/guide/plugins.md +153 -0
  397. package/docs/zh/guide/plugins.md +107 -0
  398. package/extension/src/background.ts +18 -11
  399. package/package.json +10 -5
  400. package/scripts/check-doc-coverage.sh +69 -0
  401. package/scripts/clean-dist.cjs +13 -0
  402. package/scripts/copy-yaml.cjs +7 -0
  403. package/src/browser/cdp.ts +77 -32
  404. package/src/browser/daemon-client.ts +2 -1
  405. package/src/browser/dom-helpers.ts +38 -7
  406. package/src/browser/dom-snapshot.test.ts +249 -0
  407. package/src/browser/dom-snapshot.ts +770 -0
  408. package/src/browser/index.ts +2 -0
  409. package/src/browser/page.ts +57 -20
  410. package/src/build-manifest.test.ts +70 -2
  411. package/src/build-manifest.ts +114 -40
  412. package/src/cli.ts +287 -139
  413. package/src/clis/apple-podcasts/commands.test.ts +95 -0
  414. package/src/clis/apple-podcasts/search.ts +2 -2
  415. package/src/clis/apple-podcasts/top.ts +12 -2
  416. package/src/clis/arxiv/search.ts +1 -1
  417. package/src/clis/barchart/greeks.ts +1 -1
  418. package/src/clis/barchart/options.ts +1 -1
  419. package/src/clis/barchart/quote.ts +1 -1
  420. package/src/clis/bilibili/download.ts +1 -1
  421. package/src/clis/bilibili/dynamic.ts +1 -1
  422. package/src/clis/bilibili/favorite.ts +1 -1
  423. package/src/clis/bilibili/feed.ts +1 -1
  424. package/src/clis/bilibili/following.ts +2 -2
  425. package/src/clis/bilibili/history.ts +1 -1
  426. package/src/clis/bilibili/me.ts +1 -1
  427. package/src/clis/bilibili/ranking.ts +1 -1
  428. package/src/clis/bilibili/search.ts +3 -3
  429. package/src/clis/bilibili/subtitle.ts +2 -2
  430. package/src/clis/bilibili/user-videos.ts +2 -2
  431. package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
  432. package/src/clis/bloomberg/businessweek.ts +18 -0
  433. package/src/clis/bloomberg/economics.ts +18 -0
  434. package/src/clis/bloomberg/feeds.ts +16 -0
  435. package/src/clis/bloomberg/industries.ts +18 -0
  436. package/src/clis/bloomberg/main.ts +18 -0
  437. package/src/clis/bloomberg/markets.ts +18 -0
  438. package/src/clis/bloomberg/news.ts +136 -0
  439. package/src/clis/bloomberg/opinions.ts +18 -0
  440. package/src/clis/bloomberg/politics.ts +18 -0
  441. package/src/clis/bloomberg/tech.ts +18 -0
  442. package/src/clis/bloomberg/utils.test.ts +135 -0
  443. package/src/clis/bloomberg/utils.ts +429 -0
  444. package/src/clis/boss/batchgreet.ts +16 -108
  445. package/src/clis/boss/chatlist.ts +13 -27
  446. package/src/clis/boss/chatmsg.ts +16 -40
  447. package/src/clis/boss/common.ts +287 -0
  448. package/src/clis/boss/detail.ts +9 -55
  449. package/src/clis/boss/exchange.ts +15 -89
  450. package/src/clis/boss/greet.ts +25 -162
  451. package/src/clis/boss/invite.ts +36 -133
  452. package/src/clis/boss/joblist.ts +7 -36
  453. package/src/clis/boss/mark.ts +13 -94
  454. package/src/clis/boss/recommend.ts +12 -57
  455. package/src/clis/boss/resume.ts +19 -124
  456. package/src/clis/boss/search.ts +14 -67
  457. package/src/clis/boss/send.ts +22 -162
  458. package/src/clis/boss/stats.ts +21 -76
  459. package/src/clis/chaoxing/assignments.ts +1 -1
  460. package/src/clis/chaoxing/exams.ts +1 -1
  461. package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
  462. package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
  463. package/src/clis/chatgpt/read.ts +1 -1
  464. package/src/clis/chatwise/export.ts +1 -1
  465. package/src/clis/chatwise/model.ts +2 -2
  466. package/src/clis/chatwise/screenshot.ts +1 -1
  467. package/src/clis/codex/export.ts +1 -1
  468. package/src/clis/codex/model.ts +2 -2
  469. package/src/clis/codex/screenshot.ts +1 -1
  470. package/src/clis/coupang/add-to-cart.ts +3 -4
  471. package/src/clis/coupang/search.ts +2 -4
  472. package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
  473. package/src/clis/ctrip/search.ts +1 -1
  474. package/src/clis/cursor/export.ts +1 -1
  475. package/src/clis/cursor/model.ts +2 -2
  476. package/src/clis/cursor/screenshot.ts +1 -1
  477. package/src/clis/devto/tag.yaml +34 -0
  478. package/src/clis/devto/top.yaml +29 -0
  479. package/src/clis/devto/user.yaml +33 -0
  480. package/src/clis/douban/book-hot.ts +15 -0
  481. package/src/clis/douban/marks.ts +135 -0
  482. package/src/clis/douban/movie-hot.ts +15 -0
  483. package/src/clis/douban/reviews.ts +127 -0
  484. package/src/clis/douban/search.ts +17 -0
  485. package/src/clis/douban/shared.ts +165 -0
  486. package/src/clis/douban/subject.yaml +76 -0
  487. package/src/clis/douban/top250.yaml +70 -0
  488. package/src/clis/douban/utils.ts +81 -0
  489. package/src/clis/facebook/add-friend.yaml +43 -0
  490. package/src/clis/facebook/events.yaml +44 -0
  491. package/src/clis/facebook/feed.yaml +63 -0
  492. package/src/clis/facebook/friends.yaml +42 -0
  493. package/src/clis/facebook/groups.yaml +50 -0
  494. package/src/clis/facebook/join-group.yaml +44 -0
  495. package/src/clis/facebook/memories.yaml +39 -0
  496. package/src/clis/facebook/notifications.yaml +40 -0
  497. package/src/clis/facebook/profile.yaml +37 -0
  498. package/src/clis/facebook/search.yaml +46 -0
  499. package/src/clis/google/news.ts +66 -0
  500. package/src/clis/google/search.ts +133 -0
  501. package/src/clis/google/suggest.ts +40 -0
  502. package/src/clis/google/trends.ts +44 -0
  503. package/src/clis/google/utils.test.ts +82 -0
  504. package/src/clis/google/utils.ts +24 -0
  505. package/src/clis/grok/ask.test.ts +53 -0
  506. package/src/clis/grok/ask.ts +300 -69
  507. package/src/clis/instagram/comment.yaml +52 -0
  508. package/src/clis/instagram/explore.yaml +43 -0
  509. package/src/clis/instagram/follow.yaml +41 -0
  510. package/src/clis/instagram/followers.yaml +51 -0
  511. package/src/clis/instagram/following.yaml +51 -0
  512. package/src/clis/instagram/like.yaml +46 -0
  513. package/src/clis/instagram/profile.yaml +42 -0
  514. package/src/clis/instagram/save.yaml +46 -0
  515. package/src/clis/instagram/saved.yaml +40 -0
  516. package/src/clis/instagram/search.yaml +43 -0
  517. package/src/clis/instagram/unfollow.yaml +38 -0
  518. package/src/clis/instagram/unlike.yaml +46 -0
  519. package/src/clis/instagram/unsave.yaml +46 -0
  520. package/src/clis/instagram/user.yaml +54 -0
  521. package/src/clis/jike/comment.ts +2 -3
  522. package/src/clis/jike/create.ts +1 -2
  523. package/src/clis/jike/feed.ts +0 -1
  524. package/src/clis/jike/like.ts +1 -2
  525. package/src/clis/jike/notifications.ts +0 -1
  526. package/src/clis/jike/post.yaml +1 -0
  527. package/src/clis/jike/repost.ts +2 -3
  528. package/src/clis/jike/search.ts +2 -3
  529. package/src/clis/jike/topic.yaml +1 -0
  530. package/src/clis/jike/user.yaml +1 -0
  531. package/src/clis/jimeng/generate.yaml +1 -0
  532. package/src/clis/jimeng/history.yaml +0 -1
  533. package/src/clis/linkedin/search.ts +7 -7
  534. package/src/clis/linux-do/category.yaml +2 -0
  535. package/src/clis/linux-do/search.yaml +4 -3
  536. package/src/clis/linux-do/topic.yaml +1 -0
  537. package/src/clis/lobsters/active.yaml +29 -0
  538. package/src/clis/lobsters/hot.yaml +29 -0
  539. package/src/clis/lobsters/newest.yaml +29 -0
  540. package/src/clis/lobsters/tag.yaml +34 -0
  541. package/src/clis/medium/feed.ts +16 -0
  542. package/src/clis/medium/search.ts +16 -0
  543. package/src/clis/medium/shared.ts +83 -0
  544. package/src/clis/medium/user.ts +16 -0
  545. package/src/clis/notion/export.ts +1 -1
  546. package/src/clis/reddit/comment.ts +3 -4
  547. package/src/clis/reddit/read.ts +4 -5
  548. package/src/clis/reddit/save.ts +2 -3
  549. package/src/clis/reddit/saved.ts +0 -1
  550. package/src/clis/reddit/search.yaml +1 -0
  551. package/src/clis/reddit/subreddit.yaml +1 -0
  552. package/src/clis/reddit/subscribe.ts +1 -2
  553. package/src/clis/reddit/upvote.ts +2 -3
  554. package/src/clis/reddit/upvoted.ts +0 -1
  555. package/src/clis/reddit/user-comments.yaml +1 -0
  556. package/src/clis/reddit/user-posts.yaml +1 -0
  557. package/src/clis/reddit/user.yaml +1 -0
  558. package/src/clis/reuters/search.ts +1 -1
  559. package/src/clis/sinablog/article.ts +15 -0
  560. package/src/clis/sinablog/hot.ts +15 -0
  561. package/src/clis/sinablog/search.ts +56 -0
  562. package/src/clis/sinablog/shared.ts +198 -0
  563. package/src/clis/sinablog/user.ts +16 -0
  564. package/src/clis/smzdm/search.ts +2 -3
  565. package/src/clis/stackoverflow/search.yaml +1 -0
  566. package/src/clis/steam/top-sellers.yaml +29 -0
  567. package/src/clis/substack/feed.ts +16 -0
  568. package/src/clis/substack/publication.ts +16 -0
  569. package/src/clis/substack/search.ts +91 -0
  570. package/src/clis/substack/shared.ts +132 -0
  571. package/src/clis/tiktok/comment.yaml +66 -0
  572. package/src/clis/tiktok/explore.yaml +39 -0
  573. package/src/clis/tiktok/follow.yaml +39 -0
  574. package/src/clis/tiktok/following.yaml +46 -0
  575. package/src/clis/tiktok/friends.yaml +47 -0
  576. package/src/clis/tiktok/like.yaml +38 -0
  577. package/src/clis/tiktok/live.yaml +51 -0
  578. package/src/clis/tiktok/notifications.yaml +52 -0
  579. package/src/clis/tiktok/profile.yaml +45 -0
  580. package/src/clis/tiktok/save.yaml +34 -0
  581. package/src/clis/tiktok/search.yaml +46 -0
  582. package/src/clis/tiktok/unfollow.yaml +44 -0
  583. package/src/clis/tiktok/unlike.yaml +38 -0
  584. package/src/clis/tiktok/unsave.yaml +36 -0
  585. package/src/clis/tiktok/user.yaml +44 -0
  586. package/src/clis/twitter/accept.ts +2 -2
  587. package/src/clis/twitter/article.ts +2 -2
  588. package/src/clis/twitter/block.ts +92 -0
  589. package/src/clis/twitter/delete.ts +1 -1
  590. package/src/clis/twitter/download.ts +3 -3
  591. package/src/clis/twitter/followers.ts +1 -1
  592. package/src/clis/twitter/following.ts +1 -1
  593. package/src/clis/twitter/hide-reply.ts +70 -0
  594. package/src/clis/twitter/like.ts +1 -1
  595. package/src/clis/twitter/post.ts +1 -1
  596. package/src/clis/twitter/reply-dm.ts +1 -1
  597. package/src/clis/twitter/reply.ts +2 -2
  598. package/src/clis/twitter/search.ts +1 -1
  599. package/src/clis/twitter/thread.ts +2 -2
  600. package/src/clis/twitter/timeline.test.ts +109 -0
  601. package/src/clis/twitter/timeline.ts +59 -19
  602. package/src/clis/twitter/trending.ts +113 -0
  603. package/src/clis/twitter/unblock.ts +75 -0
  604. package/src/clis/v2ex/topic.yaml +1 -0
  605. package/src/clis/weibo/hot.ts +0 -1
  606. package/src/clis/weread/book.ts +1 -1
  607. package/src/clis/weread/highlights.ts +1 -1
  608. package/src/clis/weread/notes.ts +1 -1
  609. package/src/clis/weread/search.ts +1 -1
  610. package/src/clis/wikipedia/random.ts +19 -0
  611. package/src/clis/wikipedia/search.ts +11 -5
  612. package/src/clis/wikipedia/summary.ts +4 -9
  613. package/src/clis/wikipedia/trending.ts +41 -0
  614. package/src/clis/wikipedia/utils.ts +31 -0
  615. package/src/clis/xiaohongshu/creator-note-detail.test.ts +84 -33
  616. package/src/clis/xiaohongshu/creator-note-detail.ts +89 -5
  617. package/src/clis/xiaohongshu/creator-notes.test.ts +41 -6
  618. package/src/clis/xiaohongshu/creator-notes.ts +44 -5
  619. package/src/clis/xiaohongshu/creator-profile.ts +0 -1
  620. package/src/clis/xiaohongshu/creator-stats.ts +0 -1
  621. package/src/clis/xiaohongshu/download.ts +2 -3
  622. package/src/clis/xiaohongshu/feed.yaml +0 -1
  623. package/src/clis/xiaohongshu/notifications.yaml +0 -1
  624. package/src/clis/xiaohongshu/search.ts +2 -2
  625. package/src/clis/xiaohongshu/user.ts +1 -2
  626. package/src/clis/xueqiu/earnings-date.yaml +69 -0
  627. package/src/clis/xueqiu/search.yaml +2 -1
  628. package/src/clis/xueqiu/stock.yaml +2 -0
  629. package/src/clis/yahoo-finance/quote.ts +1 -2
  630. package/src/clis/youtube/search.ts +1 -1
  631. package/src/clis/youtube/transcript.ts +1 -1
  632. package/src/clis/youtube/video.ts +1 -1
  633. package/src/clis/zhihu/download.ts +1 -2
  634. package/src/clis/zhihu/question.ts +1 -1
  635. package/src/clis/zhihu/search.yaml +4 -3
  636. package/src/commanderAdapter.ts +120 -0
  637. package/src/discovery.ts +277 -0
  638. package/src/doctor.test.ts +59 -2
  639. package/src/doctor.ts +4 -2
  640. package/src/download/index.ts +21 -54
  641. package/src/engine.test.ts +85 -11
  642. package/src/execution.ts +164 -0
  643. package/src/explore.ts +211 -117
  644. package/src/external-clis.yaml +9 -0
  645. package/src/external.ts +15 -12
  646. package/src/generate.ts +58 -9
  647. package/src/main.ts +2 -1
  648. package/src/pipeline/executor.test.ts +35 -6
  649. package/src/pipeline/executor.ts +11 -7
  650. package/src/pipeline/registry.ts +3 -3
  651. package/src/pipeline/steps/browser.ts +29 -15
  652. package/src/pipeline/steps/fetch.ts +18 -13
  653. package/src/pipeline/steps/transform.ts +40 -15
  654. package/src/pipeline/template.test.ts +18 -0
  655. package/src/pipeline/template.ts +86 -13
  656. package/src/pipeline/transform.test.ts +15 -2
  657. package/src/plugin.test.ts +86 -0
  658. package/src/plugin.ts +254 -0
  659. package/src/registry-api.ts +12 -0
  660. package/src/registry.ts +24 -1
  661. package/src/runtime.ts +9 -0
  662. package/src/serialization.ts +79 -0
  663. package/src/synthesize.ts +102 -21
  664. package/src/types.ts +45 -13
  665. package/src/validate.ts +19 -4
  666. package/tests/e2e/browser-public.test.ts +36 -0
  667. package/tests/e2e/public-commands.test.ts +119 -1
  668. package/dist/clis/feishu/new.d.ts +0 -1
  669. package/dist/clis/feishu/new.js +0 -27
  670. package/dist/clis/feishu/read.d.ts +0 -1
  671. package/dist/clis/feishu/read.js +0 -40
  672. package/dist/clis/feishu/search.d.ts +0 -1
  673. package/dist/clis/feishu/search.js +0 -30
  674. package/dist/clis/feishu/send.d.ts +0 -1
  675. package/dist/clis/feishu/send.js +0 -39
  676. package/dist/clis/feishu/status.d.ts +0 -1
  677. package/dist/clis/feishu/status.js +0 -28
  678. package/dist/clis/neteasemusic/like.d.ts +0 -1
  679. package/dist/clis/neteasemusic/like.js +0 -25
  680. package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
  681. package/dist/clis/neteasemusic/lyrics.js +0 -47
  682. package/dist/clis/neteasemusic/next.d.ts +0 -1
  683. package/dist/clis/neteasemusic/next.js +0 -26
  684. package/dist/clis/neteasemusic/play.d.ts +0 -1
  685. package/dist/clis/neteasemusic/play.js +0 -26
  686. package/dist/clis/neteasemusic/playing.d.ts +0 -1
  687. package/dist/clis/neteasemusic/playing.js +0 -59
  688. package/dist/clis/neteasemusic/playlist.d.ts +0 -1
  689. package/dist/clis/neteasemusic/playlist.js +0 -46
  690. package/dist/clis/neteasemusic/prev.d.ts +0 -1
  691. package/dist/clis/neteasemusic/prev.js +0 -25
  692. package/dist/clis/neteasemusic/search.d.ts +0 -1
  693. package/dist/clis/neteasemusic/search.js +0 -52
  694. package/dist/clis/neteasemusic/status.d.ts +0 -1
  695. package/dist/clis/neteasemusic/status.js +0 -16
  696. package/dist/clis/neteasemusic/volume.d.ts +0 -1
  697. package/dist/clis/neteasemusic/volume.js +0 -54
  698. package/dist/clis/twitter/trending.yaml +0 -46
  699. package/dist/clis/wechat/chats.d.ts +0 -1
  700. package/dist/clis/wechat/chats.js +0 -28
  701. package/dist/clis/wechat/contacts.d.ts +0 -1
  702. package/dist/clis/wechat/contacts.js +0 -28
  703. package/dist/clis/wechat/read.d.ts +0 -1
  704. package/dist/clis/wechat/read.js +0 -58
  705. package/dist/clis/wechat/search.d.ts +0 -1
  706. package/dist/clis/wechat/search.js +0 -31
  707. package/dist/clis/wechat/send.d.ts +0 -1
  708. package/dist/clis/wechat/send.js +0 -42
  709. package/dist/clis/wechat/status.d.ts +0 -1
  710. package/dist/clis/wechat/status.js +0 -29
  711. package/dist/pipeline.d.ts +0 -7
  712. package/dist/pipeline.js +0 -7
  713. package/docs/adapters/browser/github.md +0 -26
  714. package/docs/adapters/desktop/feishu.md +0 -20
  715. package/docs/adapters/desktop/neteasemusic.md +0 -31
  716. package/docs/adapters/desktop/wechat.md +0 -28
  717. package/docs/public/CNAME +0 -1
  718. package/src/clis/antigravity/README.md +0 -5
  719. package/src/clis/antigravity/README.zh-CN.md +0 -51
  720. package/src/clis/chaoxing/README.md +0 -14
  721. package/src/clis/chaoxing/README.zh-CN.md +0 -35
  722. package/src/clis/chatgpt/README.md +0 -5
  723. package/src/clis/chatgpt/README.zh-CN.md +0 -44
  724. package/src/clis/chatwise/README.md +0 -5
  725. package/src/clis/chatwise/README.zh-CN.md +0 -38
  726. package/src/clis/codex/README.md +0 -5
  727. package/src/clis/codex/README.zh-CN.md +0 -33
  728. package/src/clis/cursor/README.md +0 -5
  729. package/src/clis/cursor/README.zh-CN.md +0 -33
  730. package/src/clis/discord-app/README.md +0 -5
  731. package/src/clis/discord-app/README.zh-CN.md +0 -28
  732. package/src/clis/feishu/README.md +0 -5
  733. package/src/clis/feishu/README.zh-CN.md +0 -20
  734. package/src/clis/feishu/new.ts +0 -32
  735. package/src/clis/feishu/read.ts +0 -48
  736. package/src/clis/feishu/search.ts +0 -35
  737. package/src/clis/feishu/send.ts +0 -46
  738. package/src/clis/feishu/status.ts +0 -34
  739. package/src/clis/neteasemusic/README.md +0 -5
  740. package/src/clis/neteasemusic/README.zh-CN.md +0 -31
  741. package/src/clis/neteasemusic/like.ts +0 -28
  742. package/src/clis/neteasemusic/lyrics.ts +0 -53
  743. package/src/clis/neteasemusic/next.ts +0 -30
  744. package/src/clis/neteasemusic/play.ts +0 -30
  745. package/src/clis/neteasemusic/playing.ts +0 -62
  746. package/src/clis/neteasemusic/playlist.ts +0 -51
  747. package/src/clis/neteasemusic/prev.ts +0 -29
  748. package/src/clis/neteasemusic/search.ts +0 -58
  749. package/src/clis/neteasemusic/status.ts +0 -18
  750. package/src/clis/neteasemusic/volume.ts +0 -61
  751. package/src/clis/notion/README.md +0 -5
  752. package/src/clis/notion/README.zh-CN.md +0 -29
  753. package/src/clis/twitter/trending.yaml +0 -46
  754. package/src/clis/wechat/README.md +0 -5
  755. package/src/clis/wechat/README.zh-CN.md +0 -28
  756. package/src/clis/wechat/chats.ts +0 -33
  757. package/src/clis/wechat/contacts.ts +0 -33
  758. package/src/clis/wechat/read.ts +0 -72
  759. package/src/clis/wechat/search.ts +0 -36
  760. package/src/clis/wechat/send.ts +0 -49
  761. package/src/clis/wechat/status.ts +0 -35
  762. package/src/engine.ts +0 -274
  763. package/src/pipeline.ts +0 -8
  764. /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
  765. /package/dist/{chaoxing.test.d.ts → clis/bloomberg/businessweek.d.ts} +0 -0
  766. /package/dist/{coupang.test.d.ts → clis/bloomberg/economics.d.ts} +0 -0
  767. /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
  768. /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
  769. /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Command execution: validates args, manages browser sessions, runs commands.
3
+ *
4
+ * This is the single entry point for executing any CLI command. It handles:
5
+ * 1. Argument validation and coercion
6
+ * 2. Browser session lifecycle (if needed)
7
+ * 3. Domain pre-navigation for cookie/header strategies
8
+ * 4. Timeout enforcement
9
+ * 5. Lazy-loading of TS modules from manifest
10
+ */
11
+ import { Strategy, getRegistry, fullName } from './registry.js';
12
+ import { pathToFileURL } from 'node:url';
13
+ import { executePipeline } from './pipeline/index.js';
14
+ import { AdapterLoadError } from './errors.js';
15
+ import { shouldUseBrowserSession } from './capabilityRouting.js';
16
+ import { getBrowserFactory, browserSession, runWithTimeout, DEFAULT_BROWSER_COMMAND_TIMEOUT } from './runtime.js';
17
+ /** Set of TS module paths that have been loaded */
18
+ const _loadedModules = new Set();
19
+ function getErrorMessage(error) {
20
+ return error instanceof Error ? error.message : String(error);
21
+ }
22
+ /**
23
+ * Validates and coerces arguments based on the command's Arg definitions.
24
+ */
25
+ export function coerceAndValidateArgs(cmdArgs, kwargs) {
26
+ const result = { ...kwargs };
27
+ for (const argDef of cmdArgs) {
28
+ const val = result[argDef.name];
29
+ // 1. Check required
30
+ if (argDef.required && (val === undefined || val === null || val === '')) {
31
+ throw new Error(`Argument "${argDef.name}" is required.\n${argDef.help ? `Hint: ${argDef.help}` : ''}`);
32
+ }
33
+ if (val !== undefined && val !== null) {
34
+ // 2. Type coercion
35
+ if (argDef.type === 'int' || argDef.type === 'number') {
36
+ const num = Number(val);
37
+ if (Number.isNaN(num)) {
38
+ throw new Error(`Argument "${argDef.name}" must be a valid number. Received: "${val}"`);
39
+ }
40
+ result[argDef.name] = num;
41
+ }
42
+ else if (argDef.type === 'boolean' || argDef.type === 'bool') {
43
+ if (typeof val === 'string') {
44
+ const lower = val.toLowerCase();
45
+ if (lower === 'true' || lower === '1')
46
+ result[argDef.name] = true;
47
+ else if (lower === 'false' || lower === '0')
48
+ result[argDef.name] = false;
49
+ else
50
+ throw new Error(`Argument "${argDef.name}" must be a boolean (true/false). Received: "${val}"`);
51
+ }
52
+ else {
53
+ result[argDef.name] = Boolean(val);
54
+ }
55
+ }
56
+ // 3. Choices validation
57
+ const coercedVal = result[argDef.name];
58
+ if (argDef.choices && argDef.choices.length > 0) {
59
+ if (!argDef.choices.map(String).includes(String(coercedVal))) {
60
+ throw new Error(`Argument "${argDef.name}" must be one of: ${argDef.choices.join(', ')}. Received: "${coercedVal}"`);
61
+ }
62
+ }
63
+ }
64
+ else if (argDef.default !== undefined) {
65
+ result[argDef.name] = argDef.default;
66
+ }
67
+ }
68
+ return result;
69
+ }
70
+ /**
71
+ * Run a command's func or pipeline against a page.
72
+ */
73
+ async function runCommand(cmd, page, kwargs, debug) {
74
+ // Lazy-load TS module on first execution (manifest fast-path)
75
+ const internal = cmd;
76
+ if (internal._lazy && internal._modulePath) {
77
+ const modulePath = internal._modulePath;
78
+ if (!_loadedModules.has(modulePath)) {
79
+ try {
80
+ await import(pathToFileURL(modulePath).href);
81
+ _loadedModules.add(modulePath);
82
+ }
83
+ catch (err) {
84
+ throw new AdapterLoadError(`Failed to load adapter module ${modulePath}: ${getErrorMessage(err)}`, 'Check that the adapter file exists and has no syntax errors.');
85
+ }
86
+ }
87
+ // After loading, the module's cli() call will have updated the registry.
88
+ const updated = getRegistry().get(fullName(cmd));
89
+ if (updated?.func)
90
+ return updated.func(page, kwargs, debug);
91
+ if (updated?.pipeline)
92
+ return executePipeline(page, updated.pipeline, { args: kwargs, debug });
93
+ }
94
+ if (cmd.func)
95
+ return cmd.func(page, kwargs, debug);
96
+ if (cmd.pipeline)
97
+ return executePipeline(page, cmd.pipeline, { args: kwargs, debug });
98
+ throw new Error(`Command ${fullName(cmd)} has no func or pipeline`);
99
+ }
100
+ /**
101
+ * Resolve the pre-navigation URL for a command, or null to skip.
102
+ *
103
+ * COOKIE/HEADER strategies need the browser on the target domain so
104
+ * `fetch(url, { credentials: 'include' })` carries cookies.
105
+ * Adapters that handle their own navigation set `navigateBefore: false`.
106
+ */
107
+ function resolvePreNav(cmd) {
108
+ if (cmd.navigateBefore === false)
109
+ return null;
110
+ if (typeof cmd.navigateBefore === 'string')
111
+ return cmd.navigateBefore;
112
+ // Default: pre-navigate for COOKIE/HEADER strategies with a domain
113
+ if ((cmd.strategy === Strategy.COOKIE || cmd.strategy === Strategy.HEADER) && cmd.domain) {
114
+ return `https://${cmd.domain}`;
115
+ }
116
+ return null;
117
+ }
118
+ /**
119
+ * Execute a CLI command. Automatically manages browser sessions when needed.
120
+ *
121
+ * This is the unified entry point — callers don't need to care about
122
+ * whether the command requires a browser or not.
123
+ */
124
+ export async function executeCommand(cmd, rawKwargs, debug = false) {
125
+ let kwargs;
126
+ try {
127
+ kwargs = coerceAndValidateArgs(cmd.args, rawKwargs);
128
+ }
129
+ catch (err) {
130
+ throw new Error(`[Argument Validation Error]\n${getErrorMessage(err)}`);
131
+ }
132
+ if (shouldUseBrowserSession(cmd)) {
133
+ const BrowserFactory = getBrowserFactory();
134
+ return browserSession(BrowserFactory, async (page) => {
135
+ // Pre-navigate to target domain for cookie/header context if needed.
136
+ // Each adapter controls this via `navigateBefore` (see CliCommand docs).
137
+ const preNavUrl = resolvePreNav(cmd);
138
+ if (preNavUrl) {
139
+ try {
140
+ await page.goto(preNavUrl);
141
+ await page.wait(2);
142
+ }
143
+ catch { }
144
+ }
145
+ return runWithTimeout(runCommand(cmd, page, kwargs, debug), {
146
+ timeout: cmd.timeoutSeconds ?? DEFAULT_BROWSER_COMMAND_TIMEOUT,
147
+ label: fullName(cmd),
148
+ });
149
+ }, { workspace: `site:${cmd.site}` });
150
+ }
151
+ // Non-browser commands run directly
152
+ return runCommand(cmd, null, kwargs, debug);
153
+ }
package/dist/explore.d.ts CHANGED
@@ -5,8 +5,80 @@
5
5
  * captures network traffic, analyzes JSON responses, and automatically
6
6
  * infers CLI capabilities from discovered API endpoints.
7
7
  */
8
+ import type { IBrowserFactory } from './runtime.js';
8
9
  export declare function detectSiteName(url: string): string;
9
10
  export declare function slugify(value: string): string;
11
+ interface InferredCapability {
12
+ name: string;
13
+ description: string;
14
+ strategy: string;
15
+ confidence: number;
16
+ endpoint: string;
17
+ itemPath: string | null;
18
+ recommendedColumns: string[];
19
+ recommendedArgs: Array<{
20
+ name: string;
21
+ type: string;
22
+ required: boolean;
23
+ default?: unknown;
24
+ }>;
25
+ storeHint?: {
26
+ store: string;
27
+ action: string;
28
+ };
29
+ }
30
+ export interface ExploreManifest {
31
+ site: string;
32
+ target_url: string;
33
+ final_url: string;
34
+ title: string;
35
+ framework: Record<string, boolean>;
36
+ stores: Array<{
37
+ type: DiscoveredStore['type'];
38
+ id: string;
39
+ actions: string[];
40
+ }>;
41
+ top_strategy: string;
42
+ explored_at?: string;
43
+ }
44
+ export interface ExploreAuthSummary {
45
+ top_strategy: string;
46
+ indicators: string[];
47
+ framework: Record<string, boolean>;
48
+ }
49
+ export interface ExploreEndpointArtifact {
50
+ pattern: string;
51
+ method: string;
52
+ url: string;
53
+ status: number | null;
54
+ contentType: string;
55
+ score: number;
56
+ queryParams: string[];
57
+ itemPath: string | null;
58
+ itemCount: number;
59
+ detectedFields: Record<string, string>;
60
+ authIndicators: string[];
61
+ }
62
+ export interface ExploreResult {
63
+ site: string;
64
+ target_url: string;
65
+ final_url: string;
66
+ title: string;
67
+ framework: Record<string, boolean>;
68
+ stores: DiscoveredStore[];
69
+ top_strategy: string;
70
+ endpoint_count: number;
71
+ api_endpoint_count: number;
72
+ capabilities: InferredCapability[];
73
+ auth_indicators: string[];
74
+ out_dir: string;
75
+ }
76
+ export interface ExploreBundle {
77
+ manifest: ExploreManifest;
78
+ endpoints: ExploreEndpointArtifact[];
79
+ capabilities: InferredCapability[];
80
+ auth: ExploreAuthSummary;
81
+ }
10
82
  export interface DiscoveredStore {
11
83
  type: 'pinia' | 'vuex';
12
84
  id: string;
@@ -14,7 +86,7 @@ export interface DiscoveredStore {
14
86
  stateKeys: string[];
15
87
  }
16
88
  export declare function exploreUrl(url: string, opts: {
17
- BrowserFactory: new () => any;
89
+ BrowserFactory: new () => IBrowserFactory;
18
90
  site?: string;
19
91
  goal?: string;
20
92
  authenticated?: boolean;
@@ -24,5 +96,6 @@ export declare function exploreUrl(url: string, opts: {
24
96
  clickLabels?: string[];
25
97
  auto?: boolean;
26
98
  workspace?: string;
27
- }): Promise<Record<string, any>>;
28
- export declare function renderExploreSummary(result: Record<string, any>): string;
99
+ }): Promise<ExploreResult>;
100
+ export declare function renderExploreSummary(result: ExploreResult): string;
101
+ export {};
package/dist/explore.js CHANGED
@@ -147,6 +147,9 @@ function flattenFields(obj, prefix, maxDepth) {
147
147
  }
148
148
  return names;
149
149
  }
150
+ function isBooleanRecord(value) {
151
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
152
+ }
150
153
  function scoreEndpoint(ep) {
151
154
  let s = 0;
152
155
  if (ep.contentType.includes('json'))
@@ -210,6 +213,120 @@ const FRAMEWORK_DETECT_JS = detectFramework.toString();
210
213
  const STORE_DISCOVER_JS = discoverStores.toString();
211
214
  // ── Auto-Interaction (Fuzzing) ─────────────────────────────────────────────
212
215
  const INTERACT_FUZZ_JS = interactFuzz.toString();
216
+ // ── Analysis helpers (extracted from exploreUrl) ───────────────────────────
217
+ /** Filter, deduplicate, and score network endpoints. */
218
+ function analyzeEndpoints(networkEntries) {
219
+ const seen = new Map();
220
+ for (const entry of networkEntries) {
221
+ if (!entry.url)
222
+ continue;
223
+ const ct = entry.contentType.toLowerCase();
224
+ if (ct.includes('image/') || ct.includes('font/') || ct.includes('css') || ct.includes('javascript') || ct.includes('wasm'))
225
+ continue;
226
+ if (entry.status && entry.status >= 400)
227
+ continue;
228
+ const pattern = urlToPattern(entry.url);
229
+ const key = `${entry.method}:${pattern}`;
230
+ if (seen.has(key))
231
+ continue;
232
+ const qp = [];
233
+ try {
234
+ new URL(entry.url).searchParams.forEach((_v, k) => { if (!VOLATILE_PARAMS.has(k))
235
+ qp.push(k); });
236
+ }
237
+ catch { }
238
+ const ep = {
239
+ pattern, method: entry.method, url: entry.url, status: entry.status, contentType: ct,
240
+ queryParams: qp, hasSearchParam: qp.some(p => SEARCH_PARAMS.has(p)),
241
+ hasPaginationParam: qp.some(p => PAGINATION_PARAMS.has(p)),
242
+ hasLimitParam: qp.some(p => LIMIT_PARAMS.has(p)),
243
+ authIndicators: detectAuthIndicators(entry.requestHeaders),
244
+ responseAnalysis: entry.responseBody ? analyzeResponseBody(entry.responseBody) : null,
245
+ score: 0,
246
+ };
247
+ ep.score = scoreEndpoint(ep);
248
+ seen.set(key, ep);
249
+ }
250
+ const analyzed = [...seen.values()].filter(ep => ep.score >= 5).sort((a, b) => b.score - a.score);
251
+ return { analyzed, totalCount: seen.size };
252
+ }
253
+ /** Infer CLI capabilities from analyzed endpoints. */
254
+ function inferCapabilitiesFromEndpoints(endpoints, stores, opts) {
255
+ const capabilities = [];
256
+ const usedNames = new Set();
257
+ for (const ep of endpoints.slice(0, 8)) {
258
+ let capName = inferCapabilityName(ep.url, opts.goal);
259
+ if (usedNames.has(capName)) {
260
+ const suffix = ep.pattern.split('/').filter(s => s && !s.startsWith('{') && !s.includes('.')).pop();
261
+ capName = suffix ? `${capName}_${suffix}` : `${capName}_${usedNames.size}`;
262
+ }
263
+ usedNames.add(capName);
264
+ const cols = [];
265
+ if (ep.responseAnalysis) {
266
+ for (const role of ['title', 'url', 'author', 'score', 'time']) {
267
+ if (ep.responseAnalysis.detectedFields[role])
268
+ cols.push(role);
269
+ }
270
+ }
271
+ const args = [];
272
+ if (ep.hasSearchParam)
273
+ args.push({ name: 'keyword', type: 'str', required: true });
274
+ args.push({ name: 'limit', type: 'int', required: false, default: 20 });
275
+ if (ep.hasPaginationParam)
276
+ args.push({ name: 'page', type: 'int', required: false, default: 1 });
277
+ const epStrategy = inferStrategy(ep.authIndicators);
278
+ let storeHint;
279
+ if ((epStrategy === 'intercept' || ep.authIndicators.includes('signature')) && stores.length > 0) {
280
+ for (const s of stores) {
281
+ const matchingAction = s.actions.find(a => capName.split('_').some(part => a.toLowerCase().includes(part)) ||
282
+ a.toLowerCase().includes('fetch') || a.toLowerCase().includes('get'));
283
+ if (matchingAction) {
284
+ storeHint = { store: s.id, action: matchingAction };
285
+ break;
286
+ }
287
+ }
288
+ }
289
+ capabilities.push({
290
+ name: capName, description: `${opts.site ?? detectSiteName(opts.url)} ${capName}`,
291
+ strategy: storeHint ? 'store-action' : epStrategy,
292
+ confidence: Math.min(ep.score / 20, 1.0), endpoint: ep.pattern,
293
+ itemPath: ep.responseAnalysis?.itemPath ?? null,
294
+ recommendedColumns: cols.length ? cols : ['title', 'url'],
295
+ recommendedArgs: args,
296
+ ...(storeHint ? { storeHint } : {}),
297
+ });
298
+ }
299
+ const allAuth = new Set(endpoints.flatMap(ep => ep.authIndicators));
300
+ const topStrategy = allAuth.has('signature') ? 'intercept'
301
+ : allAuth.has('bearer') || allAuth.has('csrf') ? 'header'
302
+ : allAuth.size === 0 ? 'public' : 'cookie';
303
+ return { capabilities, topStrategy, authIndicators: [...allAuth] };
304
+ }
305
+ /** Write explore artifacts (manifest, endpoints, capabilities, auth, stores) to disk. */
306
+ async function writeExploreArtifacts(targetDir, result, analyzedEndpoints, stores) {
307
+ await fs.promises.mkdir(targetDir, { recursive: true });
308
+ const tasks = [
309
+ fs.promises.writeFile(path.join(targetDir, 'manifest.json'), JSON.stringify({
310
+ site: result.site, target_url: result.target_url, final_url: result.final_url, title: result.title,
311
+ framework: result.framework, stores: stores.map(s => ({ type: s.type, id: s.id, actions: s.actions })),
312
+ top_strategy: result.top_strategy, explored_at: new Date().toISOString(),
313
+ }, null, 2)),
314
+ fs.promises.writeFile(path.join(targetDir, 'endpoints.json'), JSON.stringify(analyzedEndpoints.map(ep => ({
315
+ pattern: ep.pattern, method: ep.method, url: ep.url, status: ep.status,
316
+ contentType: ep.contentType, score: ep.score, queryParams: ep.queryParams,
317
+ itemPath: ep.responseAnalysis?.itemPath ?? null, itemCount: ep.responseAnalysis?.itemCount ?? 0,
318
+ detectedFields: ep.responseAnalysis?.detectedFields ?? {}, authIndicators: ep.authIndicators,
319
+ })), null, 2)),
320
+ fs.promises.writeFile(path.join(targetDir, 'capabilities.json'), JSON.stringify(result.capabilities, null, 2)),
321
+ fs.promises.writeFile(path.join(targetDir, 'auth.json'), JSON.stringify({
322
+ top_strategy: result.top_strategy, indicators: result.auth_indicators, framework: result.framework,
323
+ }, null, 2)),
324
+ ];
325
+ if (stores.length > 0) {
326
+ tasks.push(fs.promises.writeFile(path.join(targetDir, 'stores.json'), JSON.stringify(stores, null, 2)));
327
+ }
328
+ await Promise.all(tasks);
329
+ }
213
330
  // ── Main explore function ──────────────────────────────────────────────────
214
331
  export async function exploreUrl(url, opts) {
215
332
  const waitSeconds = opts.waitSeconds ?? 3.0;
@@ -287,7 +404,7 @@ export async function exploreUrl(url, opts) {
287
404
  let framework = {};
288
405
  try {
289
406
  const fw = await page.evaluate(FRAMEWORK_DETECT_JS);
290
- if (fw && typeof fw === 'object')
407
+ if (isBooleanRecord(fw))
291
408
  framework = fw;
292
409
  }
293
410
  catch { }
@@ -301,120 +418,20 @@ export async function exploreUrl(url, opts) {
301
418
  }
302
419
  catch { }
303
420
  }
304
- // Step 7: Analyze endpoints
305
- const seen = new Map();
306
- for (const entry of networkEntries) {
307
- if (!entry.url)
308
- continue;
309
- const ct = entry.contentType.toLowerCase();
310
- if (ct.includes('image/') || ct.includes('font/') || ct.includes('css') || ct.includes('javascript') || ct.includes('wasm'))
311
- continue;
312
- if (entry.status && entry.status >= 400)
313
- continue;
314
- const pattern = urlToPattern(entry.url);
315
- const key = `${entry.method}:${pattern}`;
316
- if (seen.has(key))
317
- continue;
318
- const qp = [];
319
- try {
320
- new URL(entry.url).searchParams.forEach((_v, k) => { if (!VOLATILE_PARAMS.has(k))
321
- qp.push(k); });
322
- }
323
- catch { }
324
- const ep = {
325
- pattern, method: entry.method, url: entry.url, status: entry.status, contentType: ct,
326
- queryParams: qp, hasSearchParam: qp.some(p => SEARCH_PARAMS.has(p)),
327
- hasPaginationParam: qp.some(p => PAGINATION_PARAMS.has(p)),
328
- hasLimitParam: qp.some(p => LIMIT_PARAMS.has(p)),
329
- authIndicators: detectAuthIndicators(entry.requestHeaders),
330
- responseAnalysis: entry.responseBody ? analyzeResponseBody(entry.responseBody) : null,
331
- score: 0,
332
- };
333
- ep.score = scoreEndpoint(ep);
334
- seen.set(key, ep);
335
- }
336
- const analyzedEndpoints = [...seen.values()].filter(ep => ep.score >= 5).sort((a, b) => b.score - a.score);
337
- // Step 8: Infer capabilities
338
- const capabilities = [];
339
- const usedNames = new Set();
340
- for (const ep of analyzedEndpoints.slice(0, 8)) {
341
- let capName = inferCapabilityName(ep.url, opts.goal);
342
- if (usedNames.has(capName)) {
343
- const suffix = ep.pattern.split('/').filter(s => s && !s.startsWith('{') && !s.includes('.')).pop();
344
- capName = suffix ? `${capName}_${suffix}` : `${capName}_${usedNames.size}`;
345
- }
346
- usedNames.add(capName);
347
- const cols = [];
348
- if (ep.responseAnalysis) {
349
- for (const role of ['title', 'url', 'author', 'score', 'time']) {
350
- if (ep.responseAnalysis.detectedFields[role])
351
- cols.push(role);
352
- }
353
- }
354
- const args = [];
355
- if (ep.hasSearchParam)
356
- args.push({ name: 'keyword', type: 'str', required: true });
357
- args.push({ name: 'limit', type: 'int', required: false, default: 20 });
358
- if (ep.hasPaginationParam)
359
- args.push({ name: 'page', type: 'int', required: false, default: 1 });
360
- // Link store actions to capabilities when store-action strategy is recommended
361
- const epStrategy = inferStrategy(ep.authIndicators);
362
- let storeHint;
363
- if ((epStrategy === 'intercept' || ep.authIndicators.includes('signature')) && stores.length > 0) {
364
- // Try to find a store/action that matches this endpoint's purpose
365
- for (const s of stores) {
366
- const matchingAction = s.actions.find(a => capName.split('_').some(part => a.toLowerCase().includes(part)) ||
367
- a.toLowerCase().includes('fetch') || a.toLowerCase().includes('get'));
368
- if (matchingAction) {
369
- storeHint = { store: s.id, action: matchingAction };
370
- break;
371
- }
372
- }
373
- }
374
- capabilities.push({
375
- name: capName, description: `${opts.site ?? detectSiteName(url)} ${capName}`,
376
- strategy: storeHint ? 'store-action' : epStrategy,
377
- confidence: Math.min(ep.score / 20, 1.0), endpoint: ep.pattern,
378
- itemPath: ep.responseAnalysis?.itemPath ?? null,
379
- recommendedColumns: cols.length ? cols : ['title', 'url'],
380
- recommendedArgs: args,
381
- ...(storeHint ? { storeHint } : {}),
382
- });
383
- }
384
- // Step 9: Determine overall auth strategy
385
- const allAuth = new Set(analyzedEndpoints.flatMap(ep => ep.authIndicators));
386
- const topStrategy = allAuth.has('signature') ? 'intercept' : allAuth.has('bearer') || allAuth.has('csrf') ? 'header' : allAuth.size === 0 ? 'public' : 'cookie';
421
+ // Step 7+8: Analyze endpoints and infer capabilities
422
+ const { analyzed: analyzedEndpoints, totalCount } = analyzeEndpoints(networkEntries);
423
+ const { capabilities, topStrategy, authIndicators } = inferCapabilitiesFromEndpoints(analyzedEndpoints, stores, { site: opts.site, goal: opts.goal, url });
424
+ // Step 9: Assemble result and write artifacts
387
425
  const siteName = opts.site ?? detectSiteName(metadata.url || url);
388
426
  const targetDir = opts.outDir ?? path.join('.opencli', 'explore', siteName);
389
- await fs.promises.mkdir(targetDir, { recursive: true });
390
427
  const result = {
391
428
  site: siteName, target_url: url, final_url: metadata.url, title: metadata.title,
392
429
  framework, stores, top_strategy: topStrategy,
393
- endpoint_count: analyzedEndpoints.length + [...seen.values()].filter(ep => ep.score < 5).length,
430
+ endpoint_count: totalCount,
394
431
  api_endpoint_count: analyzedEndpoints.length,
395
- capabilities, auth_indicators: [...allAuth],
432
+ capabilities, auth_indicators: authIndicators,
396
433
  };
397
- // Write artifacts
398
- const writeTasks = [];
399
- writeTasks.push(fs.promises.writeFile(path.join(targetDir, 'manifest.json'), JSON.stringify({
400
- site: siteName, target_url: url, final_url: metadata.url, title: metadata.title,
401
- framework, stores: stores.map(s => ({ type: s.type, id: s.id, actions: s.actions })),
402
- top_strategy: topStrategy, explored_at: new Date().toISOString(),
403
- }, null, 2)));
404
- writeTasks.push(fs.promises.writeFile(path.join(targetDir, 'endpoints.json'), JSON.stringify(analyzedEndpoints.map(ep => ({
405
- pattern: ep.pattern, method: ep.method, url: ep.url, status: ep.status,
406
- contentType: ep.contentType, score: ep.score, queryParams: ep.queryParams,
407
- itemPath: ep.responseAnalysis?.itemPath ?? null, itemCount: ep.responseAnalysis?.itemCount ?? 0,
408
- detectedFields: ep.responseAnalysis?.detectedFields ?? {}, authIndicators: ep.authIndicators,
409
- })), null, 2)));
410
- writeTasks.push(fs.promises.writeFile(path.join(targetDir, 'capabilities.json'), JSON.stringify(capabilities, null, 2)));
411
- writeTasks.push(fs.promises.writeFile(path.join(targetDir, 'auth.json'), JSON.stringify({
412
- top_strategy: topStrategy, indicators: [...allAuth], framework,
413
- }, null, 2)));
414
- if (stores.length > 0) {
415
- writeTasks.push(fs.promises.writeFile(path.join(targetDir, 'stores.json'), JSON.stringify(stores, null, 2)));
416
- }
417
- await Promise.all(writeTasks);
434
+ await writeExploreArtifacts(targetDir, result, analyzedEndpoints, stores);
418
435
  return { ...result, out_dir: targetDir };
419
436
  })(), { timeout: exploreTimeout, label: `Explore ${url}` });
420
437
  }, { workspace: opts.workspace });
@@ -444,11 +461,15 @@ export function renderExploreSummary(result) {
444
461
  lines.push(`Output: ${result.out_dir}`);
445
462
  return lines.join('\n');
446
463
  }
447
- async function readPageMetadata(page /* IPage */) {
464
+ async function readPageMetadata(page) {
448
465
  try {
449
466
  const result = await page.evaluate(`() => ({ url: window.location.href, title: document.title || '' })`);
450
- if (result && typeof result === 'object')
451
- return { url: String(result.url ?? ''), title: String(result.title ?? '') };
467
+ if (result && typeof result === 'object' && !Array.isArray(result)) {
468
+ return {
469
+ url: String(result.url ?? ''),
470
+ title: String(result.title ?? ''),
471
+ };
472
+ }
452
473
  }
453
474
  catch { }
454
475
  return { url: '', title: '' };
@@ -0,0 +1,48 @@
1
+ - name: gh
2
+ binary: gh
3
+ description: "GitHub CLI — repos, PRs, issues, releases, gists"
4
+ homepage: "https://cli.github.com"
5
+ tags: [github, git, dev]
6
+ install:
7
+ mac: "brew install gh"
8
+
9
+ - name: obsidian
10
+ binary: obsidian
11
+ description: "Obsidian vault management — notes, search, tags, tasks, sync"
12
+ homepage: "https://obsidian.md/help/cli"
13
+ tags: [notes, knowledge, markdown]
14
+ install:
15
+ mac: "brew install --cask obsidian"
16
+
17
+ - name: readwise
18
+ binary: readwise
19
+ description: "Readwise & Reader CLI — highlights, annotations, reading list"
20
+ homepage: "https://github.com/readwiseio/readwise-cli"
21
+ tags: [reading, highlights]
22
+ install:
23
+ default: "npm install -g @readwiseio/readwise-cli"
24
+
25
+ - name: kubectl
26
+ binary: kubectl
27
+ description: "Kubernetes command-line tool"
28
+ homepage: "https://kubernetes.io/docs/reference/kubectl/"
29
+ tags: [kubernetes, k8s, devops]
30
+ install:
31
+ mac: "brew install kubectl"
32
+
33
+ - name: docker
34
+ binary: docker
35
+ description: "Docker command-line interface"
36
+ homepage: "https://docs.docker.com/engine/reference/commandline/cli/"
37
+ tags: [docker, containers, devops]
38
+ install:
39
+ mac: "brew install --cask docker"
40
+
41
+ - name: gws
42
+ binary: gws
43
+ description: "Google Workspace CLI — Docs, Sheets, Drive, Gmail, Calendar"
44
+ homepage: "https://github.com/nicholasgasior/gws"
45
+ tags: [google, docs, sheets, drive, workspace]
46
+ install:
47
+ mac: "brew install gws"
48
+ default: "npm install -g @nicholasgasior/gws"
@@ -16,5 +16,10 @@ export declare function loadExternalClis(): ExternalCliConfig[];
16
16
  export declare function isBinaryInstalled(binary: string): boolean;
17
17
  export declare function getInstallCmd(installConfig?: ExternalCliInstall): string | null;
18
18
  export declare function installExternalCli(cli: ExternalCliConfig): boolean;
19
- export declare function executeExternalCli(name: string, args: string[]): Promise<void>;
20
- export declare function registerExternalCli(name: string, binary?: string, install?: string, description?: string): void;
19
+ export declare function executeExternalCli(name: string, args: string[], preloaded?: ExternalCliConfig[]): void;
20
+ export interface RegisterOptions {
21
+ binary?: string;
22
+ install?: string;
23
+ description?: string;
24
+ }
25
+ export declare function registerExternalCli(name: string, opts?: RegisterOptions): void;
package/dist/external.js CHANGED
@@ -2,7 +2,7 @@ import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import * as os from 'node:os';
4
4
  import { fileURLToPath } from 'node:url';
5
- import { spawnSync, execSync } from 'node:child_process';
5
+ import { spawnSync, execSync, execFileSync } from 'node:child_process';
6
6
  import yaml from 'js-yaml';
7
7
  import chalk from 'chalk';
8
8
  import { log } from './logger.js';
@@ -45,8 +45,7 @@ export function loadExternalClis() {
45
45
  export function isBinaryInstalled(binary) {
46
46
  try {
47
47
  const isWindows = os.platform() === 'win32';
48
- const cmd = isWindows ? 'where' : 'command -v';
49
- execSync(`${cmd} ${binary}`, { stdio: 'ignore' });
48
+ execFileSync(isWindows ? 'where' : 'which', [binary], { stdio: 'ignore' });
50
49
  return true;
51
50
  }
52
51
  catch {
@@ -92,8 +91,8 @@ export function installExternalCli(cli) {
92
91
  return false;
93
92
  }
94
93
  }
95
- export async function executeExternalCli(name, args) {
96
- const configs = loadExternalClis();
94
+ export function executeExternalCli(name, args, preloaded) {
95
+ const configs = preloaded ?? loadExternalClis();
97
96
  const cli = configs.find((c) => c.name === name);
98
97
  if (!cli) {
99
98
  throw new Error(`External CLI '${name}' not found in registry.`);
@@ -107,8 +106,7 @@ export async function executeExternalCli(name, args) {
107
106
  return;
108
107
  }
109
108
  }
110
- // 3. Passthrough execution
111
- // We use spawnSync to properly inherit stdio and block until completion
109
+ // 3. Passthrough execution with stdio inherited
112
110
  const result = spawnSync(cli.binary, args, { stdio: 'inherit' });
113
111
  if (result.error) {
114
112
  console.error(chalk.red(`Failed to execute '${cli.binary}': ${result.error.message}`));
@@ -119,7 +117,7 @@ export async function executeExternalCli(name, args) {
119
117
  process.exitCode = result.status;
120
118
  }
121
119
  }
122
- export function registerExternalCli(name, binary, install, description) {
120
+ export function registerExternalCli(name, opts) {
123
121
  const userPath = getUserRegistryPath();
124
122
  const configDir = path.dirname(userPath);
125
123
  if (!fs.existsSync(configDir)) {
@@ -138,14 +136,13 @@ export function registerExternalCli(name, binary, install, description) {
138
136
  const existingIndex = items.findIndex((c) => c.name === name);
139
137
  const newItem = {
140
138
  name,
141
- binary: binary || name,
139
+ binary: opts?.binary || name,
142
140
  };
143
- if (description)
144
- newItem.description = description;
145
- if (install)
146
- newItem.install = { default: install };
141
+ if (opts?.description)
142
+ newItem.description = opts.description;
143
+ if (opts?.install)
144
+ newItem.install = { default: opts.install };
147
145
  if (existingIndex >= 0) {
148
- // Merge
149
146
  items[existingIndex] = { ...items[existingIndex], ...newItem };
150
147
  console.log(chalk.green(`Updated '${name}' in user registry.`));
151
148
  }