@jackwener/opencli 1.7.12 → 1.7.14

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 (419) hide show
  1. package/README.md +8 -7
  2. package/README.zh-CN.md +9 -8
  3. package/cli-manifest.json +12128 -6665
  4. package/clis/1point3acres/digest.js +35 -0
  5. package/clis/1point3acres/forum.js +51 -0
  6. package/clis/1point3acres/forums.js +44 -0
  7. package/clis/1point3acres/hot.js +35 -0
  8. package/clis/1point3acres/latest.js +35 -0
  9. package/clis/1point3acres/notifications.js +64 -0
  10. package/clis/1point3acres/search.js +71 -0
  11. package/clis/1point3acres/thread.js +117 -0
  12. package/clis/1point3acres/user.js +77 -0
  13. package/clis/1point3acres/utils.js +247 -0
  14. package/clis/_shared/desktop-commands.js +4 -0
  15. package/clis/aibase/news.js +110 -0
  16. package/clis/aibase/news.test.js +59 -0
  17. package/clis/amazon/discussion.test.js +1 -28
  18. package/clis/antigravity/watch.js +3 -2
  19. package/clis/arxiv/author.js +44 -0
  20. package/clis/baidu-scholar/search.js +0 -1
  21. package/clis/bbc/topic.js +57 -0
  22. package/clis/bbc/utils.js +79 -0
  23. package/clis/chaoxing/assignments.js +1 -1
  24. package/clis/chaoxing/exams.js +1 -1
  25. package/clis/chatgpt/ask.js +57 -0
  26. package/clis/chatgpt/commands.test.js +45 -0
  27. package/clis/chatgpt/detail.js +46 -0
  28. package/clis/chatgpt/history.js +39 -0
  29. package/clis/chatgpt/image.js +12 -11
  30. package/clis/chatgpt/image.test.js +23 -0
  31. package/clis/chatgpt/new.js +25 -0
  32. package/clis/chatgpt/read.js +43 -0
  33. package/clis/chatgpt/send.js +46 -0
  34. package/clis/chatgpt/status.js +29 -0
  35. package/clis/chatgpt/utils.js +294 -4
  36. package/clis/chatgpt/utils.test.js +13 -0
  37. package/clis/chatgpt-app/ask.js +6 -3
  38. package/clis/chatwise/ask.js +16 -43
  39. package/clis/chatwise/composer.test.js +186 -0
  40. package/clis/chatwise/send.js +2 -24
  41. package/clis/chatwise/utils.js +143 -0
  42. package/clis/claude/ask.js +1 -1
  43. package/clis/claude/detail.js +1 -0
  44. package/clis/claude/history.js +1 -0
  45. package/clis/claude/new.js +1 -0
  46. package/clis/claude/read.js +1 -0
  47. package/clis/claude/send.js +1 -0
  48. package/clis/claude/status.js +1 -0
  49. package/clis/codex/ask.js +15 -9
  50. package/clis/codex/history.js +16 -33
  51. package/clis/codex/projects.js +28 -0
  52. package/clis/codex/read.js +10 -4
  53. package/clis/codex/send.js +10 -3
  54. package/clis/codex/sidebar.js +356 -0
  55. package/clis/codex/sidebar.test.js +329 -0
  56. package/clis/coingecko/categories.js +75 -0
  57. package/clis/coingecko/coin.js +107 -0
  58. package/clis/coingecko/coingecko.test.js +109 -0
  59. package/clis/coingecko/derivatives.js +84 -0
  60. package/clis/coingecko/exchanges.js +74 -0
  61. package/clis/coingecko/global.js +71 -0
  62. package/clis/coingecko/top.js +64 -0
  63. package/clis/coingecko/trending.js +55 -0
  64. package/clis/coupang/add-to-cart.js +21 -13
  65. package/clis/coupang/coupang.test.js +159 -0
  66. package/clis/coupang/product.js +257 -0
  67. package/clis/coupang/search.js +38 -16
  68. package/clis/coupang/utils.js +55 -1
  69. package/clis/crates/crate.js +62 -0
  70. package/clis/crates/search.js +44 -0
  71. package/clis/crates/utils.js +72 -0
  72. package/clis/ctrip/ctrip.test.js +234 -0
  73. package/clis/ctrip/hotel-suggest.js +45 -0
  74. package/clis/ctrip/search.js +22 -68
  75. package/clis/ctrip/utils.js +175 -0
  76. package/clis/cursor/ask.js +6 -3
  77. package/clis/dblp/author.js +133 -0
  78. package/clis/dblp/venue.js +64 -0
  79. package/clis/deepseek/ask.js +12 -7
  80. package/clis/deepseek/ask.test.js +13 -13
  81. package/clis/deepseek/detail.js +38 -0
  82. package/clis/deepseek/detail.test.js +81 -0
  83. package/clis/deepseek/history.js +1 -0
  84. package/clis/deepseek/new.js +1 -0
  85. package/clis/deepseek/read.js +1 -0
  86. package/clis/deepseek/send.js +140 -0
  87. package/clis/deepseek/send.test.js +107 -0
  88. package/clis/deepseek/status.js +1 -0
  89. package/clis/deepseek/utils.js +66 -0
  90. package/clis/deepseek/utils.test.js +107 -1
  91. package/clis/defillama/defillama.test.js +99 -0
  92. package/clis/defillama/protocol.js +84 -0
  93. package/clis/defillama/protocols.js +55 -0
  94. package/clis/defillama/utils.js +99 -0
  95. package/clis/devto/latest.js +74 -0
  96. package/clis/dockerhub/image.js +52 -0
  97. package/clis/dockerhub/search.js +47 -0
  98. package/clis/dockerhub/utils.js +100 -0
  99. package/clis/doubao/ask.js +7 -3
  100. package/clis/doubao/detail.js +1 -0
  101. package/clis/doubao/history.js +1 -0
  102. package/clis/doubao/meeting-summary.js +1 -0
  103. package/clis/doubao/meeting-transcript.js +1 -0
  104. package/clis/doubao/new.js +1 -0
  105. package/clis/doubao/read.js +1 -0
  106. package/clis/doubao/send.js +1 -0
  107. package/clis/doubao/status.js +1 -0
  108. package/clis/douyin/draft.test.js +1 -30
  109. package/clis/endoflife/endoflife.test.js +51 -0
  110. package/clis/endoflife/product.js +55 -0
  111. package/clis/endoflife/utils.js +89 -0
  112. package/clis/facebook/__fixtures__/notifications-page.html +13 -0
  113. package/clis/facebook/notifications.js +326 -30
  114. package/clis/facebook/notifications.test.js +458 -0
  115. package/clis/flathub/app.js +71 -0
  116. package/clis/flathub/flathub.test.js +90 -0
  117. package/clis/flathub/search.js +80 -0
  118. package/clis/flathub/utils.js +114 -0
  119. package/clis/gemini/ask.js +7 -3
  120. package/clis/gemini/ask.test.js +2 -2
  121. package/clis/gemini/deep-research-result.js +6 -2
  122. package/clis/gemini/deep-research-result.test.js +15 -14
  123. package/clis/gemini/deep-research.js +8 -4
  124. package/clis/gemini/deep-research.test.js +15 -18
  125. package/clis/gemini/image.js +7 -2
  126. package/clis/gemini/new.js +1 -0
  127. package/clis/gemini/utils.js +0 -4
  128. package/clis/google-scholar/cite.js +0 -1
  129. package/clis/google-scholar/profile.js +0 -1
  130. package/clis/google-scholar/search.js +0 -1
  131. package/clis/goproxy/goproxy.test.js +103 -0
  132. package/clis/goproxy/module.js +47 -0
  133. package/clis/goproxy/utils.js +165 -0
  134. package/clis/goproxy/versions.js +59 -0
  135. package/clis/gov-law/recent.js +0 -1
  136. package/clis/gov-law/search.js +0 -1
  137. package/clis/gov-policy/__fixtures__/recent.html +16 -0
  138. package/clis/gov-policy/__fixtures__/search.html +41 -0
  139. package/clis/gov-policy/gov-policy.test.js +224 -0
  140. package/clis/gov-policy/recent.js +66 -24
  141. package/clis/gov-policy/search.js +65 -23
  142. package/clis/gov-policy/utils.js +54 -0
  143. package/clis/grok/ask.js +49 -265
  144. package/clis/grok/ask.test.js +21 -46
  145. package/clis/grok/detail.js +60 -0
  146. package/clis/grok/history.js +48 -0
  147. package/clis/grok/{image.ts → image.js} +56 -70
  148. package/clis/grok/image.test.ts +20 -0
  149. package/clis/grok/new.js +20 -0
  150. package/clis/grok/read.js +39 -0
  151. package/clis/grok/send.js +50 -0
  152. package/clis/grok/status.js +41 -0
  153. package/clis/grok/utils.js +326 -0
  154. package/clis/grok/utils.test.js +103 -0
  155. package/clis/hf/datasets.js +88 -0
  156. package/clis/hf/hf.test.js +16 -0
  157. package/clis/hf/models.js +91 -0
  158. package/clis/hf/paper.js +79 -0
  159. package/clis/hf/spaces.js +101 -0
  160. package/clis/hf/top.js +1 -0
  161. package/clis/homebrew/cask.js +39 -0
  162. package/clis/homebrew/formula.js +41 -0
  163. package/clis/homebrew/popular.js +54 -0
  164. package/clis/homebrew/utils.js +100 -0
  165. package/clis/hupu/__fixtures__/hot-home.html +64 -0
  166. package/clis/hupu/detail.js +0 -1
  167. package/clis/hupu/hot.js +156 -35
  168. package/clis/hupu/hot.test.js +224 -0
  169. package/clis/hupu/search.js +0 -1
  170. package/clis/instagram/note.js +1 -1
  171. package/clis/instagram/note.test.js +1 -29
  172. package/clis/instagram/post.js +1 -1
  173. package/clis/instagram/post.test.js +1 -1
  174. package/clis/instagram/reel.js +1 -1
  175. package/clis/instagram/story.js +1 -1
  176. package/clis/instagram/story.test.js +1 -34
  177. package/clis/jd/commands.test.js +1 -24
  178. package/clis/lichess/lichess.test.js +85 -0
  179. package/clis/lichess/top.js +46 -0
  180. package/clis/lichess/user.js +91 -0
  181. package/clis/lichess/utils.js +97 -0
  182. package/clis/linkedin/search.js +107 -10
  183. package/clis/linkedin/search.test.js +222 -0
  184. package/clis/linux-do/feed.js +2 -5
  185. package/clis/linux-do/feed.test.js +35 -0
  186. package/clis/lobsters/domain.js +92 -0
  187. package/clis/maven/artifact.js +49 -0
  188. package/clis/maven/search.js +51 -0
  189. package/clis/maven/utils.js +110 -0
  190. package/clis/mdn/search.js +97 -0
  191. package/clis/medium/tag.js +135 -0
  192. package/clis/npm/downloads.js +59 -0
  193. package/clis/npm/package.js +70 -0
  194. package/clis/npm/search.js +49 -0
  195. package/clis/npm/utils.js +76 -0
  196. package/clis/nuget/nuget.test.js +111 -0
  197. package/clis/nuget/package.js +101 -0
  198. package/clis/nuget/search.js +69 -0
  199. package/clis/nuget/utils.js +87 -0
  200. package/clis/nvd/cve.js +121 -0
  201. package/clis/oeis/oeis.test.js +88 -0
  202. package/clis/oeis/search.js +63 -0
  203. package/clis/oeis/sequence.js +71 -0
  204. package/clis/oeis/utils.js +88 -0
  205. package/clis/openalex/search.js +69 -0
  206. package/clis/openalex/utils.js +160 -0
  207. package/clis/openalex/work.js +65 -0
  208. package/clis/openfda/drug-label.js +74 -0
  209. package/clis/openfda/food-recall.js +65 -0
  210. package/clis/openfda/openfda.test.js +114 -0
  211. package/clis/openfda/utils.js +67 -0
  212. package/clis/osv/osv.test.js +97 -0
  213. package/clis/osv/query.js +72 -0
  214. package/clis/osv/utils.js +169 -0
  215. package/clis/osv/vulnerability.js +54 -0
  216. package/clis/packagist/package.js +49 -0
  217. package/clis/packagist/search.js +43 -0
  218. package/clis/packagist/utils.js +113 -0
  219. package/clis/paperreview/feedback.js +1 -1
  220. package/clis/paperreview/review.js +1 -1
  221. package/clis/paperreview/submit.js +1 -1
  222. package/clis/pixiv/download.test.js +1 -1
  223. package/clis/pixiv/illusts.test.js +1 -1
  224. package/clis/pixiv/search.test.js +1 -1
  225. package/clis/pubmed/article.js +50 -0
  226. package/clis/pubmed/author.js +64 -0
  227. package/clis/pubmed/citations.js +36 -0
  228. package/clis/pubmed/pubmed.test.js +276 -0
  229. package/clis/pubmed/related.js +45 -0
  230. package/clis/pubmed/search.js +75 -0
  231. package/clis/pubmed/utils.js +309 -0
  232. package/clis/pypi/downloads.js +66 -0
  233. package/clis/pypi/package.js +79 -0
  234. package/clis/pypi/utils.js +55 -0
  235. package/clis/quark/mv.js +1 -1
  236. package/clis/quark/save.js +1 -1
  237. package/clis/qwen/ask.js +85 -0
  238. package/clis/qwen/detail.js +62 -0
  239. package/clis/qwen/history.js +61 -0
  240. package/clis/qwen/image.js +179 -0
  241. package/clis/qwen/new.js +23 -0
  242. package/clis/qwen/read.js +41 -0
  243. package/clis/qwen/send.js +55 -0
  244. package/clis/qwen/status.js +37 -0
  245. package/clis/qwen/utils.js +409 -0
  246. package/clis/qwen/utils.test.js +45 -0
  247. package/clis/rest-countries/country.js +65 -0
  248. package/clis/rest-countries/region.js +64 -0
  249. package/clis/rest-countries/rest-countries.test.js +83 -0
  250. package/clis/rest-countries/utils.js +126 -0
  251. package/clis/reuters/article-detail.js +53 -0
  252. package/clis/reuters/reuters.test.js +299 -0
  253. package/clis/reuters/search.js +45 -34
  254. package/clis/reuters/utils.js +159 -0
  255. package/clis/rfc/rfc.js +52 -0
  256. package/clis/rfc/rfc.test.js +74 -0
  257. package/clis/rfc/utils.js +72 -0
  258. package/clis/rubygems/gem.js +42 -0
  259. package/clis/rubygems/search.js +47 -0
  260. package/clis/rubygems/utils.js +86 -0
  261. package/clis/stackoverflow/related.js +66 -0
  262. package/clis/stackoverflow/stackoverflow.test.js +58 -0
  263. package/clis/stackoverflow/tag.js +60 -0
  264. package/clis/stackoverflow/user.js +50 -0
  265. package/clis/stackoverflow/utils.js +118 -0
  266. package/clis/steam/app.js +67 -0
  267. package/clis/steam/search.js +58 -0
  268. package/clis/steam/steam.test.js +46 -0
  269. package/clis/steam/utils.js +107 -0
  270. package/clis/taobao/commands.test.js +1 -24
  271. package/clis/test-utils.js +61 -0
  272. package/clis/tieba/hot.js +0 -1
  273. package/clis/tiktok/comment.js +128 -41
  274. package/clis/tiktok/creator-videos.js +270 -0
  275. package/clis/tiktok/creator-videos.test.js +113 -0
  276. package/clis/tiktok/explore.js +137 -29
  277. package/clis/tiktok/follow.js +115 -33
  278. package/clis/tiktok/following.js +157 -36
  279. package/clis/tiktok/friends.js +139 -37
  280. package/clis/tiktok/live.js +137 -41
  281. package/clis/tiktok/notifications.js +141 -38
  282. package/clis/tiktok/refactor.test.js +389 -0
  283. package/clis/tiktok/unfollow.js +124 -38
  284. package/clis/tiktok/user.js +203 -29
  285. package/clis/tiktok/utils.js +505 -0
  286. package/clis/tiktok/write-refactor.test.js +370 -0
  287. package/clis/toutiao/articles.js +36 -62
  288. package/clis/toutiao/hot.js +63 -0
  289. package/clis/toutiao/toutiao.test.js +378 -0
  290. package/clis/toutiao/utils.js +161 -0
  291. package/clis/tvmaze/search.js +61 -0
  292. package/clis/tvmaze/show.js +60 -0
  293. package/clis/tvmaze/tvmaze.test.js +93 -0
  294. package/clis/tvmaze/utils.js +110 -0
  295. package/clis/twitter/accept.js +1 -1
  296. package/clis/twitter/followers.js +134 -69
  297. package/clis/twitter/quote.js +139 -0
  298. package/clis/twitter/quote.test.js +106 -0
  299. package/clis/twitter/reply-dm.js +1 -1
  300. package/clis/twitter/reply.test.js +1 -29
  301. package/clis/twitter/retweet.js +99 -0
  302. package/clis/twitter/retweet.test.js +69 -0
  303. package/clis/twitter/shared.js +38 -0
  304. package/clis/twitter/shared.test.js +28 -1
  305. package/clis/twitter/unlike.js +87 -0
  306. package/clis/twitter/unlike.test.js +72 -0
  307. package/clis/twitter/unretweet.js +99 -0
  308. package/clis/twitter/unretweet.test.js +69 -0
  309. package/clis/uisdc/news.js +105 -0
  310. package/clis/uisdc/news.test.js +66 -0
  311. package/clis/wanfang/search.js +0 -1
  312. package/clis/web/read.js +47 -17
  313. package/clis/web/read.test.js +101 -1
  314. package/clis/weixin/create-draft.js +1 -1
  315. package/clis/weixin/drafts.js +1 -1
  316. package/clis/weixin/drafts.test.js +5 -1
  317. package/clis/weixin/search.js +157 -0
  318. package/clis/weixin/search.test.js +227 -0
  319. package/clis/wikidata/entity.js +60 -0
  320. package/clis/wikidata/search.js +50 -0
  321. package/clis/wikidata/utils.js +117 -0
  322. package/clis/wikidata/wikidata.test.js +83 -0
  323. package/clis/wikipedia/page.js +95 -0
  324. package/clis/wttr/current.js +63 -0
  325. package/clis/wttr/forecast.js +71 -0
  326. package/clis/wttr/utils.js +50 -0
  327. package/clis/wttr/wttr.test.js +84 -0
  328. package/clis/xianyu/chat.js +16 -4
  329. package/clis/xianyu/chat.test.js +64 -0
  330. package/clis/xianyu/publish.js +485 -0
  331. package/clis/xianyu/publish.test.js +220 -0
  332. package/clis/xiaoe/catalog.js +105 -40
  333. package/clis/xiaoe/content.js +164 -29
  334. package/clis/xiaoe/courses.js +86 -29
  335. package/clis/xiaoe/xiaoe.test.js +486 -0
  336. package/clis/xiaohongshu/creator-notes-summary.js +1 -1
  337. package/clis/xiaohongshu/publish.js +16 -3
  338. package/clis/xiaohongshu/publish.test.js +46 -1
  339. package/clis/youtube/transcript.js +13 -19
  340. package/clis/youtube/transcript.test.js +17 -0
  341. package/clis/yuanbao/ask.js +17 -66
  342. package/clis/yuanbao/ask.test.js +5 -5
  343. package/clis/yuanbao/detail.js +65 -0
  344. package/clis/yuanbao/history.js +51 -0
  345. package/clis/yuanbao/new.js +1 -0
  346. package/clis/yuanbao/read.js +38 -0
  347. package/clis/yuanbao/send.js +57 -0
  348. package/clis/yuanbao/shared.js +297 -5
  349. package/clis/yuanbao/shared.test.js +80 -0
  350. package/clis/yuanbao/status.js +44 -0
  351. package/clis/zlibrary/commands.test.js +1 -11
  352. package/dist/src/browser/base-page.d.ts +9 -0
  353. package/dist/src/browser/base-page.js +44 -1
  354. package/dist/src/browser/base-page.test.js +66 -0
  355. package/dist/src/browser/bridge.js +47 -45
  356. package/dist/src/browser/cdp.d.ts +1 -0
  357. package/dist/src/browser/cdp.js +51 -9
  358. package/dist/src/browser/daemon-client.d.ts +4 -0
  359. package/dist/src/browser/errors.js +1 -1
  360. package/dist/src/browser/page.d.ts +1 -1
  361. package/dist/src/browser/page.js +3 -1
  362. package/dist/src/browser/page.test.js +29 -0
  363. package/dist/src/browser/target-errors.d.ts +2 -1
  364. package/dist/src/browser/target-errors.js +1 -0
  365. package/dist/src/browser/target-resolver.d.ts +25 -0
  366. package/dist/src/browser/target-resolver.js +43 -0
  367. package/dist/src/browser.test.js +18 -0
  368. package/dist/src/build-manifest.js +9 -4
  369. package/dist/src/build-manifest.test.js +2 -8
  370. package/dist/src/capabilityRouting.d.ts +16 -1
  371. package/dist/src/capabilityRouting.js +24 -1
  372. package/dist/src/capabilityRouting.test.js +19 -1
  373. package/dist/src/cli.js +76 -11
  374. package/dist/src/cli.test.js +241 -1
  375. package/dist/src/commanderAdapter.js +23 -9
  376. package/dist/src/commanderAdapter.test.js +0 -1
  377. package/dist/src/discovery.js +2 -5
  378. package/dist/src/errors.js +1 -1
  379. package/dist/src/execution.d.ts +1 -1
  380. package/dist/src/execution.js +111 -27
  381. package/dist/src/execution.test.js +326 -17
  382. package/dist/src/help.d.ts +27 -2
  383. package/dist/src/help.js +196 -23
  384. package/dist/src/help.test.d.ts +1 -0
  385. package/dist/src/help.test.js +54 -0
  386. package/dist/src/main.js +14 -1
  387. package/dist/src/manifest-types.d.ts +5 -3
  388. package/dist/src/pipeline/executor.js +1 -1
  389. package/dist/src/pipeline/executor.test.js +8 -0
  390. package/dist/src/pipeline/registry.d.ts +9 -0
  391. package/dist/src/pipeline/registry.js +13 -1
  392. package/dist/src/pipeline/steps/browser.d.ts +1 -0
  393. package/dist/src/pipeline/steps/browser.js +10 -0
  394. package/dist/src/pipeline/steps/download.test.js +1 -0
  395. package/dist/src/registry-api.d.ts +1 -1
  396. package/dist/src/registry.d.ts +12 -11
  397. package/dist/src/registry.js +16 -6
  398. package/dist/src/registry.test.js +2 -2
  399. package/dist/src/runtime.d.ts +2 -1
  400. package/dist/src/runtime.js +1 -1
  401. package/dist/src/serialization.d.ts +2 -2
  402. package/dist/src/serialization.js +4 -6
  403. package/dist/src/serialization.test.js +17 -0
  404. package/dist/src/types.d.ts +17 -0
  405. package/dist/src/validate.js +15 -11
  406. package/dist/src/validate.test.d.ts +9 -0
  407. package/dist/src/validate.test.js +90 -0
  408. package/package.json +1 -1
  409. package/scripts/fetch-adapters.js +1 -1
  410. package/scripts/typed-error-lint-baseline.json +5 -77
  411. package/clis/ctrip/search.test.js +0 -64
  412. package/clis/gov-policy/commands.test.js +0 -27
  413. package/clis/linux-do/category.js +0 -37
  414. package/clis/linux-do/hot.js +0 -26
  415. package/clis/linux-do/latest.js +0 -19
  416. package/clis/pixiv/test-utils.js +0 -23
  417. package/clis/toutiao/articles.test.js +0 -30
  418. package/dist/src/analysis.d.ts +0 -40
  419. package/dist/src/analysis.js +0 -172
