@jackwener/opencli 1.1.0 → 1.1.1

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 (354) 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/README.md +25 -10
  8. package/README.zh-CN.md +26 -11
  9. package/SKILL.md +95 -31
  10. package/dist/browser/cdp.js +6 -1
  11. package/dist/browser/page.d.ts +4 -1
  12. package/dist/browser/page.js +7 -1
  13. package/dist/build-manifest.js +23 -16
  14. package/dist/cli-manifest.json +431 -276
  15. package/dist/cli.d.ts +6 -0
  16. package/dist/cli.js +189 -162
  17. package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
  18. package/dist/clis/apple-podcasts/commands.test.js +76 -0
  19. package/dist/clis/apple-podcasts/search.js +2 -2
  20. package/dist/clis/apple-podcasts/top.js +9 -2
  21. package/dist/clis/arxiv/search.js +1 -1
  22. package/dist/clis/bilibili/dynamic.js +1 -1
  23. package/dist/clis/bilibili/favorite.js +1 -1
  24. package/dist/clis/bilibili/feed.js +1 -1
  25. package/dist/clis/bilibili/following.js +1 -1
  26. package/dist/clis/bilibili/history.js +1 -1
  27. package/dist/clis/bilibili/me.js +1 -1
  28. package/dist/clis/bilibili/ranking.js +1 -1
  29. package/dist/clis/bilibili/search.js +3 -3
  30. package/dist/clis/bilibili/subtitle.js +1 -1
  31. package/dist/clis/bilibili/user-videos.js +1 -1
  32. package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
  33. package/dist/clis/bloomberg/businessweek.js +17 -0
  34. package/dist/clis/bloomberg/economics.js +17 -0
  35. package/dist/clis/bloomberg/feeds.d.ts +1 -0
  36. package/dist/clis/bloomberg/feeds.js +15 -0
  37. package/dist/clis/bloomberg/industries.d.ts +1 -0
  38. package/dist/clis/bloomberg/industries.js +17 -0
  39. package/dist/clis/bloomberg/main.d.ts +1 -0
  40. package/dist/clis/bloomberg/main.js +17 -0
  41. package/dist/clis/bloomberg/markets.d.ts +1 -0
  42. package/dist/clis/bloomberg/markets.js +17 -0
  43. package/dist/clis/bloomberg/news.d.ts +1 -0
  44. package/dist/clis/bloomberg/news.js +105 -0
  45. package/dist/clis/bloomberg/opinions.d.ts +1 -0
  46. package/dist/clis/bloomberg/opinions.js +17 -0
  47. package/dist/clis/bloomberg/politics.d.ts +1 -0
  48. package/dist/clis/bloomberg/politics.js +17 -0
  49. package/dist/clis/bloomberg/tech.d.ts +1 -0
  50. package/dist/clis/bloomberg/tech.js +17 -0
  51. package/dist/clis/bloomberg/utils.d.ts +34 -0
  52. package/dist/clis/bloomberg/utils.js +364 -0
  53. package/dist/clis/bloomberg/utils.test.d.ts +1 -0
  54. package/dist/clis/bloomberg/utils.test.js +129 -0
  55. package/dist/clis/boss/batchgreet.js +2 -2
  56. package/dist/clis/boss/chatlist.js +2 -2
  57. package/dist/clis/boss/detail.js +2 -2
  58. package/dist/clis/boss/greet.js +4 -4
  59. package/dist/clis/boss/search.js +1 -1
  60. package/dist/clis/boss/send.js +1 -1
  61. package/dist/clis/boss/stats.js +2 -2
  62. package/dist/clis/chaoxing/assignments.js +1 -1
  63. package/dist/clis/chaoxing/exams.js +1 -1
  64. package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
  65. package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
  66. package/dist/clis/chaoxing/utils.test.d.ts +1 -0
  67. package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
  68. package/dist/clis/chatgpt/read.js +1 -1
  69. package/dist/clis/chatwise/export.js +1 -1
  70. package/dist/clis/chatwise/model.js +2 -2
  71. package/dist/clis/chatwise/screenshot.js +1 -1
  72. package/dist/clis/codex/export.js +1 -1
  73. package/dist/clis/codex/model.js +2 -2
  74. package/dist/clis/codex/screenshot.js +1 -1
  75. package/dist/clis/coupang/add-to-cart.js +3 -4
  76. package/dist/clis/coupang/search.js +2 -4
  77. package/dist/clis/coupang/utils.test.d.ts +1 -0
  78. package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
  79. package/dist/clis/ctrip/search.js +1 -1
  80. package/dist/clis/cursor/export.js +1 -1
  81. package/dist/clis/cursor/model.js +2 -2
  82. package/dist/clis/cursor/screenshot.js +1 -1
  83. package/dist/clis/jike/comment.js +2 -3
  84. package/dist/clis/jike/create.js +1 -2
  85. package/dist/clis/jike/feed.js +0 -1
  86. package/dist/clis/jike/like.js +1 -2
  87. package/dist/clis/jike/notifications.js +0 -1
  88. package/dist/clis/jike/post.yaml +1 -0
  89. package/dist/clis/jike/repost.js +1 -2
  90. package/dist/clis/jike/search.js +2 -3
  91. package/dist/clis/jike/topic.yaml +1 -0
  92. package/dist/clis/jike/user.yaml +1 -0
  93. package/dist/clis/jimeng/history.yaml +0 -1
  94. package/dist/clis/linkedin/search.js +7 -7
  95. package/dist/clis/linux-do/category.yaml +1 -0
  96. package/dist/clis/linux-do/search.yaml +4 -3
  97. package/dist/clis/linux-do/topic.yaml +1 -0
  98. package/dist/clis/notion/export.js +1 -1
  99. package/dist/clis/reddit/comment.js +3 -4
  100. package/dist/clis/reddit/read.js +4 -5
  101. package/dist/clis/reddit/save.js +2 -3
  102. package/dist/clis/reddit/saved.js +0 -1
  103. package/dist/clis/reddit/search.yaml +1 -0
  104. package/dist/clis/reddit/subscribe.js +0 -1
  105. package/dist/clis/reddit/upvote.js +2 -3
  106. package/dist/clis/reddit/upvoted.js +0 -1
  107. package/dist/clis/reddit/user-comments.yaml +1 -0
  108. package/dist/clis/reddit/user-posts.yaml +1 -0
  109. package/dist/clis/reddit/user.yaml +1 -0
  110. package/dist/clis/reuters/search.js +1 -1
  111. package/dist/clis/smzdm/search.js +2 -3
  112. package/dist/clis/stackoverflow/search.yaml +1 -0
  113. package/dist/clis/steam/top-sellers.yaml +29 -0
  114. package/dist/clis/twitter/accept.js +2 -2
  115. package/dist/clis/twitter/article.js +2 -2
  116. package/dist/clis/twitter/block.d.ts +1 -0
  117. package/dist/clis/twitter/block.js +88 -0
  118. package/dist/clis/twitter/delete.js +1 -1
  119. package/dist/clis/twitter/hide-reply.d.ts +1 -0
  120. package/dist/clis/twitter/hide-reply.js +66 -0
  121. package/dist/clis/twitter/like.js +1 -1
  122. package/dist/clis/twitter/post.js +1 -1
  123. package/dist/clis/twitter/reply-dm.js +1 -1
  124. package/dist/clis/twitter/reply.js +2 -2
  125. package/dist/clis/twitter/search.js +1 -1
  126. package/dist/clis/twitter/thread.js +2 -2
  127. package/dist/clis/twitter/trending.d.ts +1 -0
  128. package/dist/clis/twitter/trending.js +91 -0
  129. package/dist/clis/twitter/unblock.d.ts +1 -0
  130. package/dist/clis/twitter/unblock.js +71 -0
  131. package/dist/clis/v2ex/topic.yaml +1 -0
  132. package/dist/clis/weibo/hot.js +0 -1
  133. package/dist/clis/weread/book.js +1 -1
  134. package/dist/clis/weread/highlights.js +1 -1
  135. package/dist/clis/weread/notes.js +1 -1
  136. package/dist/clis/weread/search.js +1 -1
  137. package/dist/clis/wikipedia/search.js +1 -1
  138. package/dist/clis/xiaohongshu/creator-note-detail.d.ts +15 -0
  139. package/dist/clis/xiaohongshu/creator-note-detail.js +69 -5
  140. package/dist/clis/xiaohongshu/creator-note-detail.test.js +80 -33
  141. package/dist/clis/xiaohongshu/creator-notes.js +35 -5
  142. package/dist/clis/xiaohongshu/creator-notes.test.js +35 -6
  143. package/dist/clis/xiaohongshu/creator-profile.js +0 -1
  144. package/dist/clis/xiaohongshu/creator-stats.js +0 -1
  145. package/dist/clis/xiaohongshu/download.js +2 -3
  146. package/dist/clis/xiaohongshu/feed.yaml +0 -1
  147. package/dist/clis/xiaohongshu/notifications.yaml +0 -1
  148. package/dist/clis/xiaohongshu/search.js +2 -2
  149. package/dist/clis/xiaohongshu/user.js +1 -2
  150. package/dist/clis/yahoo-finance/quote.js +0 -1
  151. package/dist/clis/youtube/search.js +1 -1
  152. package/dist/clis/youtube/transcript.js +1 -1
  153. package/dist/clis/youtube/video.js +1 -1
  154. package/dist/clis/zhihu/download.js +1 -2
  155. package/dist/clis/zhihu/question.js +1 -1
  156. package/dist/clis/zhihu/search.yaml +4 -3
  157. package/dist/commanderAdapter.d.ts +21 -0
  158. package/dist/commanderAdapter.js +111 -0
  159. package/dist/{engine.d.ts → discovery.d.ts} +0 -6
  160. package/dist/{engine.js → discovery.js} +1 -98
  161. package/dist/download/index.d.ts +2 -6
  162. package/dist/download/index.js +19 -46
  163. package/dist/engine.test.d.ts +1 -1
  164. package/dist/engine.test.js +8 -7
  165. package/dist/execution.d.ts +22 -0
  166. package/dist/execution.js +129 -0
  167. package/dist/explore.js +121 -107
  168. package/dist/external-clis.yaml +48 -0
  169. package/dist/external.d.ts +7 -2
  170. package/dist/external.js +11 -14
  171. package/dist/main.js +1 -1
  172. package/dist/pipeline/steps/browser.js +8 -2
  173. package/dist/registry.d.ts +2 -0
  174. package/dist/registry.js +2 -0
  175. package/dist/runtime.d.ts +5 -0
  176. package/dist/runtime.js +8 -0
  177. package/dist/serialization.d.ts +34 -0
  178. package/dist/serialization.js +63 -0
  179. package/dist/types.d.ts +4 -1
  180. package/docs/.vitepress/config.mts +14 -3
  181. package/docs/adapters/browser/arxiv.md +27 -0
  182. package/docs/adapters/browser/barchart.md +32 -0
  183. package/docs/adapters/browser/bloomberg.md +70 -0
  184. package/docs/adapters/browser/chaoxing.md +39 -0
  185. package/docs/adapters/browser/grok.md +35 -0
  186. package/docs/adapters/browser/hf.md +42 -0
  187. package/docs/adapters/browser/jike.md +45 -0
  188. package/docs/adapters/browser/jimeng.md +39 -0
  189. package/docs/adapters/browser/linux-do.md +45 -0
  190. package/docs/adapters/browser/sinafinance.md +35 -0
  191. package/docs/adapters/browser/stackoverflow.md +35 -0
  192. package/docs/adapters/browser/steam.md +26 -0
  193. package/docs/adapters/browser/twitter.md +3 -0
  194. package/docs/adapters/browser/weread.md +48 -0
  195. package/docs/adapters/browser/wikipedia.md +30 -0
  196. package/docs/adapters/browser/xiaohongshu.md +5 -1
  197. package/docs/adapters/desktop/chatgpt.md +3 -3
  198. package/docs/adapters/index.md +13 -0
  199. package/docs/advanced/download.md +4 -4
  200. package/docs/developer/architecture.md +17 -4
  201. package/package.json +1 -1
  202. package/scripts/check-doc-coverage.sh +69 -0
  203. package/scripts/copy-yaml.cjs +7 -0
  204. package/src/browser/cdp.ts +6 -1
  205. package/src/browser/page.ts +7 -1
  206. package/src/build-manifest.ts +25 -19
  207. package/src/cli.ts +218 -139
  208. package/src/clis/apple-podcasts/commands.test.ts +95 -0
  209. package/src/clis/apple-podcasts/search.ts +2 -2
  210. package/src/clis/apple-podcasts/top.ts +12 -2
  211. package/src/clis/arxiv/search.ts +1 -1
  212. package/src/clis/bilibili/dynamic.ts +1 -1
  213. package/src/clis/bilibili/favorite.ts +1 -1
  214. package/src/clis/bilibili/feed.ts +1 -1
  215. package/src/clis/bilibili/following.ts +1 -1
  216. package/src/clis/bilibili/history.ts +1 -1
  217. package/src/clis/bilibili/me.ts +1 -1
  218. package/src/clis/bilibili/ranking.ts +1 -1
  219. package/src/clis/bilibili/search.ts +3 -3
  220. package/src/clis/bilibili/subtitle.ts +1 -1
  221. package/src/clis/bilibili/user-videos.ts +1 -1
  222. package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
  223. package/src/clis/bloomberg/businessweek.ts +18 -0
  224. package/src/clis/bloomberg/economics.ts +18 -0
  225. package/src/clis/bloomberg/feeds.ts +16 -0
  226. package/src/clis/bloomberg/industries.ts +18 -0
  227. package/src/clis/bloomberg/main.ts +18 -0
  228. package/src/clis/bloomberg/markets.ts +18 -0
  229. package/src/clis/bloomberg/news.ts +136 -0
  230. package/src/clis/bloomberg/opinions.ts +18 -0
  231. package/src/clis/bloomberg/politics.ts +18 -0
  232. package/src/clis/bloomberg/tech.ts +18 -0
  233. package/src/clis/bloomberg/utils.test.ts +135 -0
  234. package/src/clis/bloomberg/utils.ts +429 -0
  235. package/src/clis/boss/batchgreet.ts +2 -2
  236. package/src/clis/boss/chatlist.ts +2 -2
  237. package/src/clis/boss/detail.ts +2 -2
  238. package/src/clis/boss/greet.ts +4 -4
  239. package/src/clis/boss/search.ts +1 -1
  240. package/src/clis/boss/send.ts +1 -1
  241. package/src/clis/boss/stats.ts +2 -2
  242. package/src/clis/chaoxing/assignments.ts +1 -1
  243. package/src/clis/chaoxing/exams.ts +1 -1
  244. package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
  245. package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
  246. package/src/clis/chatgpt/README.zh-CN.md +3 -3
  247. package/src/clis/chatgpt/read.ts +1 -1
  248. package/src/clis/chatwise/export.ts +1 -1
  249. package/src/clis/chatwise/model.ts +2 -2
  250. package/src/clis/chatwise/screenshot.ts +1 -1
  251. package/src/clis/codex/export.ts +1 -1
  252. package/src/clis/codex/model.ts +2 -2
  253. package/src/clis/codex/screenshot.ts +1 -1
  254. package/src/clis/coupang/add-to-cart.ts +3 -4
  255. package/src/clis/coupang/search.ts +2 -4
  256. package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
  257. package/src/clis/ctrip/search.ts +1 -1
  258. package/src/clis/cursor/export.ts +1 -1
  259. package/src/clis/cursor/model.ts +2 -2
  260. package/src/clis/cursor/screenshot.ts +1 -1
  261. package/src/clis/jike/comment.ts +2 -3
  262. package/src/clis/jike/create.ts +1 -2
  263. package/src/clis/jike/feed.ts +0 -1
  264. package/src/clis/jike/like.ts +1 -2
  265. package/src/clis/jike/notifications.ts +0 -1
  266. package/src/clis/jike/post.yaml +1 -0
  267. package/src/clis/jike/repost.ts +1 -2
  268. package/src/clis/jike/search.ts +2 -3
  269. package/src/clis/jike/topic.yaml +1 -0
  270. package/src/clis/jike/user.yaml +1 -0
  271. package/src/clis/jimeng/history.yaml +0 -1
  272. package/src/clis/linkedin/search.ts +7 -7
  273. package/src/clis/linux-do/category.yaml +1 -0
  274. package/src/clis/linux-do/search.yaml +4 -3
  275. package/src/clis/linux-do/topic.yaml +1 -0
  276. package/src/clis/notion/export.ts +1 -1
  277. package/src/clis/reddit/comment.ts +3 -4
  278. package/src/clis/reddit/read.ts +4 -5
  279. package/src/clis/reddit/save.ts +2 -3
  280. package/src/clis/reddit/saved.ts +0 -1
  281. package/src/clis/reddit/search.yaml +1 -0
  282. package/src/clis/reddit/subscribe.ts +0 -1
  283. package/src/clis/reddit/upvote.ts +2 -3
  284. package/src/clis/reddit/upvoted.ts +0 -1
  285. package/src/clis/reddit/user-comments.yaml +1 -0
  286. package/src/clis/reddit/user-posts.yaml +1 -0
  287. package/src/clis/reddit/user.yaml +1 -0
  288. package/src/clis/reuters/search.ts +1 -1
  289. package/src/clis/smzdm/search.ts +2 -3
  290. package/src/clis/stackoverflow/search.yaml +1 -0
  291. package/src/clis/steam/top-sellers.yaml +29 -0
  292. package/src/clis/twitter/accept.ts +2 -2
  293. package/src/clis/twitter/article.ts +2 -2
  294. package/src/clis/twitter/block.ts +92 -0
  295. package/src/clis/twitter/delete.ts +1 -1
  296. package/src/clis/twitter/hide-reply.ts +70 -0
  297. package/src/clis/twitter/like.ts +1 -1
  298. package/src/clis/twitter/post.ts +1 -1
  299. package/src/clis/twitter/reply-dm.ts +1 -1
  300. package/src/clis/twitter/reply.ts +2 -2
  301. package/src/clis/twitter/search.ts +1 -1
  302. package/src/clis/twitter/thread.ts +2 -2
  303. package/src/clis/twitter/trending.ts +113 -0
  304. package/src/clis/twitter/unblock.ts +75 -0
  305. package/src/clis/v2ex/topic.yaml +1 -0
  306. package/src/clis/weibo/hot.ts +0 -1
  307. package/src/clis/weread/book.ts +1 -1
  308. package/src/clis/weread/highlights.ts +1 -1
  309. package/src/clis/weread/notes.ts +1 -1
  310. package/src/clis/weread/search.ts +1 -1
  311. package/src/clis/wikipedia/search.ts +1 -1
  312. package/src/clis/xiaohongshu/creator-note-detail.test.ts +82 -33
  313. package/src/clis/xiaohongshu/creator-note-detail.ts +89 -5
  314. package/src/clis/xiaohongshu/creator-notes.test.ts +39 -6
  315. package/src/clis/xiaohongshu/creator-notes.ts +44 -5
  316. package/src/clis/xiaohongshu/creator-profile.ts +0 -1
  317. package/src/clis/xiaohongshu/creator-stats.ts +0 -1
  318. package/src/clis/xiaohongshu/download.ts +2 -3
  319. package/src/clis/xiaohongshu/feed.yaml +0 -1
  320. package/src/clis/xiaohongshu/notifications.yaml +0 -1
  321. package/src/clis/xiaohongshu/search.ts +2 -2
  322. package/src/clis/xiaohongshu/user.ts +1 -2
  323. package/src/clis/yahoo-finance/quote.ts +0 -1
  324. package/src/clis/youtube/search.ts +1 -1
  325. package/src/clis/youtube/transcript.ts +1 -1
  326. package/src/clis/youtube/video.ts +1 -1
  327. package/src/clis/zhihu/download.ts +1 -2
  328. package/src/clis/zhihu/question.ts +1 -1
  329. package/src/clis/zhihu/search.yaml +4 -3
  330. package/src/commanderAdapter.ts +113 -0
  331. package/src/{engine.ts → discovery.ts} +1 -108
  332. package/src/download/index.ts +21 -54
  333. package/src/engine.test.ts +8 -7
  334. package/src/execution.ts +138 -0
  335. package/src/explore.ts +135 -109
  336. package/src/external-clis.yaml +9 -0
  337. package/src/external.ts +15 -12
  338. package/src/main.ts +1 -1
  339. package/src/pipeline/steps/browser.ts +7 -2
  340. package/src/registry.ts +5 -0
  341. package/src/runtime.ts +9 -0
  342. package/src/serialization.ts +79 -0
  343. package/src/types.ts +1 -1
  344. package/tests/e2e/browser-public.test.ts +25 -0
  345. package/tests/e2e/public-commands.test.ts +55 -1
  346. package/dist/clis/twitter/trending.yaml +0 -46
  347. package/docs/public/CNAME +0 -1
  348. package/src/clis/twitter/trending.yaml +0 -46
  349. /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
  350. /package/dist/{chaoxing.test.d.ts → clis/bloomberg/businessweek.d.ts} +0 -0
  351. /package/dist/{coupang.test.d.ts → clis/bloomberg/economics.d.ts} +0 -0
  352. /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
  353. /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
  354. /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