package/dist/src/cli.js CHANGED
@@ -8,7 +8,7 @@ import * as fs from 'node:fs';
8
8
  import * as os from 'node:os';
9
9
  import * as path from 'node:path';
10
10
  import { fileURLToPath } from 'node:url';
11
- import { Command } from 'commander';
11
+ import { Command, InvalidArgumentError } from 'commander';
12
12
  import { styleText } from 'node:util';
13
13
  import { findPackageRoot, getBuiltEntryCandidates } from './package-paths.js';
14
14
  import { fullName, getRegistry, strategyLabel } from './registry.js';
@@ -18,7 +18,7 @@ import { PKG_VERSION } from './version.js';
18
18
  import { printCompletionScript } from './completion.js';
19
19
  import { loadExternalClis, executeExternalCli, installExternalCli, registerExternalCli, isBinaryInstalled } from './external.js';
20
20
  import { registerAllCommands } from './commanderAdapter.js';
21
- import { formatRootAdapterHelpText, installStructuredHelp, rootHelpData } from './help.js';
21
+ import { classifyAdapter, formatRootAdapterHelpText, installStructuredHelp, rootHelpData } from './help.js';
22
22
  import { EXIT_CODES, getErrorMessage, BrowserConnectError } from './errors.js';
23
23
  import { TargetError } from './browser/target-errors.js';
24
24
  import { resolveTargetJs, getTextResolvedJs, getValueResolvedJs, getAttributesResolvedJs, selectResolvedJs, isAutocompleteResolvedJs } from './browser/target-resolver.js';
@@ -316,7 +316,9 @@ async function resolveStoredBrowserTarget(page, scope = DEFAULT_BROWSER_WORKSPAC
316
316
  async function getBrowserPage(targetPage, workspace = DEFAULT_BROWSER_WORKSPACE, contextId) {
317
317
  const { BrowserBridge } = await import('./browser/index.js');
318
318
  const bridge = new BrowserBridge();
319
- const envTimeout = process.env.OPENCLI_BROWSER_TIMEOUT;
319
+ // Idle timeout: how long the browser workspace lease stays alive between commands
320
+ // (controls when the automation tab is released). Not the per-command runtime timeout.
321
+ const envTimeout = process.env.OPENCLI_BROWSER_IDLE_TIMEOUT;
320
322
  const idleTimeout = envTimeout ? parseInt(envTimeout, 10) : undefined;
321
323
  const page = await bridge.connect({
322
324
  timeout: 30,
@@ -389,6 +391,16 @@ function parsePositiveIntOption(val, label, fallback) {
389
391
  }
390
392
  return parsed;
391
393
  }
394
+ function parseScreenshotDim(val, label) {
395
+ if (!/^\d+$/.test(val)) {
396
+ throw new InvalidArgumentError(`--${label} must be a positive integer (got "${val}")`);
397
+ }
398
+ const parsed = parseInt(val, 10);
399
+ if (parsed <= 0) {
400
+ throw new InvalidArgumentError(`--${label} must be a positive integer (got "${val}")`);
401
+ }
402
+ return parsed;
403
+ }
392
404
  function applyVerbose(opts) {
393
405
  if (opts.verbose)
394
406
  process.env.OPENCLI_VERBOSE = '1';
@@ -888,14 +900,22 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
888
900
  console.log(JSON.stringify(frames, null, 2));
889
901
  }));
890
902
  addBrowserTabOption(browser.command('screenshot').argument('[path]', 'Save to file (base64 if omitted)'))
903
+ .option('--full-page', 'Capture the full scrollable page, not just the viewport', false)
904
+ .option('--width <n>', 'Override viewport width in CSS pixels for this screenshot only', (v) => parseScreenshotDim(v, 'width'))
905
+ .option('--height <n>', 'Override viewport height in CSS pixels for this screenshot only (ignored with --full-page)', (v) => parseScreenshotDim(v, 'height'))
891
906
  .description('Take screenshot')
892
- .action(browserAction(async (page, path) => {
907
+ .action(browserAction(async (page, path, opts) => {
908
+ const shotOpts = {
909
+ fullPage: opts.fullPage === true,
910
+ width: opts.width,
911
+ height: opts.height,
912
+ };
893
913
  if (path) {
894
- await page.screenshot({ path });
914
+ await page.screenshot({ ...shotOpts, path });
895
915
  console.log(`Screenshot saved to: ${path}`);
896
916
  }
897
917
  else {
898
- console.log(await page.screenshot({ format: 'png' }));
918
+ console.log(await page.screenshot({ ...shotOpts, format: 'png' }));
899
919
  }
900
920
  }));
901
921
  addBrowserTabOption(browser.command('console'))
@@ -1313,6 +1333,33 @@ export function createProgram(BUILTIN_CLIS, USER_CLIS) {
1313
1333
  autocomplete: !!isAutocomplete,
1314
1334
  }, null, 2));
1315
1335
  }));