package/dist/explore.js CHANGED
@@ -210,6 +210,120 @@ const FRAMEWORK_DETECT_JS = detectFramework.toString();
210
210
  const STORE_DISCOVER_JS = discoverStores.toString();
211
211
  // ── Auto-Interaction (Fuzzing) ─────────────────────────────────────────────
212
212
  const INTERACT_FUZZ_JS = interactFuzz.toString();
213
+ // ── Analysis helpers (extracted from exploreUrl) ───────────────────────────
214
+ /** Filter, deduplicate, and score network endpoints. */
215
+ function analyzeEndpoints(networkEntries) {
216
+ const seen = new Map();
217
+ for (const entry of networkEntries) {
218
+ if (!entry.url)
219
+ continue;
220
+ const ct = entry.contentType.toLowerCase();
221
+ if (ct.includes('image/') || ct.includes('font/') || ct.includes('css') || ct.includes('javascript') || ct.includes('wasm'))
222
+ continue;
223
+ if (entry.status && entry.status >= 400)
224
+ continue;
225
+ const pattern = urlToPattern(entry.url);
226
+ const key = `${entry.method}:${pattern}`;
227
+ if (seen.has(key))
228
+ continue;
229
+ const qp = [];
230
+ try {
231
+ new URL(entry.url).searchParams.forEach((_v, k) => { if (!VOLATILE_PARAMS.has(k))
232
+ qp.push(k); });
233
+ }
234
+ catch { }
235
+ const ep = {
236
+ pattern, method: entry.method, url: entry.url, status: entry.status, contentType: ct,
237
+ queryParams: qp, hasSearchParam: qp.some(p => SEARCH_PARAMS.has(p)),
238
+ hasPaginationParam: qp.some(p => PAGINATION_PARAMS.has(p)),
239
+ hasLimitParam: qp.some(p => LIMIT_PARAMS.has(p)),
240
+ authIndicators: detectAuthIndicators(entry.requestHeaders),
241
+ responseAnalysis: entry.responseBody ? analyzeResponseBody(entry.responseBody) : null,
242
+ score: 0,
243
+ };
244
+ ep.score = scoreEndpoint(ep);
245
+ seen.set(key, ep);
246
+ }
247
+ const analyzed = [...seen.values()].filter(ep => ep.score >= 5).sort((a, b) => b.score - a.score);
248
+ return { analyzed, totalCount: seen.size };
249
+ }
250
+ /** Infer CLI capabilities from analyzed endpoints. */
251
+ function inferCapabilitiesFromEndpoints(endpoints, stores, opts) {
252
+ const capabilities = [];
253
+ const usedNames = new Set();
254
+ for (const ep of endpoints.slice(0, 8)) {
255
+ let capName = inferCapabilityName(ep.url, opts.goal);
256
+ if (usedNames.has(capName)) {
257
+ const suffix = ep.pattern.split('/').filter(s => s && !s.startsWith('{') && !s.includes('.')).pop();
258
+ capName = suffix ? `${capName}_${suffix}` : `${capName}_${usedNames.size}`;
259
+ }
260
+ usedNames.add(capName);
261
+ const cols = [];
262
+ if (ep.responseAnalysis) {
263
+ for (const role of ['title', 'url', 'author', 'score', 'time']) {
264
+ if (ep.responseAnalysis.detectedFields[role])
265
+ cols.push(role);
266
+ }
267
+ }
268
+ const args = [];
269
+ if (ep.hasSearchParam)
270
+ args.push({ name: 'keyword', type: 'str', required: true });
271
+ args.push({ name: 'limit', type: 'int', required: false, default: 20 });
272
+ if (ep.hasPaginationParam)
273
+ args.push({ name: 'page', type: 'int', required: false, default: 1 });
274
+ const epStrategy = inferStrategy(ep.authIndicators);
275
+ let storeHint;
276
+ if ((epStrategy === 'intercept' || ep.authIndicators.includes('signature')) && stores.length > 0) {
277
+ for (const s of stores) {
278
+ const matchingAction = s.actions.find(a => capName.split('_').some(part => a.toLowerCase().includes(part)) ||
279
+ a.toLowerCase().includes('fetch') || a.toLowerCase().includes('get'));
280
+ if (matchingAction) {
281
+ storeHint = { store: s.id, action: matchingAction };
282
+ break;
283
+ }
284
+ }
285
+ }
286
+ capabilities.push({
287
+ name: capName, description: `${opts.site ?? detectSiteName(opts.url)} ${capName}`,
288
+ strategy: storeHint ? 'store-action' : epStrategy,
289
+ confidence: Math.min(ep.score / 20, 1.0), endpoint: ep.pattern,
290
+ itemPath: ep.responseAnalysis?.itemPath ?? null,
291
+ recommendedColumns: cols.length ? cols : ['title', 'url'],
292
+ recommendedArgs: args,
293
+ ...(storeHint ? { storeHint } : {}),
294
+ });
295
+ }
296
+ const allAuth = new Set(endpoints.flatMap(ep => ep.authIndicators));
297
+ const topStrategy = allAuth.has('signature') ? 'intercept'
298
+ : allAuth.has('bearer') || allAuth.has('csrf') ? 'header'
299
+ : allAuth.size === 0 ? 'public' : 'cookie';
300
+ return { capabilities, topStrategy, authIndicators: [...allAuth] };
301
+ }
302
+ /** Write explore artifacts (manifest, endpoints, capabilities, auth, stores) to disk. */
303
+ async function writeExploreArtifacts(targetDir, result, analyzedEndpoints, stores) {
304
+ await fs.promises.mkdir(targetDir, { recursive: true });
305
+ const tasks = [
306
+ fs.promises.writeFile(path.join(targetDir, 'manifest.json'), JSON.stringify({
307
+ site: result.site, target_url: result.target_url, final_url: result.final_url, title: result.title,
308
+ framework: result.framework, stores: stores.map(s => ({ type: s.type, id: s.id, actions: s.actions })),
309
+ top_strategy: result.top_strategy, explored_at: new Date().toISOString(),
310
+ }, null, 2)),
311
+ fs.promises.writeFile(path.join(targetDir, 'endpoints.json'), JSON.stringify(analyzedEndpoints.map(ep => ({
312
+ pattern: ep.pattern, method: ep.method, url: ep.url, status: ep.status,
313
+ contentType: ep.contentType, score: ep.score, queryParams: ep.queryParams,
314
+ itemPath: ep.responseAnalysis?.itemPath ?? null, itemCount: ep.responseAnalysis?.itemCount ?? 0,
315
+ detectedFields: ep.responseAnalysis?.detectedFields ?? {}, authIndicators: ep.authIndicators,
316
+ })), null, 2)),
317
+ fs.promises.writeFile(path.join(targetDir, 'capabilities.json'), JSON.stringify(result.capabilities, null, 2)),
318
+ fs.promises.writeFile(path.join(targetDir, 'auth.json'), JSON.stringify({
319
+ top_strategy: result.top_strategy, indicators: result.auth_indicators, framework: result.framework,
320
+ }, null, 2)),
321
+ ];
322
+ if (stores.length > 0) {
323
+ tasks.push(fs.promises.writeFile(path.join(targetDir, 'stores.json'), JSON.stringify(stores, null, 2)));
324
+ }
325
+ await Promise.all(tasks);
326
+ }
213
327
  // ── Main explore function ──────────────────────────────────────────────────
214
328
  export async function exploreUrl(url, opts) {
215
329
  const waitSeconds = opts.waitSeconds ?? 3.0;
@@ -301,120 +415,20 @@ export async function exploreUrl(url, opts) {
301
415
  }
302
416
  catch { }
303
417
  }
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';
418
+ // Step 7+8: Analyze endpoints and infer capabilities
419
+ const { analyzed: analyzedEndpoints, totalCount } = analyzeEndpoints(networkEntries);
420
+ const { capabilities, topStrategy, authIndicators } = inferCapabilitiesFromEndpoints(analyzedEndpoints, stores, { site: opts.site, goal: opts.goal, url });
421
+ // Step 9: Assemble result and write artifacts
387
422
  const siteName = opts.site ?? detectSiteName(metadata.url || url);
388
423
  const targetDir = opts.outDir ?? path.join('.opencli', 'explore', siteName);
389
- await fs.promises.mkdir(targetDir, { recursive: true });
390
424
  const result = {
391
425
  site: siteName, target_url: url, final_url: metadata.url, title: metadata.title,
392
426
  framework, stores, top_strategy: topStrategy,
393
- endpoint_count: analyzedEndpoints.length + [...seen.values()].filter(ep => ep.score < 5).length,
427
+ endpoint_count: totalCount,
394
428
  api_endpoint_count: analyzedEndpoints.length,
395
- capabilities, auth_indicators: [...allAuth],
429
+ capabilities, auth_indicators: authIndicators,
396
430
  };
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);
431
+ await writeExploreArtifacts(targetDir, result, analyzedEndpoints, stores);
418
432
  return { ...result, out_dir: targetDir };