1336
+ addBrowserTabOption(browser.command('fill')
1337
+ .argument('<target>', 'Numeric ref (from browser state / find) or CSS selector')
1338
+ .argument('<text>', 'Text to set exactly')
1339
+ .option('--nth <n>', 'When <target> is a multi-match CSS selector, pick the nth match (0-based)')
1340
+ .description('Set input/textarea/contenteditable text exactly and verify the value — JSON envelope {filled, verified, text, actual}'))
1341
+ .action(browserAction(async (page, target, text, opts) => {
1342
+ const parsed = nthToResolveOpts(opts?.nth);
1343
+ if ('error' in parsed) {
1344
+ console.log(JSON.stringify({ error: { code: 'usage_error', message: parsed.error } }, null, 2));
1345
+ process.exitCode = EXIT_CODES.USAGE_ERROR;
1346
+ return;
1347
+ }
1348
+ const result = await page.fillText(String(target), String(text), parsed.opts);
1349
+ if (!result.verified)
1350
+ process.exitCode = EXIT_CODES.GENERIC_ERROR;
1351
+ console.log(JSON.stringify({
1352
+ filled: result.filled,
1353
+ verified: result.verified,
1354
+ target: String(target),
1355
+ text: String(text),
1356
+ actual: result.actual,
1357
+ length: result.length,
1358
+ matches_n: result.matches_n,
1359
+ match_level: result.match_level,
1360
+ ...(result.mode ? { mode: result.mode } : {}),
1361
+ }, null, 2));
1362
+ }));
1316
1363
  addBrowserTabOption(browser.command('select')
1317
1364
  .argument('<target>', 'Numeric ref (from browser state / find) or CSS selector of a <select> element')
1318
1365
  .argument('<option>', 'Option text (or value) to select')
@@ -2036,10 +2083,10 @@ cli({
2036
2083
  }
2037
2084
  });
2038
2085
  // ── Session ──
2039
- browser.command('close').description('Close the automation window')
2086
+ browser.command('close').description('Release the current automation tab lease')
2040
2087
  .action(browserAction(async (page) => {
2041
2088
  await page.closeWindow?.();
2042
- console.log('Automation window closed');
2089
+ console.log('Automation tab lease released');
2043
2090
  }));
2044
2091
  // ── Built-in: doctor / completion ──────────────────────────────────────────
2045
2092
  program
@@ -2539,11 +2586,29 @@ cli({
2539
2586
  siteGroups.set('antigravity', antigravityCmd);
2540
2587
  const siteNames = registerAllCommands(program, siteGroups);
2541
2588
  applyRootSubcommandSummaries(program);
2542
- const siteNameSet = new Set(siteNames);
2589
+ // ── Help-text grouping: External CLIs / App adapters / Site adapters ──
2590
+ // Classification derives from each adapter's `domain` field — see classifyAdapter.
2591
+ // External CLIs are taken from the externalClis registry (passthrough binaries).
2592
+ const externalNames = externalClis.map(ext => ext.name);
2593
+ const siteDomains = new Map();
2594
+ for (const [, cmd] of getRegistry()) {
2595
+ if (!siteDomains.has(cmd.site))
2596
+ siteDomains.set(cmd.site, cmd.domain);
2597
+ }
2598
+ const apps = [];
2599
+ const sites = [];
2600
+ for (const site of siteNames) {
2601
+ if (classifyAdapter(siteDomains.get(site)) === 'app')
2602
+ apps.push(site);
2603
+ else
2604
+ sites.push(site);
2605
+ }
2606
+ const adapterGroups = { external: externalNames, apps, sites };
2607
+ const adapterNameSet = new Set([...externalNames, ...siteNames]);
2543
2608
  program.configureHelp({
2544
- visibleCommands: (command) => command.commands.filter(child => command !== program || !siteNameSet.has(child.name())),
2609
+ visibleCommands: (command) => command.commands.filter(child => command !== program || !adapterNameSet.has(child.name())),
2545
2610
  });
2546
- installStructuredHelp(program, () => rootHelpData(program, siteNames), () => formatRootAdapterHelpText(siteNames));
2611
+ installStructuredHelp(program, () => rootHelpData(program, adapterGroups), () => formatRootAdapterHelpText(adapterGroups));
2547
2612
  // ── Unknown command fallback ──────────────────────────────────────────────
2548
2613
  // Security: do NOT auto-discover and register arbitrary system binaries.
2549
2614
  // Only explicitly registered external CLIs are allowed.
@@ -96,6 +96,90 @@ describe('createProgram root help descriptions', () => {
96
96
  registry.set(key, value);
97
97
  }
98
98
  });
99
+ it('groups adapters into App / Site buckets by domain field', () => {
100
+ const registry = getRegistry();
101
+ const snapshot = new Map(registry);
102
+ registry.clear();
103
+ try {
104
+ cli({
105
+ site: 'bilibili',
106
+ name: 'hot',
107
+ access: 'read',
108
+ description: 'Bilibili hot videos',
109
+ domain: 'www.bilibili.com',
110
+ strategy: Strategy.PUBLIC,
111
+ browser: false,
112
+ });
113
+ cli({
114
+ site: 'chatwise',
115
+ name: 'ask',
116
+ access: 'write',
117
+ description: 'Ask Chatwise desktop app',
118
+ domain: 'localhost',
119
+ strategy: Strategy.UI,
120
+ browser: true,
121
+ });
122
+ const program = createProgram('', '');
123
+ const help = program.helpInformation();
124
+ // Two separate sections, each with own count
125
+ expect(help).toContain('App adapters (1):');
126
+ expect(help).toMatch(/App adapters \(1\):\n {2}chatwise/);
127
+ expect(help).toContain('Site adapters (1):');
128
+ expect(help).toMatch(/Site adapters \(1\):\n {2}bilibili/);
129
+ // App adapters appear before Site adapters (External CLIs are absent here)
130
+ expect(help.indexOf('App adapters')).toBeLessThan(help.indexOf('Site adapters'));
131
+ }
132
+ finally {
133
+ registry.clear();
134
+ for (const [key, value] of snapshot)
135
+ registry.set(key, value);
136
+ }
137
+ });
138
+ it('exposes external_clis / app_adapters / site_adapters in structured help', () => {
139
+ const registry = getRegistry();
140
+ const snapshot = new Map(registry);
141
+ const argv = process.argv;
142
+ registry.clear();
143
+ try {
144
+ cli({
145
+ site: 'bilibili',
146
+ name: 'hot',
147
+ access: 'read',
148
+ description: 'Bilibili hot videos',
149
+ domain: 'www.bilibili.com',
150
+ strategy: Strategy.PUBLIC,
151
+ browser: false,
152
+ });
153
+ cli({
154
+ site: 'chatwise',
155
+ name: 'ask',
156
+ access: 'write',
157
+ description: 'Ask Chatwise desktop app',
158
+ domain: 'localhost',
159
+ strategy: Strategy.UI,
160
+ browser: true,
161
+ });
162
+ const program = createProgram('', '');
163
+ process.argv = ['node', 'opencli', '--help', '-f', 'yaml'];
164
+ const data = yaml.load(program.helpInformation());
165
+ expect(data.app_adapters.count).toBe(1);
166
+ expect(data.app_adapters.apps).toEqual(['chatwise']);
167
+ expect(data.site_adapters.count).toBe(1);
168
+ expect(data.site_adapters.sites).toEqual(['bilibili']);
169
+ expect(data.external_clis.count).toBeGreaterThanOrEqual(0);
170
+ expect(Array.isArray(data.external_clis.clis)).toBe(true);
171
+ // Adapters must NOT leak into the core commands list
172
+ const commandNames = data.commands.map((cmd) => cmd.name);
173
+ expect(commandNames).not.toContain('bilibili');
174
+ expect(commandNames).not.toContain('chatwise');
175
+ }
176
+ finally {
177
+ process.argv = argv;
178
+ registry.clear();
179
+ for (const [key, value] of snapshot)
180
+ registry.set(key, value);
181
+ }
182
+ });
99
183
  it('renders root structured help with built-ins and site adapter names', () => {
100
184
  const registry = getRegistry();
101
185
  const snapshot = new Map(registry);
@@ -139,6 +223,7 @@ describe('createProgram root help descriptions', () => {
139
223
  strategy: Strategy.PUBLIC,
140
224
  browser: false,
141
225
  args: [{ name: 'limit', type: 'int', default: 20, help: 'Number of videos' }],
226
+ columns: ['title', 'url'],
142
227
  });
143
228
  const program = createProgram('', '');
144
229
  const site = program.commands.find(cmd => cmd.name() === 'bilibili');
@@ -151,10 +236,99 @@ describe('createProgram root help descriptions', () => {
151
236
  name: 'hot',
152
237
  access: 'read',
153
238
  description: 'Bilibili hot videos',
239
+ browser: false,
154
240
  example: 'opencli bilibili hot -f yaml',
155
- args: [{ name: 'limit', type: 'int', default: 20 }],
241
+ command_options: [{ name: 'limit', type: 'int', default: 20 }],
242
+ columns: ['title', 'url'],
156
243
  },
157
244
  ]);
245
+ expect(data.commands[0]).not.toHaveProperty('args');
246
+ }
247
+ finally {
248
+ process.argv = argv;
249
+ registry.clear();
250
+ for (const [key, value] of snapshot)
251
+ registry.set(key, value);
252
+ }
253
+ });
254
+ it('renders per-site text help without per-command common option noise', () => {
255
+ const registry = getRegistry();
256
+ const snapshot = new Map(registry);
257
+ registry.clear();
258
+ try {
259
+ cli({
260
+ site: 'bilibili',
261
+ name: 'hot',
262
+ access: 'read',
263
+ description: 'Bilibili hot videos',
264
+ strategy: Strategy.PUBLIC,
265
+ browser: false,
266
+ args: [{ name: 'limit', type: 'int', default: 20, help: 'Number of videos' }],
267
+ });
268
+ cli({
269
+ site: 'bilibili',
270
+ name: 'video',
271
+ access: 'read',
272
+ description: 'Read one video',
273
+ domain: 'www.bilibili.com',
274
+ strategy: Strategy.PUBLIC,
275
+ browser: true,
276
+ args: [{ name: 'bvid', positional: true, required: true, help: 'Video id' }],
277
+ });
278
+ const program = createProgram('', '');
279
+ const site = program.commands.find(cmd => cmd.name() === 'bilibili');
280
+ expect(site).toBeTruthy();
281
+ const help = site.helpInformation();
282
+ expect(help).toContain('hot [options] [read] Bilibili hot videos');
283
+ expect(help).toContain('video <bvid> [read] Read one video');
284
+ expect(help).toContain('hot [options]');
285
+ expect(help).not.toContain('video <bvid> [options]');
286
+ expect(help).not.toContain('\nOptions:');
287
+ expect(help).toContain('Common options:');
288
+ expect(help).toContain('-f, --format <fmt>');
289
+ expect(help).toContain('--trace <mode>');
290
+ expect(help).toContain('get all command args/options in one structured response');
291
+ }
292
+ finally {
293
+ registry.clear();
294
+ for (const [key, value] of snapshot)
295
+ registry.set(key, value);
296
+ }
297
+ });
298
+ it('separates command args from common options in structured help', () => {
299
+ const registry = getRegistry();
300
+ const snapshot = new Map(registry);
301
+ const argv = process.argv;
302
+ registry.clear();
303
+ try {
304
+ cli({
305
+ site: 'bilibili',
306
+ name: 'video',
307
+ access: 'read',
308
+ description: 'Read one video',
309
+ strategy: Strategy.PUBLIC,
310
+ domain: 'www.bilibili.com',
311
+ browser: true,
312
+ args: [
313
+ { name: 'bvid', positional: true, required: true, help: 'Video id' },
314
+ { name: 'with-comments', type: 'boolean', default: false, help: 'Include comments' },
315
+ ],
316
+ columns: ['title', 'url'],
317
+ });
318
+ const program = createProgram('', '');
319
+ const site = program.commands.find(cmd => cmd.name() === 'bilibili');
320
+ const command = site.commands.find(cmd => cmd.name() === 'video');
321
+ expect(command).toBeTruthy();
322
+ process.argv = ['node', 'opencli', 'bilibili', 'video', '--help', '-f', 'yaml'];
323
+ const data = yaml.load(command.helpInformation());
324
+ expect(data.usage).toBe('opencli bilibili video <bvid> [options]');
325
+ expect(data.browser).toBe(true);
326
+ expect(data.domain).toBe('www.bilibili.com');
327
+ expect(data.positionals).toMatchObject([{ name: 'bvid', positional: true, required: true }]);
328
+ expect(data.command_options).toMatchObject([{ name: 'with-comments', default: false }]);
329
+ expect(data.common_options.map((option) => option.name)).toEqual(['format', 'trace', 'verbose', 'help']);
330
+ expect(data.columns).toEqual(['title', 'url']);
331
+ expect(data).not.toHaveProperty('args');
158
332
  }
159
333
  finally {
160
334
  process.argv = argv;
@@ -1624,6 +1798,16 @@ describe('browser click/type commands', () => {
1624
1798
  evaluate: vi.fn().mockResolvedValue(false),
1625
1799
  click: vi.fn().mockResolvedValue({ matches_n: 1, match_level: 'exact' }),
1626
1800
  typeText: vi.fn().mockResolvedValue({ matches_n: 1, match_level: 'exact' }),
1801
+ fillText: vi.fn().mockResolvedValue({
1802
+ filled: true,
1803
+ verified: true,
1804
+ expected: '',
1805
+ actual: '',
1806
+ length: 0,
1807
+ matches_n: 1,
1808
+ match_level: 'exact',
1809
+ mode: 'input',
1810
+ }),
1627
1811
  wait: vi.fn().mockResolvedValue(undefined),
1628
1812
  }));
1629
1813
  it('emits {clicked, target, matches_n, match_level} on success', async () => {
@@ -1721,6 +1905,62 @@ describe('browser click/type commands', () => {
1721
1905
  expect(browserState.page.click).toHaveBeenCalledWith('.field', { nth: 3 });
1722
1906
  expect(browserState.page.typeText).toHaveBeenCalledWith('.field', 'x', { nth: 3 });
1723
1907
  });
1908
+ it('fill: delegates exact raw text to page.fillText and emits verification details', async () => {
1909
+ browserState.page.fillText.mockResolvedValueOnce({
1910
+ filled: true,
1911
+ verified: true,
1912
+ expected: 'line1\\n/ / raw',
1913
+ actual: 'line1\\n/ / raw',
1914
+ length: 14,
1915
+ matches_n: 1,
1916
+ match_level: 'exact',
1917
+ mode: 'textarea',
1918
+ });
1919
+ const program = createProgram('', '');
1920
+ await program.parseAsync(['node', 'opencli', 'browser', 'fill', '#msg', 'line1\\n/ / raw']);
1921
+ expect(browserState.page.fillText).toHaveBeenCalledWith('#msg', 'line1\\n/ / raw', {});
1922
+ expect(lastJsonLog()).toEqual({
1923
+ filled: true,
1924
+ verified: true,
1925
+ target: '#msg',
1926
+ text: 'line1\\n/ / raw',
1927
+ actual: 'line1\\n/ / raw',
1928
+ length: 14,
1929
+ matches_n: 1,
1930
+ match_level: 'exact',
1931
+ mode: 'textarea',
1932
+ });
1933
+ expect(process.exitCode).toBeUndefined();
1934
+ });
1935
+ it('fill: sets a non-zero exit code when verification fails', async () => {
1936
+ browserState.page.fillText.mockResolvedValueOnce({
1937
+ filled: true,
1938
+ verified: false,
1939
+ expected: 'expected',
1940
+ actual: 'actual',
1941
+ length: 6,
1942
+ matches_n: 1,
1943
+ match_level: 'exact',
1944
+ });
1945
+ const program = createProgram('', '');
1946
+ await program.parseAsync(['node', 'opencli', 'browser', 'fill', '#msg', 'expected']);
1947
+ expect(lastJsonLog()).toEqual({
1948
+ filled: true,
1949
+ verified: false,
1950
+ target: '#msg',
1951
+ text: 'expected',
1952
+ actual: 'actual',
1953
+ length: 6,
1954
+ matches_n: 1,
1955
+ match_level: 'exact',
1956
+ });
1957
+ expect(process.exitCode).toBeDefined();
1958
+ });
1959
+ it('fill: forwards --nth to page.fillText', async () => {
1960
+ const program = createProgram('', '');
1961
+ await program.parseAsync(['node', 'opencli', 'browser', 'fill', '.field', 'x', '--nth', '2']);
1962
+ expect(browserState.page.fillText).toHaveBeenCalledWith('.field', 'x', { nth: 2 });
1963
+ });
1724
1964
  });
1725
1965
  describe('browser select command', () => {
1726
1966
  const { lastJsonLog } = installSelectorFirstTestHarness('select', () => ({
@@ -12,10 +12,9 @@
12
12
  import { log } from './logger.js';
13
13
  import yaml from 'js-yaml';
14
14
  import { fullName, getRegistry } from './registry.js';
15
- import { formatRegistryHelpText } from './serialization.js';
16
15
  import { render as renderOutput } from './output.js';
17
16
  import { executeCommand, prepareCommandArgs } from './execution.js';
18
- import { commandHelpData, formatSiteCommandDescription, installStructuredHelp, siteHelpData, } from './help.js';
17
+ import { commandHelpData, formatCommandHelpText, formatCommandListTerm, formatSiteCommandDescription, formatSiteHelpText, getRequestedHelpFormat, renderStructuredHelp, siteHelpData, } from './help.js';
19
18
  import { CliError, EXIT_CODES, toEnvelope, } from './errors.js';
20
19
  /**
21
20
  * Register a single CliCommand as a Commander subcommand.
@@ -49,7 +48,16 @@ export function registerCommandToProgram(siteCmd, cmd) {
49
48
  .option('-f, --format <fmt>', 'Output format: table, plain, json, yaml, md, csv', 'table')
50
49
  .option('--trace <mode>', 'Trace capture: off, on, retain-on-failure', 'off')
51
50
  .option('-v, --verbose', 'Debug output', false);
52
- installStructuredHelp(subCmd, () => commandHelpData(cmd), () => formatRegistryHelpText(cmd));
51
+ const originalHelpInformation = subCmd.helpInformation.bind(subCmd);
52
+ subCmd.helpInformation = ((contextOptions) => {
53
+ const format = getRequestedHelpFormat();
54
+ if (format)
55
+ return renderStructuredHelp(commandHelpData(cmd), format);
56
+ // Keep a fallback reference so future Commander upgrades still initialize
57
+ // internal help state before we render the cleaner grouped command help.
58
+ void originalHelpInformation(contextOptions);
59
+ return formatCommandHelpText(cmd);
60
+ });
53
61
  subCmd.action(async (...actionArgs) => {
54
62
  const actionOpts = actionArgs[positionalArgs.length] ?? {};
55
63
  const optionsRecord = typeof actionOpts === 'object' && actionOpts !== null ? actionOpts : {};
@@ -89,11 +97,6 @@ export function registerCommandToProgram(siteCmd, cmd) {
89
97
  const formatExplicit = subCmd.getOptionValueSource('format') === 'cli';
90
98
  if (verbose)
91
99
  process.env.OPENCLI_VERBOSE = '1';
92
- if (cmd.deprecated) {
93
- const message = typeof cmd.deprecated === 'string' ? cmd.deprecated : `${fullName(cmd)} is deprecated.`;
94
- const replacement = cmd.replacedBy ? ` Use ${cmd.replacedBy} instead.` : '';
95
- log.warn(`Deprecated: ${message}${replacement}`);
96
- }
97
100
  const globals = typeof subCmd.optsWithGlobals === 'function' ? subCmd.optsWithGlobals() : {};
98
101
  const result = await executeCommand(cmd, kwargs, verbose, {
99
102
  prepared: true,
@@ -179,7 +182,18 @@ export function registerAllCommands(program, siteGroups) {
179
182
  for (const cmd of commands) {
180
183
  registerCommandToProgram(siteCmd, cmd);
181
184
  }
182
- installStructuredHelp(siteCmd, () => siteHelpData(site, commands));
185
+ const commandTerms = new Map(commands.map(cmd => [cmd.name, formatCommandListTerm(cmd)]));
186
+ siteCmd.configureHelp({
187
+ subcommandTerm: command => commandTerms.get(command.name()) ?? command.name(),
188
+ });
189
+ const originalSiteHelpInformation = siteCmd.helpInformation.bind(siteCmd);
190
+ siteCmd.helpInformation = ((contextOptions) => {
191
+ const format = getRequestedHelpFormat();
192
+ if (format)
193
+ return renderStructuredHelp(siteHelpData(site, commands), format);
194
+ void originalSiteHelpInformation(contextOptions);
195
+ return formatSiteHelpText(site, commands);
196
+ });
183
197
  }
184
198
  return [...commandsBySite.keys()].sort((a, b) => a.localeCompare(b));
185
199
  }
@@ -279,7 +279,6 @@ describe('commanderAdapter error envelope output', () => {
279
279
  expect(output).toContain('xsec_token');
280
280
  expect(output).toContain('--trace=retain-on-failure');
281
281
  expect(output).toContain('opencli xiaohongshu note --trace retain-on-failure');
282
- expect(output).not.toContain('OPENCLI_DIAGNOSTIC');
283
282
  stderrSpy.mockRestore();
284
283
  });
285
284
  it('outputs YAML error envelope for selector errors', async () => {
@@ -131,12 +131,11 @@ async function loadFromManifest(manifestPath, clisDir) {
131
131
  browser: entry.browser,
132
132
  args: entry.args ?? [],
133
133
  columns: entry.columns,
134
+ defaultFormat: entry.defaultFormat,
134
135
  pipeline: entry.pipeline,
135
- timeoutSeconds: entry.timeout,
136
136
  source: entry.sourceFile ? path.resolve(clisDir, entry.sourceFile) : modulePath,
137
- deprecated: entry.deprecated,
138
- replacedBy: entry.replacedBy,
139
137
  navigateBefore: entry.navigateBefore,
138
+ browserSession: entry.browserSession,
140
139
  _lazy: true,
141
140
  _modulePath: modulePath,
142
141
  };
@@ -170,7 +169,6 @@ async function discoverClisFromFs(dir) {
170
169
  await Promise.all(files.map(async (file) => {
171
170
  const filePath = path.join(siteDir, file);
172
171
  if (file.endsWith('.yaml') || file.endsWith('.yml')) {
173
- log.warn(`Ignoring YAML adapter ${filePath} — YAML format is no longer supported. Convert to JavaScript using cli() from '@jackwener/opencli/registry'.`);
174
172
  return;
175
173
  }
176
174
  if (file.endsWith('.ts') && !file.endsWith('.d.ts') && !file.endsWith('.test.ts')) {
@@ -218,7 +216,6 @@ async function discoverPluginDir(dir, site) {
218
216
  await Promise.all(files.map(async (file) => {
219
217
  const filePath = path.join(dir, file);
220
218
  if (file.endsWith('.yaml') || file.endsWith('.yml')) {
221
- log.warn(`Ignoring YAML plugin ${filePath} — YAML format is no longer supported. Convert to JavaScript using cli() from '@jackwener/opencli/registry'.`);
222
219
  return;
223
220
  }
224
221
  if (file.endsWith('.js') && !file.endsWith('.d.js')) {
@@ -73,7 +73,7 @@ export class AuthRequiredError extends CliError {
73
73
  }
74
74
  export class TimeoutError extends CliError {
75
75
  constructor(label, seconds, hint) {
76
- super('TIMEOUT', `${label} timed out after ${seconds}s`, hint ?? 'Try again, or increase timeout with OPENCLI_BROWSER_COMMAND_TIMEOUT env var', EXIT_CODES.TEMPFAIL);
76
+ super('TIMEOUT', `${label} timed out after ${seconds}s`, hint ?? 'Try again, or increase timeout with --timeout <seconds> (or OPENCLI_BROWSER_COMMAND_TIMEOUT for the global default)', EXIT_CODES.TEMPFAIL);
77
77
  }
78
78
  }
79
79
  export class ArgumentError extends CliError {
@@ -4,7 +4,7 @@
4
4
  * This is the single entry point for executing any CLI command. It handles:
5
5
  * 1. Argument validation and coercion
6
6
  * 2. Browser session lifecycle (if needed)
7
- * 3. Domain pre-navigation for cookie/header strategies
7
+ * 3. Domain pre-navigation for cookie strategies
8
8
  * 4. Timeout enforcement
9
9
  * 5. Lazy-loading of TS modules from manifest
10
10
  * 6. Lifecycle hooks (onBeforeExecute / onAfterExecute)