419
433
  })(), { timeout: exploreTimeout, label: `Explore ${url}` });
420
434
  }, { workspace: opts.workspace });
@@ -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
  }
package/dist/main.js CHANGED
@@ -5,7 +5,7 @@
5
5
  import * as os from 'node:os';
6
6
  import * as path from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
8
- import { discoverClis } from './engine.js';
8
+ import { discoverClis } from './discovery.js';
9
9
  import { getCompletions } from './completion.js';
10
10
  import { runCli } from './cli.js';
11
11
  const __filename = fileURLToPath(import.meta.url);
@@ -4,8 +4,14 @@
4
4
  */
5
5
  import { render } from '../template.js';
6
6
  export async function stepNavigate(page, params, data, args) {
7
- const url = render(params, { args, data });
8
- await page.goto(String(url));
7
+ if (typeof params === 'object' && params && 'url' in params) {
8
+ const url = String(render(params.url, { args, data }));
9
+ await page.goto(url, { waitUntil: params.waitUntil, settleMs: params.settleMs });
10
+ }
11
+ else {
12
+ const url = render(params, { args, data });
13
+ await page.goto(String(url));
14
+ }
9
15
  return data;
10
16
  }
11
17
  export async function stepClick(page, params, data, args) {
@@ -49,3 +49,5 @@ export declare function getRegistry(): Map<string, CliCommand>;
49
49
  export declare function fullName(cmd: CliCommand): string;
50
50
  export declare function strategyLabel(cmd: CliCommand): string;
51
51
  export declare function registerCommand(cmd: CliCommand): void;
52
+ export { serializeArg, serializeCommand, formatArgSummary, formatRegistryHelpText } from './serialization.js';
53
+ export type { SerializedArg } from './serialization.js';
package/dist/registry.js CHANGED
@@ -43,3 +43,5 @@ export function strategyLabel(cmd) {
43
43
  export function registerCommand(cmd) {
44
44
  _registry.set(fullName(cmd), cmd);
45
45
  }
46
+ // Re-export serialization helpers from their dedicated module
47
+ export { serializeArg, serializeCommand, formatArgSummary, formatRegistryHelpText } from './serialization.js';
package/dist/runtime.d.ts CHANGED
@@ -1,4 +1,9 @@
1
1
  import type { IPage } from './types.js';
2
+ /**
3
+ * Returns the appropriate browser factory based on environment config.
4
+ * Uses CDPBridge when OPENCLI_CDP_ENDPOINT is set, otherwise BrowserBridge.
5
+ */
6
+ export declare function getBrowserFactory(): new () => IBrowserFactory;
2
7
  export declare const DEFAULT_BROWSER_CONNECT_TIMEOUT: number;
3
8
  export declare const DEFAULT_BROWSER_COMMAND_TIMEOUT: number;
4
9
  export declare const DEFAULT_BROWSER_EXPLORE_TIMEOUT: number;
package/dist/runtime.js CHANGED
@@ -1,3 +1,11 @@
1
+ import { BrowserBridge, CDPBridge } from './browser/index.js';
2
+ /**
3
+ * Returns the appropriate browser factory based on environment config.
4
+ * Uses CDPBridge when OPENCLI_CDP_ENDPOINT is set, otherwise BrowserBridge.
5
+ */
6
+ export function getBrowserFactory() {
7
+ return (process.env.OPENCLI_CDP_ENDPOINT ? CDPBridge : BrowserBridge);
8
+ }
1
9
  export const DEFAULT_BROWSER_CONNECT_TIMEOUT = parseInt(process.env.OPENCLI_BROWSER_CONNECT_TIMEOUT ?? '30', 10);
2
10
  export const DEFAULT_BROWSER_COMMAND_TIMEOUT = parseInt(process.env.OPENCLI_BROWSER_COMMAND_TIMEOUT ?? '60', 10);
3
11
  export const DEFAULT_BROWSER_EXPLORE_TIMEOUT = parseInt(process.env.OPENCLI_BROWSER_EXPLORE_TIMEOUT ?? '120', 10);
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Serialization and formatting helpers for CLI commands and args.
3
+ *
4
+ * Used by the `list` command, Commander --help, and build-manifest.
5
+ * Separated from registry.ts to keep the registry focused on types + registration.
6
+ */
7
+ import type { Arg, CliCommand } from './registry.js';
8
+ export type SerializedArg = {
9
+ name: string;
10
+ type: string;
11
+ required: boolean;
12
+ positional: boolean;
13
+ choices: string[];
14
+ default: unknown;
15
+ help: string;
16
+ };
17
+ /** Stable arg schema — every field is always present (no sparse objects). */
18
+ export declare function serializeArg(a: Arg): SerializedArg;
19
+ /** Full command metadata for structured output (json/yaml). */
20
+ export declare function serializeCommand(cmd: CliCommand): {
21
+ command: string;
22
+ site: string;
23
+ name: string;
24
+ description: string;
25
+ strategy: string;
26
+ browser: boolean;
27
+ args: SerializedArg[];
28
+ columns: string[];
29
+ domain: string | null;
30
+ };
31
+ /** Human-readable arg summary: `<required> [optional]` style. */
32
+ export declare function formatArgSummary(args: Arg[]): string;
33
+ /** Generate the --help appendix showing registry metadata not exposed by Commander. */
34
+ export declare function formatRegistryHelpText(cmd: CliCommand): string;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Serialization and formatting helpers for CLI commands and args.
3
+ *
4
+ * Used by the `list` command, Commander --help, and build-manifest.
5
+ * Separated from registry.ts to keep the registry focused on types + registration.
6
+ */
7
+ import { fullName, strategyLabel } from './registry.js';
8
+ /** Stable arg schema — every field is always present (no sparse objects). */
9
+ export function serializeArg(a) {
10
+ return {
11
+ name: a.name,
12
+ type: a.type ?? 'string',
13
+ required: !!a.required,
14
+ positional: !!a.positional,
15
+ choices: a.choices ?? [],
16
+ default: a.default ?? null,
17
+ help: a.help ?? '',
18
+ };
19
+ }
20
+ /** Full command metadata for structured output (json/yaml). */
21
+ export function serializeCommand(cmd) {
22
+ return {
23
+ command: fullName(cmd),
24
+ site: cmd.site,
25
+ name: cmd.name,
26
+ description: cmd.description,
27
+ strategy: strategyLabel(cmd),
28
+ browser: !!cmd.browser,
29
+ args: cmd.args.map(serializeArg),
30
+ columns: cmd.columns ?? [],
31
+ domain: cmd.domain ?? null,
32
+ };
33
+ }
34
+ // ── Formatting ──────────────────────────────────────────────────────────────
35
+ /** Human-readable arg summary: `<required> [optional]` style. */
36
+ export function formatArgSummary(args) {
37
+ return args
38
+ .map(a => {
39
+ if (a.positional)
40
+ return a.required ? `<${a.name}>` : `[${a.name}]`;
41
+ return a.required ? `--${a.name}` : `[--${a.name}]`;
42
+ })
43
+ .join(' ');
44
+ }
45
+ /** Generate the --help appendix showing registry metadata not exposed by Commander. */
46
+ export function formatRegistryHelpText(cmd) {
47
+ const lines = [];
48
+ const choicesArgs = cmd.args.filter(a => a.choices?.length);
49
+ for (const a of choicesArgs) {
50
+ const prefix = a.positional ? `<${a.name}>` : `--${a.name}`;
51
+ const def = a.default != null ? ` (default: ${a.default})` : '';
52
+ lines.push(` ${prefix}: ${a.choices.join(', ')}${def}`);
53
+ }
54
+ const meta = [];
55
+ meta.push(`Strategy: ${strategyLabel(cmd)}`);
56
+ meta.push(`Browser: ${cmd.browser ? 'yes' : 'no'}`);
57
+ if (cmd.domain)
58
+ meta.push(`Domain: ${cmd.domain}`);
59
+ lines.push(meta.join(' | '));
60
+ if (cmd.columns?.length)
61
+ lines.push(`Output columns: ${cmd.columns.join(', ')}`);
62
+ return '\n' + lines.join('\n') + '\n';
63
+ }
package/dist/types.d.ts CHANGED
@@ -5,7 +5,10 @@
5
5
  * instead of `any` for browser interactions.
6
6
  */
7
7
  export interface IPage {
8
- goto(url: string): Promise<void>;
8
+ goto(url: string, options?: {
9
+ waitUntil?: 'load' | 'none';
10
+ settleMs?: number;
11
+ }): Promise<void>;
9
12
  evaluate(js: string): Promise<any>;
10
13
  getCookies(opts?: {
11
14
  domain?: string;
@@ -1,7 +1,7 @@
1
1
  import { defineConfig } from 'vitepress'
2
2
 
3
3
  export default defineConfig({
4
- base: '/',
4
+ base: '/docs/',
5
5
  title: 'OpenCLI',
6
6
  description: 'Make any website or Electron App your CLI — AI-powered, account-safe, self-healing.',
7
7
 
@@ -54,12 +54,19 @@ export default defineConfig({
54
54
  { text: 'YouTube', link: '/adapters/browser/youtube' },
55
55
  { text: 'Xueqiu', link: '/adapters/browser/xueqiu' },
56
56
  { text: 'V2EX', link: '/adapters/browser/v2ex' },
57
+ { text: 'Bloomberg', link: '/adapters/browser/bloomberg' },
57
58
  { text: 'LinkedIn', link: '/adapters/browser/linkedin' },
58
59
  { text: 'Coupang', link: '/adapters/browser/coupang' },
59
60
  { text: 'BOSS Zhipin', link: '/adapters/browser/boss' },
60
61
  { text: 'Ctrip', link: '/adapters/browser/ctrip' },
61
62
  { text: 'Reuters', link: '/adapters/browser/reuters' },
62
63
  { text: 'SMZDM', link: '/adapters/browser/smzdm' },
64
+ { text: 'Jike', link: '/adapters/browser/jike' },
65
+ { text: 'Jimeng', link: '/adapters/browser/jimeng' },
66
+ { text: 'LINUX DO', link: '/adapters/browser/linux-do' },
67
+ { text: 'Chaoxing', link: '/adapters/browser/chaoxing' },
68
+ { text: 'Grok', link: '/adapters/browser/grok' },
69
+ { text: 'WeRead', link: '/adapters/browser/weread' },
63
70
  ],
64
71
  },
65
72
  {
@@ -72,6 +79,12 @@ export default defineConfig({
72
79
  { text: 'Apple Podcasts', link: '/adapters/browser/apple-podcasts' },
73
80
  { text: 'Xiaoyuzhou', link: '/adapters/browser/xiaoyuzhou' },
74
81
  { text: 'Yahoo Finance', link: '/adapters/browser/yahoo-finance' },
82
+ { text: 'arXiv', link: '/adapters/browser/arxiv' },
83
+ { text: 'Barchart', link: '/adapters/browser/barchart' },
84
+ { text: 'Hugging Face', link: '/adapters/browser/hf' },
85
+ { text: 'Sina Finance', link: '/adapters/browser/sinafinance' },
86
+ { text: 'Stack Overflow', link: '/adapters/browser/stackoverflow' },
87
+ { text: 'Wikipedia', link: '/adapters/browser/wikipedia' },
75
88
  ],
76
89
  },
77
90
  {
@@ -137,7 +150,6 @@ export default defineConfig({
137
150
  { text: '快速开始', link: '/zh/guide/getting-started' },
138
151
  { text: '安装', link: '/zh/guide/installation' },
139
152
  { text: 'Browser Bridge', link: '/zh/guide/browser-bridge' },
140
- { text: '问题排查', link: '/zh/guide/troubleshooting' },
141
153
  ],
142
154
  },
143
155
  ],
@@ -154,7 +166,6 @@ export default defineConfig({
154
166
  text: '开发者指南',
155
167
  items: [
156
168
  { text: '贡献指南', link: '/zh/developer/contributing' },
157
- { text: '测试', link: '/zh/developer/testing' },
158
169
  ],
159
170
  },
160
171
  ],
@@ -0,0 +1,27 @@
1
+ # arXiv
2
+
3
+ **Mode**: 🌐 Public · **Domain**: `arxiv.org`
4
+
5
+ ## Commands
6
+
7
+ | Command | Description |
8
+ |---------|-------------|
9
+ | `opencli arxiv search` | Search arXiv papers |
10
+ | `opencli arxiv paper` | Get arXiv paper details by ID |
11
+
12
+ ## Usage Examples
13
+
14
+ ```bash
15
+ # Search for papers
16
+ opencli arxiv search "transformer attention" --limit 10
17
+
18
+ # Get paper details by arXiv ID
19
+ opencli arxiv paper 2301.00001
20
+
21
+ # JSON output
22
+ opencli arxiv search "LLM" -f json
23
+ ```
24
+
25
+ ## Prerequisites
26
+
27
+ - No browser required — uses public arXiv API