@jackwener/opencli 1.7.11 → 1.7.13

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 (1004) hide show
  1. package/README.md +8 -7
  2. package/README.zh-CN.md +11 -9
  3. package/cli-manifest.json +12952 -6208
  4. package/clis/1688/assets.js +1 -0
  5. package/clis/1688/download.js +1 -0
  6. package/clis/1688/item.js +1 -0
  7. package/clis/1688/search.js +2 -1
  8. package/clis/1688/store.js +1 -0
  9. package/clis/1point3acres/digest.js +35 -0
  10. package/clis/1point3acres/forum.js +51 -0
  11. package/clis/1point3acres/forums.js +44 -0
  12. package/clis/1point3acres/hot.js +35 -0
  13. package/clis/1point3acres/latest.js +35 -0
  14. package/clis/1point3acres/notifications.js +64 -0
  15. package/clis/1point3acres/search.js +71 -0
  16. package/clis/1point3acres/thread.js +117 -0
  17. package/clis/1point3acres/user.js +77 -0
  18. package/clis/1point3acres/utils.js +247 -0
  19. package/clis/36kr/article.js +1 -0
  20. package/clis/36kr/hot.js +1 -0
  21. package/clis/36kr/news.js +1 -0
  22. package/clis/36kr/search.js +1 -0
  23. package/clis/51job/company.js +1 -0
  24. package/clis/51job/detail.js +1 -0
  25. package/clis/51job/hot.js +1 -0
  26. package/clis/51job/search.js +1 -0
  27. package/clis/_shared/desktop-commands.js +4 -0
  28. package/clis/aibase/news.js +110 -0
  29. package/clis/aibase/news.test.js +59 -0
  30. package/clis/amazon/bestsellers.js +1 -0
  31. package/clis/amazon/discussion.js +1 -0
  32. package/clis/amazon/discussion.test.js +1 -28
  33. package/clis/amazon/movers-shakers.js +1 -0
  34. package/clis/amazon/new-releases.js +1 -0
  35. package/clis/amazon/offer.js +1 -0
  36. package/clis/amazon/product.js +1 -0
  37. package/clis/amazon/rankings.js +1 -0
  38. package/clis/amazon/search.js +1 -0
  39. package/clis/antigravity/dump.js +1 -0
  40. package/clis/antigravity/extract-code.js +1 -0
  41. package/clis/antigravity/model.js +1 -0
  42. package/clis/antigravity/new.js +1 -0
  43. package/clis/antigravity/read.js +1 -0
  44. package/clis/antigravity/send.js +1 -0
  45. package/clis/antigravity/status.js +1 -0
  46. package/clis/antigravity/watch.js +4 -2
  47. package/clis/apple-podcasts/episodes.js +1 -0
  48. package/clis/apple-podcasts/search.js +1 -0
  49. package/clis/apple-podcasts/top.js +1 -0
  50. package/clis/arxiv/arxiv.test.js +112 -0
  51. package/clis/arxiv/author.js +44 -0
  52. package/clis/arxiv/paper.js +4 -3
  53. package/clis/arxiv/recent.js +33 -0
  54. package/clis/arxiv/search.js +19 -7
  55. package/clis/arxiv/utils.js +68 -5
  56. package/clis/baidu-scholar/search.js +1 -1
  57. package/clis/band/bands.js +1 -0
  58. package/clis/band/mentions.js +1 -0
  59. package/clis/band/post.js +1 -0
  60. package/clis/band/posts.js +1 -0
  61. package/clis/barchart/flow.js +1 -0
  62. package/clis/barchart/greeks.js +1 -0
  63. package/clis/barchart/options.js +1 -0
  64. package/clis/barchart/quote.js +1 -0
  65. package/clis/bbc/news.js +1 -0
  66. package/clis/bbc/topic.js +57 -0
  67. package/clis/bbc/utils.js +79 -0
  68. package/clis/bilibili/comments.js +1 -0
  69. package/clis/bilibili/download.js +1 -0
  70. package/clis/bilibili/dynamic.js +1 -0
  71. package/clis/bilibili/favorite.js +1 -0
  72. package/clis/bilibili/feed.js +2 -0
  73. package/clis/bilibili/following.js +1 -0
  74. package/clis/bilibili/history.js +1 -0
  75. package/clis/bilibili/hot.js +6 -1
  76. package/clis/bilibili/hot.test.js +17 -0
  77. package/clis/bilibili/me.js +1 -1
  78. package/clis/bilibili/ranking.js +1 -0
  79. package/clis/bilibili/search.js +1 -1
  80. package/clis/bilibili/subtitle.js +1 -0
  81. package/clis/bilibili/user-videos.js +1 -0
  82. package/clis/bilibili/video.js +1 -0
  83. package/clis/binance/asks.js +1 -0
  84. package/clis/binance/depth.js +1 -0
  85. package/clis/binance/gainers.js +1 -0
  86. package/clis/binance/klines.js +1 -0
  87. package/clis/binance/losers.js +1 -0
  88. package/clis/binance/pairs.js +1 -0
  89. package/clis/binance/price.js +1 -0
  90. package/clis/binance/prices.js +1 -0
  91. package/clis/binance/ticker.js +1 -0
  92. package/clis/binance/top.js +1 -0
  93. package/clis/binance/trades.js +1 -0
  94. package/clis/bloomberg/businessweek.js +1 -0
  95. package/clis/bloomberg/economics.js +1 -0
  96. package/clis/bloomberg/feeds.js +1 -0
  97. package/clis/bloomberg/industries.js +1 -0
  98. package/clis/bloomberg/main.js +1 -0
  99. package/clis/bloomberg/markets.js +1 -0
  100. package/clis/bloomberg/news.js +1 -0
  101. package/clis/bloomberg/opinions.js +1 -0
  102. package/clis/bloomberg/politics.js +1 -0
  103. package/clis/bloomberg/tech.js +1 -0
  104. package/clis/bluesky/feeds.js +1 -0
  105. package/clis/bluesky/followers.js +1 -0
  106. package/clis/bluesky/following.js +1 -0
  107. package/clis/bluesky/profile.js +1 -0
  108. package/clis/bluesky/search.js +1 -0
  109. package/clis/bluesky/starter-packs.js +1 -0
  110. package/clis/bluesky/thread.js +1 -0
  111. package/clis/bluesky/trending.js +1 -0
  112. package/clis/bluesky/user.js +3 -1
  113. package/clis/boss/batchgreet.js +1 -0
  114. package/clis/boss/chatlist.js +1 -0
  115. package/clis/boss/chatmsg.js +1 -0
  116. package/clis/boss/detail.js +1 -0
  117. package/clis/boss/exchange.js +1 -0
  118. package/clis/boss/greet.js +1 -0
  119. package/clis/boss/invite.js +1 -0
  120. package/clis/boss/joblist.js +1 -0
  121. package/clis/boss/mark.js +1 -0
  122. package/clis/boss/recommend.js +1 -0
  123. package/clis/boss/resume.js +1 -0
  124. package/clis/boss/search.js +1 -0
  125. package/clis/boss/send.js +1 -0
  126. package/clis/boss/stats.js +1 -0
  127. package/clis/chaoxing/assignments.js +2 -1
  128. package/clis/chaoxing/exams.js +2 -1
  129. package/clis/chatgpt/ask.js +57 -0
  130. package/clis/chatgpt/commands.test.js +45 -0
  131. package/clis/chatgpt/detail.js +46 -0
  132. package/clis/chatgpt/history.js +39 -0
  133. package/clis/chatgpt/image.js +13 -11
  134. package/clis/chatgpt/image.test.js +23 -0
  135. package/clis/chatgpt/new.js +25 -0
  136. package/clis/chatgpt/read.js +43 -0
  137. package/clis/chatgpt/send.js +46 -0
  138. package/clis/chatgpt/status.js +29 -0
  139. package/clis/chatgpt/utils.js +294 -4
  140. package/clis/chatgpt/utils.test.js +13 -0
  141. package/clis/chatgpt-app/ask.js +7 -3
  142. package/clis/chatgpt-app/model.js +1 -0
  143. package/clis/chatgpt-app/new.js +1 -0
  144. package/clis/chatgpt-app/read.js +1 -0
  145. package/clis/chatgpt-app/send.js +1 -0
  146. package/clis/chatgpt-app/status.js +1 -0
  147. package/clis/chatwise/ask.js +17 -43
  148. package/clis/chatwise/composer.test.js +186 -0
  149. package/clis/chatwise/export.js +1 -0
  150. package/clis/chatwise/history.js +1 -0
  151. package/clis/chatwise/model.js +1 -0
  152. package/clis/chatwise/read.js +1 -0
  153. package/clis/chatwise/send.js +3 -24
  154. package/clis/chatwise/utils.js +143 -0
  155. package/clis/claude/ask.js +2 -1
  156. package/clis/claude/detail.js +2 -0
  157. package/clis/claude/history.js +2 -0
  158. package/clis/claude/new.js +2 -0
  159. package/clis/claude/read.js +2 -0
  160. package/clis/claude/send.js +2 -0
  161. package/clis/claude/status.js +2 -0
  162. package/clis/cnki/search.js +1 -0
  163. package/clis/codex/ask.js +16 -9
  164. package/clis/codex/export.js +1 -0
  165. package/clis/codex/extract-diff.js +1 -0
  166. package/clis/codex/history.js +17 -33
  167. package/clis/codex/model.js +1 -0
  168. package/clis/codex/projects.js +28 -0
  169. package/clis/codex/read.js +11 -4
  170. package/clis/codex/send.js +11 -3
  171. package/clis/codex/sidebar.js +356 -0
  172. package/clis/codex/sidebar.test.js +329 -0
  173. package/clis/coingecko/categories.js +75 -0
  174. package/clis/coingecko/coin.js +107 -0
  175. package/clis/coingecko/coingecko.test.js +109 -0
  176. package/clis/coingecko/derivatives.js +84 -0
  177. package/clis/coingecko/exchanges.js +74 -0
  178. package/clis/coingecko/global.js +71 -0
  179. package/clis/coingecko/top.js +64 -0
  180. package/clis/coingecko/trending.js +55 -0
  181. package/clis/coupang/add-to-cart.js +22 -13
  182. package/clis/coupang/coupang.test.js +159 -0
  183. package/clis/coupang/product.js +257 -0
  184. package/clis/coupang/search.js +39 -16
  185. package/clis/coupang/utils.js +55 -1
  186. package/clis/crates/crate.js +62 -0
  187. package/clis/crates/search.js +44 -0
  188. package/clis/crates/utils.js +72 -0
  189. package/clis/ctrip/ctrip.test.js +234 -0
  190. package/clis/ctrip/hotel-suggest.js +45 -0
  191. package/clis/ctrip/search.js +23 -68
  192. package/clis/ctrip/utils.js +175 -0
  193. package/clis/cursor/ask.js +7 -3
  194. package/clis/cursor/composer.js +1 -0
  195. package/clis/cursor/export.js +1 -0
  196. package/clis/cursor/extract-code.js +1 -0
  197. package/clis/cursor/history.js +1 -0
  198. package/clis/cursor/model.js +1 -0
  199. package/clis/cursor/read.js +1 -0
  200. package/clis/cursor/send.js +1 -0
  201. package/clis/dblp/author.js +133 -0
  202. package/clis/dblp/dblp.test.js +397 -0
  203. package/clis/dblp/paper.js +40 -0
  204. package/clis/dblp/search.js +45 -0
  205. package/clis/dblp/utils.js +290 -0
  206. package/clis/dblp/venue.js +64 -0
  207. package/clis/deepseek/ask.js +13 -7
  208. package/clis/deepseek/ask.test.js +13 -13
  209. package/clis/deepseek/detail.js +38 -0
  210. package/clis/deepseek/detail.test.js +81 -0
  211. package/clis/deepseek/history.js +2 -0
  212. package/clis/deepseek/new.js +2 -0
  213. package/clis/deepseek/read.js +2 -0
  214. package/clis/deepseek/send.js +140 -0
  215. package/clis/deepseek/send.test.js +107 -0
  216. package/clis/deepseek/status.js +2 -0
  217. package/clis/deepseek/utils.js +66 -0
  218. package/clis/deepseek/utils.test.js +107 -1
  219. package/clis/defillama/defillama.test.js +99 -0
  220. package/clis/defillama/protocol.js +84 -0
  221. package/clis/defillama/protocols.js +55 -0
  222. package/clis/defillama/utils.js +99 -0
  223. package/clis/devto/devto.test.js +236 -0
  224. package/clis/devto/latest.js +74 -0
  225. package/clis/devto/read.js +103 -0
  226. package/clis/devto/tag.js +5 -1
  227. package/clis/devto/top.js +5 -1
  228. package/clis/devto/user.js +5 -1
  229. package/clis/dianping/__fixtures__/search.html +168 -0
  230. package/clis/dianping/__fixtures__/shop.html +6 -0
  231. package/clis/dianping/dianping.test.js +424 -0
  232. package/clis/dianping/search.js +154 -0
  233. package/clis/dianping/shop.js +173 -0
  234. package/clis/dianping/utils.js +157 -0
  235. package/clis/dictionary/examples.js +1 -0
  236. package/clis/dictionary/search.js +1 -0
  237. package/clis/dictionary/synonyms.js +1 -0
  238. package/clis/discord-app/channels.js +1 -0
  239. package/clis/discord-app/delete.js +1 -0
  240. package/clis/discord-app/members.js +1 -0
  241. package/clis/discord-app/read.js +1 -0
  242. package/clis/discord-app/search.js +1 -0
  243. package/clis/discord-app/send.js +1 -0
  244. package/clis/discord-app/servers.js +1 -0
  245. package/clis/discord-app/status.js +1 -0
  246. package/clis/dockerhub/image.js +52 -0
  247. package/clis/dockerhub/search.js +47 -0
  248. package/clis/dockerhub/utils.js +100 -0
  249. package/clis/douban/book-hot.js +1 -0
  250. package/clis/douban/download.js +1 -0
  251. package/clis/douban/marks.js +1 -0
  252. package/clis/douban/movie-hot.js +2 -1
  253. package/clis/douban/movie-hot.test.js +14 -0
  254. package/clis/douban/photos.js +2 -1
  255. package/clis/douban/reviews.js +1 -0
  256. package/clis/douban/search.js +1 -0
  257. package/clis/douban/subject.js +1 -0
  258. package/clis/douban/top250.js +1 -0
  259. package/clis/douban/utils.js +11 -13
  260. package/clis/douban/utils.test.js +79 -0
  261. package/clis/doubao/ask.js +8 -3
  262. package/clis/doubao/detail.js +2 -0
  263. package/clis/doubao/history.js +2 -0
  264. package/clis/doubao/meeting-summary.js +2 -0
  265. package/clis/doubao/meeting-transcript.js +2 -0
  266. package/clis/doubao/new.js +2 -0
  267. package/clis/doubao/read.js +2 -0
  268. package/clis/doubao/send.js +2 -0
  269. package/clis/doubao/status.js +2 -0
  270. package/clis/doubao-app/ask.js +1 -0
  271. package/clis/doubao-app/dump.js +1 -0
  272. package/clis/doubao-app/new.js +1 -0
  273. package/clis/doubao-app/read.js +1 -0
  274. package/clis/doubao-app/screenshot.js +1 -0
  275. package/clis/doubao-app/send.js +1 -0
  276. package/clis/doubao-app/status.js +1 -0
  277. package/clis/douyin/activities.js +1 -0
  278. package/clis/douyin/collections.js +1 -0
  279. package/clis/douyin/delete.js +1 -0
  280. package/clis/douyin/draft.js +1 -0
  281. package/clis/douyin/draft.test.js +1 -30
  282. package/clis/douyin/drafts.js +1 -0
  283. package/clis/douyin/hashtag.js +1 -0
  284. package/clis/douyin/location.js +1 -0
  285. package/clis/douyin/profile.js +1 -0
  286. package/clis/douyin/publish.js +1 -0
  287. package/clis/douyin/stats.js +1 -0
  288. package/clis/douyin/update.js +1 -0
  289. package/clis/douyin/user-videos.js +1 -0
  290. package/clis/douyin/videos.js +1 -0
  291. package/clis/eastmoney/announcement.js +1 -0
  292. package/clis/eastmoney/convertible.js +1 -0
  293. package/clis/eastmoney/etf.js +1 -0
  294. package/clis/eastmoney/holders.js +1 -0
  295. package/clis/eastmoney/hot-rank.js +1 -0
  296. package/clis/eastmoney/index-board.js +1 -0
  297. package/clis/eastmoney/kline.js +1 -0
  298. package/clis/eastmoney/kuaixun.js +1 -0
  299. package/clis/eastmoney/longhu.js +1 -0
  300. package/clis/eastmoney/money-flow.js +1 -0
  301. package/clis/eastmoney/northbound.js +1 -0
  302. package/clis/eastmoney/quote.js +1 -0
  303. package/clis/eastmoney/rank.js +1 -0
  304. package/clis/eastmoney/sectors.js +1 -0
  305. package/clis/endoflife/endoflife.test.js +51 -0
  306. package/clis/endoflife/product.js +55 -0
  307. package/clis/endoflife/utils.js +89 -0
  308. package/clis/facebook/__fixtures__/notifications-page.html +13 -0
  309. package/clis/facebook/add-friend.js +1 -0
  310. package/clis/facebook/events.js +1 -0
  311. package/clis/facebook/feed.js +1 -0
  312. package/clis/facebook/friends.js +1 -0
  313. package/clis/facebook/groups.js +1 -0
  314. package/clis/facebook/join-group.js +1 -0
  315. package/clis/facebook/marketplace-inbox.js +1 -0
  316. package/clis/facebook/marketplace-listings.js +1 -0
  317. package/clis/facebook/memories.js +1 -0
  318. package/clis/facebook/notifications.js +327 -30
  319. package/clis/facebook/notifications.test.js +458 -0
  320. package/clis/facebook/profile.js +1 -0
  321. package/clis/facebook/search.js +1 -0
  322. package/clis/flathub/app.js +71 -0
  323. package/clis/flathub/flathub.test.js +90 -0
  324. package/clis/flathub/search.js +80 -0
  325. package/clis/flathub/utils.js +114 -0
  326. package/clis/gemini/ask.js +8 -3
  327. package/clis/gemini/ask.test.js +2 -2
  328. package/clis/gemini/deep-research-result.js +7 -2
  329. package/clis/gemini/deep-research-result.test.js +15 -14
  330. package/clis/gemini/deep-research.js +9 -4
  331. package/clis/gemini/deep-research.test.js +15 -18
  332. package/clis/gemini/image.js +8 -2
  333. package/clis/gemini/new.js +2 -0
  334. package/clis/gemini/utils.js +0 -4
  335. package/clis/gitee/search.js +1 -0
  336. package/clis/gitee/trending.js +1 -0
  337. package/clis/gitee/user.js +1 -0
  338. package/clis/google/news.js +1 -0
  339. package/clis/google/search.js +1 -0
  340. package/clis/google/suggest.js +1 -0
  341. package/clis/google/trends.js +1 -0
  342. package/clis/google-scholar/cite.js +1 -1
  343. package/clis/google-scholar/profile.js +1 -1
  344. package/clis/google-scholar/search.js +1 -1
  345. package/clis/goproxy/goproxy.test.js +103 -0
  346. package/clis/goproxy/module.js +47 -0
  347. package/clis/goproxy/utils.js +165 -0
  348. package/clis/goproxy/versions.js +59 -0
  349. package/clis/gov-law/recent.js +1 -1
  350. package/clis/gov-law/search.js +1 -1
  351. package/clis/gov-policy/__fixtures__/recent.html +16 -0
  352. package/clis/gov-policy/__fixtures__/search.html +41 -0
  353. package/clis/gov-policy/gov-policy.test.js +224 -0
  354. package/clis/gov-policy/recent.js +67 -24
  355. package/clis/gov-policy/search.js +66 -23
  356. package/clis/gov-policy/utils.js +54 -0
  357. package/clis/grok/ask.js +50 -265
  358. package/clis/grok/ask.test.js +21 -46
  359. package/clis/grok/detail.js +60 -0
  360. package/clis/grok/history.js +48 -0
  361. package/clis/grok/{image.ts → image.js} +57 -70
  362. package/clis/grok/image.test.ts +20 -0
  363. package/clis/grok/new.js +20 -0
  364. package/clis/grok/read.js +39 -0
  365. package/clis/grok/send.js +50 -0
  366. package/clis/grok/status.js +41 -0
  367. package/clis/grok/utils.js +326 -0
  368. package/clis/grok/utils.test.js +103 -0
  369. package/clis/hackernews/ask.js +3 -1
  370. package/clis/hackernews/best.js +3 -1
  371. package/clis/hackernews/hackernews.test.js +132 -0
  372. package/clis/hackernews/jobs.js +3 -1
  373. package/clis/hackernews/new.js +3 -1
  374. package/clis/hackernews/read.js +188 -0
  375. package/clis/hackernews/search.js +3 -1
  376. package/clis/hackernews/show.js +3 -1
  377. package/clis/hackernews/top.js +3 -1
  378. package/clis/hackernews/user.js +1 -0
  379. package/clis/hf/datasets.js +88 -0
  380. package/clis/hf/hf.test.js +16 -0
  381. package/clis/hf/models.js +91 -0
  382. package/clis/hf/paper.js +79 -0
  383. package/clis/hf/spaces.js +101 -0
  384. package/clis/hf/top.js +2 -0
  385. package/clis/homebrew/cask.js +39 -0
  386. package/clis/homebrew/formula.js +41 -0
  387. package/clis/homebrew/popular.js +54 -0
  388. package/clis/homebrew/utils.js +100 -0
  389. package/clis/hupu/__fixtures__/hot-home.html +64 -0
  390. package/clis/hupu/detail.js +1 -1
  391. package/clis/hupu/hot.js +157 -34
  392. package/clis/hupu/hot.test.js +224 -0
  393. package/clis/hupu/like.js +1 -0
  394. package/clis/hupu/mentions.js +2 -1
  395. package/clis/hupu/reply.js +1 -0
  396. package/clis/hupu/search.js +3 -2
  397. package/clis/hupu/unlike.js +1 -0
  398. package/clis/imdb/person.js +1 -0
  399. package/clis/imdb/reviews.js +1 -0
  400. package/clis/imdb/search.js +1 -0
  401. package/clis/imdb/title.js +1 -0
  402. package/clis/imdb/top.js +1 -0
  403. package/clis/imdb/trending.js +1 -0
  404. package/clis/indeed/indeed.test.js +375 -0
  405. package/clis/indeed/job.js +86 -0
  406. package/clis/indeed/search.js +110 -0
  407. package/clis/indeed/utils.js +152 -0
  408. package/clis/instagram/collection-create.js +1 -0
  409. package/clis/instagram/collection-delete.js +1 -0
  410. package/clis/instagram/comment.js +1 -0
  411. package/clis/instagram/download.js +1 -0
  412. package/clis/instagram/explore.js +1 -0
  413. package/clis/instagram/follow.js +1 -0
  414. package/clis/instagram/followers.js +1 -0
  415. package/clis/instagram/following.js +1 -0
  416. package/clis/instagram/like.js +1 -0
  417. package/clis/instagram/note.js +2 -1
  418. package/clis/instagram/note.test.js +1 -29
  419. package/clis/instagram/post.js +2 -1
  420. package/clis/instagram/post.test.js +1 -1
  421. package/clis/instagram/profile.js +1 -0
  422. package/clis/instagram/reel.js +2 -1
  423. package/clis/instagram/save.js +1 -0
  424. package/clis/instagram/saved.js +1 -0
  425. package/clis/instagram/search.js +1 -0
  426. package/clis/instagram/story.js +2 -1
  427. package/clis/instagram/story.test.js +1 -34
  428. package/clis/instagram/unfollow.js +1 -0
  429. package/clis/instagram/unlike.js +1 -0
  430. package/clis/instagram/unsave.js +1 -0
  431. package/clis/instagram/user.js +1 -0
  432. package/clis/jd/add-cart.js +1 -0
  433. package/clis/jd/cart.js +1 -0
  434. package/clis/jd/commands.test.js +1 -24
  435. package/clis/jd/detail.js +1 -0
  436. package/clis/jd/item.js +1 -0
  437. package/clis/jd/reviews.js +1 -0
  438. package/clis/jd/search.js +1 -0
  439. package/clis/jianyu/detail.js +1 -0
  440. package/clis/jianyu/search.js +1 -0
  441. package/clis/jike/comment.js +1 -0
  442. package/clis/jike/create.js +1 -0
  443. package/clis/jike/feed.js +3 -1
  444. package/clis/jike/like.js +1 -0
  445. package/clis/jike/notifications.js +1 -0
  446. package/clis/jike/post.js +1 -0
  447. package/clis/jike/repost.js +1 -0
  448. package/clis/jike/search.js +3 -1
  449. package/clis/jike/topic.js +1 -0
  450. package/clis/jike/user.js +3 -1
  451. package/clis/jimeng/generate.js +1 -0
  452. package/clis/jimeng/history.js +1 -0
  453. package/clis/jimeng/new.js +1 -0
  454. package/clis/jimeng/workspaces.js +1 -0
  455. package/clis/ke/chengjiao.js +1 -0
  456. package/clis/ke/ershoufang.js +1 -0
  457. package/clis/ke/xiaoqu.js +1 -0
  458. package/clis/ke/zufang.js +1 -0
  459. package/clis/lesswrong/comments.js +1 -0
  460. package/clis/lesswrong/curated.js +1 -0
  461. package/clis/lesswrong/frontpage.js +1 -0
  462. package/clis/lesswrong/new.js +1 -0
  463. package/clis/lesswrong/read.js +1 -0
  464. package/clis/lesswrong/sequences.js +1 -0
  465. package/clis/lesswrong/shortform.js +1 -0
  466. package/clis/lesswrong/tag.js +1 -0
  467. package/clis/lesswrong/tags.js +1 -0
  468. package/clis/lesswrong/top-month.js +1 -0
  469. package/clis/lesswrong/top-week.js +1 -0
  470. package/clis/lesswrong/top-year.js +1 -0
  471. package/clis/lesswrong/top.js +1 -0
  472. package/clis/lesswrong/user-posts.js +1 -0
  473. package/clis/lesswrong/user.js +1 -0
  474. package/clis/lichess/lichess.test.js +85 -0
  475. package/clis/lichess/top.js +46 -0
  476. package/clis/lichess/user.js +91 -0
  477. package/clis/lichess/utils.js +97 -0
  478. package/clis/linkedin/search.js +108 -10
  479. package/clis/linkedin/search.test.js +222 -0
  480. package/clis/linkedin/timeline.js +1 -0
  481. package/clis/linux-do/categories.js +1 -0
  482. package/clis/linux-do/feed.js +3 -5
  483. package/clis/linux-do/feed.test.js +35 -0
  484. package/clis/linux-do/search.js +1 -0
  485. package/clis/linux-do/tags.js +2 -1
  486. package/clis/linux-do/topic-content.js +1 -0
  487. package/clis/linux-do/topic.js +1 -0
  488. package/clis/linux-do/user-posts.js +1 -0
  489. package/clis/linux-do/user-topics.js +1 -0
  490. package/clis/lobsters/active.js +4 -1
  491. package/clis/lobsters/domain.js +92 -0
  492. package/clis/lobsters/hot.js +4 -1
  493. package/clis/lobsters/lobsters.test.js +169 -0
  494. package/clis/lobsters/newest.js +4 -1
  495. package/clis/lobsters/read.js +196 -0
  496. package/clis/lobsters/tag.js +4 -1
  497. package/clis/maimai/search-talents.js +1 -0
  498. package/clis/maven/artifact.js +49 -0
  499. package/clis/maven/search.js +51 -0
  500. package/clis/maven/utils.js +110 -0
  501. package/clis/mdn/search.js +97 -0
  502. package/clis/medium/feed.js +1 -0
  503. package/clis/medium/search.js +1 -0
  504. package/clis/medium/tag.js +135 -0
  505. package/clis/medium/user.js +1 -0
  506. package/clis/mubu/doc.js +1 -0
  507. package/clis/mubu/docs.js +1 -0
  508. package/clis/mubu/notes.js +1 -0
  509. package/clis/mubu/recent.js +1 -0
  510. package/clis/mubu/search.js +1 -0
  511. package/clis/notebooklm/current.js +1 -0
  512. package/clis/notebooklm/get.js +1 -0
  513. package/clis/notebooklm/history.js +1 -0
  514. package/clis/notebooklm/list.js +1 -0
  515. package/clis/notebooklm/note-list.js +1 -0
  516. package/clis/notebooklm/notes-get.js +1 -0
  517. package/clis/notebooklm/open.js +1 -0
  518. package/clis/notebooklm/source-fulltext.js +1 -0
  519. package/clis/notebooklm/source-get.js +1 -0
  520. package/clis/notebooklm/source-guide.js +1 -0
  521. package/clis/notebooklm/source-list.js +1 -0
  522. package/clis/notebooklm/status.js +1 -0
  523. package/clis/notebooklm/summary.js +1 -0
  524. package/clis/notion/export.js +1 -0
  525. package/clis/notion/favorites.js +1 -0
  526. package/clis/notion/new.js +1 -0
  527. package/clis/notion/read.js +1 -0
  528. package/clis/notion/search.js +1 -0
  529. package/clis/notion/sidebar.js +1 -0
  530. package/clis/notion/status.js +1 -0
  531. package/clis/notion/write.js +1 -0
  532. package/clis/nowcoder/companies.js +1 -0
  533. package/clis/nowcoder/creators.js +1 -0
  534. package/clis/nowcoder/detail.js +1 -0
  535. package/clis/nowcoder/experience.js +1 -0
  536. package/clis/nowcoder/hot.js +1 -0
  537. package/clis/nowcoder/jobs.js +1 -0
  538. package/clis/nowcoder/notifications.js +1 -0
  539. package/clis/nowcoder/papers.js +1 -0
  540. package/clis/nowcoder/practice.js +1 -0
  541. package/clis/nowcoder/recommend.js +1 -0
  542. package/clis/nowcoder/referral.js +1 -0
  543. package/clis/nowcoder/salary.js +1 -0
  544. package/clis/nowcoder/search.js +1 -0
  545. package/clis/nowcoder/suggest.js +1 -0
  546. package/clis/nowcoder/topics.js +1 -0
  547. package/clis/nowcoder/trending.js +1 -0
  548. package/clis/npm/downloads.js +59 -0
  549. package/clis/npm/package.js +70 -0
  550. package/clis/npm/search.js +49 -0
  551. package/clis/npm/utils.js +76 -0
  552. package/clis/nuget/nuget.test.js +111 -0
  553. package/clis/nuget/package.js +101 -0
  554. package/clis/nuget/search.js +69 -0
  555. package/clis/nuget/utils.js +87 -0
  556. package/clis/nvd/cve.js +121 -0
  557. package/clis/oeis/oeis.test.js +88 -0
  558. package/clis/oeis/search.js +63 -0
  559. package/clis/oeis/sequence.js +71 -0
  560. package/clis/oeis/utils.js +88 -0
  561. package/clis/ones/login.js +1 -0
  562. package/clis/ones/logout.js +1 -0
  563. package/clis/ones/me.js +1 -0
  564. package/clis/ones/my-tasks.js +1 -0
  565. package/clis/ones/task.js +1 -0
  566. package/clis/ones/tasks.js +1 -0
  567. package/clis/ones/token-info.js +1 -0
  568. package/clis/ones/worklog.js +1 -0
  569. package/clis/openalex/search.js +69 -0
  570. package/clis/openalex/utils.js +160 -0
  571. package/clis/openalex/work.js +65 -0
  572. package/clis/openfda/drug-label.js +74 -0
  573. package/clis/openfda/food-recall.js +65 -0
  574. package/clis/openfda/openfda.test.js +114 -0
  575. package/clis/openfda/utils.js +67 -0
  576. package/clis/openreview/openreview.test.js +345 -0
  577. package/clis/openreview/paper.js +43 -0
  578. package/clis/openreview/reviews.js +131 -0
  579. package/clis/openreview/search.js +46 -0
  580. package/clis/openreview/utils.js +158 -0
  581. package/clis/openreview/venue.js +63 -0
  582. package/clis/osv/osv.test.js +97 -0
  583. package/clis/osv/query.js +72 -0
  584. package/clis/osv/utils.js +169 -0
  585. package/clis/osv/vulnerability.js +54 -0
  586. package/clis/packagist/package.js +49 -0
  587. package/clis/packagist/search.js +43 -0
  588. package/clis/packagist/utils.js +113 -0
  589. package/clis/paperreview/feedback.js +2 -1
  590. package/clis/paperreview/review.js +2 -1
  591. package/clis/paperreview/submit.js +2 -1
  592. package/clis/pixiv/detail.js +1 -0
  593. package/clis/pixiv/download.js +1 -0
  594. package/clis/pixiv/download.test.js +1 -1
  595. package/clis/pixiv/illusts.js +2 -1
  596. package/clis/pixiv/illusts.test.js +1 -1
  597. package/clis/pixiv/ranking.js +2 -1
  598. package/clis/pixiv/search.js +2 -1
  599. package/clis/pixiv/search.test.js +1 -1
  600. package/clis/pixiv/user.js +2 -0
  601. package/clis/powerchina/search.js +1 -0
  602. package/clis/producthunt/browse.js +1 -0
  603. package/clis/producthunt/hot.js +1 -0
  604. package/clis/producthunt/posts.js +1 -0
  605. package/clis/producthunt/today.js +1 -0
  606. package/clis/pubmed/article.js +50 -0
  607. package/clis/pubmed/author.js +64 -0
  608. package/clis/pubmed/citations.js +36 -0
  609. package/clis/pubmed/pubmed.test.js +276 -0
  610. package/clis/pubmed/related.js +45 -0
  611. package/clis/pubmed/search.js +75 -0
  612. package/clis/pubmed/utils.js +309 -0
  613. package/clis/pypi/downloads.js +66 -0
  614. package/clis/pypi/package.js +79 -0
  615. package/clis/pypi/utils.js +55 -0
  616. package/clis/quark/ls.js +1 -0
  617. package/clis/quark/mkdir.js +1 -0
  618. package/clis/quark/mv.js +2 -1
  619. package/clis/quark/rename.js +1 -0
  620. package/clis/quark/rm.js +1 -0
  621. package/clis/quark/save.js +2 -1
  622. package/clis/quark/share-tree.js +1 -0
  623. package/clis/qwen/ask.js +85 -0
  624. package/clis/qwen/detail.js +62 -0
  625. package/clis/qwen/history.js +61 -0
  626. package/clis/qwen/image.js +179 -0
  627. package/clis/qwen/new.js +23 -0
  628. package/clis/qwen/read.js +41 -0
  629. package/clis/qwen/send.js +55 -0
  630. package/clis/qwen/status.js +37 -0
  631. package/clis/qwen/utils.js +409 -0
  632. package/clis/qwen/utils.test.js +45 -0
  633. package/clis/reddit/comment.js +1 -0
  634. package/clis/reddit/frontpage.js +1 -0
  635. package/clis/reddit/hot.js +6 -1
  636. package/clis/reddit/hot.test.js +18 -0
  637. package/clis/reddit/popular.js +1 -0
  638. package/clis/reddit/read.js +1 -0
  639. package/clis/reddit/save.js +1 -0
  640. package/clis/reddit/saved.js +1 -0
  641. package/clis/reddit/search.js +1 -0
  642. package/clis/reddit/subreddit.js +1 -0
  643. package/clis/reddit/subscribe.js +1 -0
  644. package/clis/reddit/upvote.js +1 -0
  645. package/clis/reddit/upvoted.js +1 -0
  646. package/clis/reddit/user-comments.js +1 -0
  647. package/clis/reddit/user-posts.js +1 -0
  648. package/clis/reddit/user.js +1 -0
  649. package/clis/rest-countries/country.js +65 -0
  650. package/clis/rest-countries/region.js +64 -0
  651. package/clis/rest-countries/rest-countries.test.js +83 -0
  652. package/clis/rest-countries/utils.js +126 -0
  653. package/clis/reuters/article-detail.js +53 -0
  654. package/clis/reuters/reuters.test.js +299 -0
  655. package/clis/reuters/search.js +46 -34
  656. package/clis/reuters/utils.js +159 -0
  657. package/clis/rfc/rfc.js +52 -0
  658. package/clis/rfc/rfc.test.js +74 -0
  659. package/clis/rfc/utils.js +72 -0
  660. package/clis/rubygems/gem.js +42 -0
  661. package/clis/rubygems/search.js +47 -0
  662. package/clis/rubygems/utils.js +86 -0
  663. package/clis/sinablog/article.js +1 -0
  664. package/clis/sinablog/hot.js +1 -0
  665. package/clis/sinablog/search.js +1 -0
  666. package/clis/sinablog/user.js +1 -0
  667. package/clis/sinafinance/news.js +1 -0
  668. package/clis/sinafinance/rolling-news.js +1 -0
  669. package/clis/sinafinance/stock-rank.js +1 -0
  670. package/clis/sinafinance/stock.js +1 -0
  671. package/clis/smzdm/search.js +1 -0
  672. package/clis/spotify/spotify.js +11 -0
  673. package/clis/stackoverflow/bounties.js +11 -3
  674. package/clis/stackoverflow/hot.js +10 -2
  675. package/clis/stackoverflow/read.js +314 -0
  676. package/clis/stackoverflow/related.js +66 -0
  677. package/clis/stackoverflow/search.js +10 -2
  678. package/clis/stackoverflow/stackoverflow.test.js +404 -0
  679. package/clis/stackoverflow/tag.js +60 -0
  680. package/clis/stackoverflow/unanswered.js +9 -2
  681. package/clis/stackoverflow/user.js +50 -0
  682. package/clis/stackoverflow/utils.js +118 -0
  683. package/clis/steam/app.js +67 -0
  684. package/clis/steam/search.js +58 -0
  685. package/clis/steam/steam.test.js +46 -0
  686. package/clis/steam/top-sellers.js +1 -0
  687. package/clis/steam/utils.js +107 -0
  688. package/clis/substack/feed.js +1 -0
  689. package/clis/substack/publication.js +1 -0
  690. package/clis/substack/search.js +1 -0
  691. package/clis/taobao/add-cart.js +1 -0
  692. package/clis/taobao/cart.js +1 -0
  693. package/clis/taobao/commands.test.js +1 -24
  694. package/clis/taobao/detail.js +1 -0
  695. package/clis/taobao/reviews.js +1 -0
  696. package/clis/taobao/search.js +1 -0
  697. package/clis/tdx/hot-rank.js +1 -0
  698. package/clis/test-utils.js +61 -0
  699. package/clis/ths/hot-rank.js +1 -0
  700. package/clis/tieba/hot.js +2 -2
  701. package/clis/tieba/posts.js +1 -0
  702. package/clis/tieba/read.js +1 -0
  703. package/clis/tieba/search.js +2 -1
  704. package/clis/tiktok/comment.js +128 -40
  705. package/clis/tiktok/creator-videos.js +270 -0
  706. package/clis/tiktok/creator-videos.test.js +113 -0
  707. package/clis/tiktok/explore.js +138 -29
  708. package/clis/tiktok/follow.js +116 -33
  709. package/clis/tiktok/following.js +157 -35
  710. package/clis/tiktok/friends.js +140 -37
  711. package/clis/tiktok/like.js +1 -0
  712. package/clis/tiktok/live.js +138 -41
  713. package/clis/tiktok/notifications.js +142 -38
  714. package/clis/tiktok/profile.js +1 -0
  715. package/clis/tiktok/refactor.test.js +389 -0
  716. package/clis/tiktok/save.js +1 -0
  717. package/clis/tiktok/search.js +1 -0
  718. package/clis/tiktok/unfollow.js +125 -38
  719. package/clis/tiktok/unlike.js +1 -0
  720. package/clis/tiktok/unsave.js +1 -0
  721. package/clis/tiktok/user.js +204 -29
  722. package/clis/tiktok/utils.js +505 -0
  723. package/clis/tiktok/write-refactor.test.js +370 -0
  724. package/clis/toutiao/articles.js +37 -62
  725. package/clis/toutiao/hot.js +63 -0
  726. package/clis/toutiao/toutiao.test.js +378 -0
  727. package/clis/toutiao/utils.js +161 -0
  728. package/clis/tvmaze/search.js +61 -0
  729. package/clis/tvmaze/show.js +60 -0
  730. package/clis/tvmaze/tvmaze.test.js +93 -0
  731. package/clis/tvmaze/utils.js +110 -0
  732. package/clis/twitter/accept.js +2 -1
  733. package/clis/twitter/article.js +1 -0
  734. package/clis/twitter/block.js +1 -0
  735. package/clis/twitter/bookmark.js +1 -0
  736. package/clis/twitter/bookmarks.js +2 -1
  737. package/clis/twitter/delete.js +1 -0
  738. package/clis/twitter/download.js +1 -0
  739. package/clis/twitter/follow.js +1 -0
  740. package/clis/twitter/followers.js +135 -69
  741. package/clis/twitter/following.js +1 -0
  742. package/clis/twitter/hide-reply.js +1 -0
  743. package/clis/twitter/like.js +1 -0
  744. package/clis/twitter/likes.js +2 -1
  745. package/clis/twitter/list-add.js +1 -0
  746. package/clis/twitter/list-remove.js +1 -0
  747. package/clis/twitter/list-tweets.js +1 -0
  748. package/clis/twitter/lists.js +1 -0
  749. package/clis/twitter/notifications.js +1 -0
  750. package/clis/twitter/post.js +1 -0
  751. package/clis/twitter/profile.js +1 -0
  752. package/clis/twitter/reply-dm.js +2 -1
  753. package/clis/twitter/reply.js +1 -0
  754. package/clis/twitter/reply.test.js +1 -29
  755. package/clis/twitter/search.js +1 -0
  756. package/clis/twitter/thread.js +1 -0
  757. package/clis/twitter/timeline.js +1 -0
  758. package/clis/twitter/trending.js +11 -12
  759. package/clis/twitter/trending.test.js +15 -0
  760. package/clis/twitter/tweets.js +2 -1
  761. package/clis/twitter/tweets.test.js +2 -2
  762. package/clis/twitter/unblock.js +1 -0
  763. package/clis/twitter/unbookmark.js +1 -0
  764. package/clis/twitter/unfollow.js +1 -0
  765. package/clis/uisdc/news.js +105 -0
  766. package/clis/uisdc/news.test.js +66 -0
  767. package/clis/uiverse/code.js +1 -0
  768. package/clis/uiverse/preview.js +1 -0
  769. package/clis/v2ex/daily.js +1 -0
  770. package/clis/v2ex/hot.js +1 -0
  771. package/clis/v2ex/latest.js +1 -0
  772. package/clis/v2ex/me.js +1 -0
  773. package/clis/v2ex/member.js +1 -0
  774. package/clis/v2ex/node.js +1 -0
  775. package/clis/v2ex/nodes.js +1 -0
  776. package/clis/v2ex/notifications.js +1 -0
  777. package/clis/v2ex/replies.js +1 -0
  778. package/clis/v2ex/topic.js +1 -0
  779. package/clis/v2ex/user.js +1 -0
  780. package/clis/wanfang/search.js +1 -1
  781. package/clis/web/read.js +48 -17
  782. package/clis/web/read.test.js +101 -1
  783. package/clis/weibo/comments.js +1 -0
  784. package/clis/weibo/favorites.js +1 -0
  785. package/clis/weibo/feed.js +3 -1
  786. package/clis/weibo/hot.js +1 -0
  787. package/clis/weibo/me.js +1 -0
  788. package/clis/weibo/post.js +1 -0
  789. package/clis/weibo/publish.js +1 -0
  790. package/clis/weibo/search.js +8 -2
  791. package/clis/weibo/user.js +1 -0
  792. package/clis/weixin/create-draft.js +2 -1
  793. package/clis/weixin/download.js +1 -0
  794. package/clis/weixin/drafts.js +2 -1
  795. package/clis/weixin/drafts.test.js +5 -1
  796. package/clis/weixin/search.js +157 -0
  797. package/clis/weixin/search.test.js +227 -0
  798. package/clis/weread/ai-outline.js +1 -0
  799. package/clis/weread/book.js +1 -0
  800. package/clis/weread/highlights.js +1 -0
  801. package/clis/weread/notebooks.js +1 -0
  802. package/clis/weread/notes.js +1 -0
  803. package/clis/weread/ranking.js +1 -0
  804. package/clis/weread/search.js +1 -0
  805. package/clis/weread/shelf.js +1 -0
  806. package/clis/wikidata/entity.js +60 -0
  807. package/clis/wikidata/search.js +50 -0
  808. package/clis/wikidata/utils.js +117 -0
  809. package/clis/wikidata/wikidata.test.js +83 -0
  810. package/clis/wikipedia/page.js +95 -0
  811. package/clis/wikipedia/random.js +1 -0
  812. package/clis/wikipedia/search.js +1 -0
  813. package/clis/wikipedia/summary.js +1 -0
  814. package/clis/wikipedia/trending.js +1 -0
  815. package/clis/wttr/current.js +63 -0
  816. package/clis/wttr/forecast.js +71 -0
  817. package/clis/wttr/utils.js +50 -0
  818. package/clis/wttr/wttr.test.js +84 -0
  819. package/clis/xianyu/chat.js +17 -4
  820. package/clis/xianyu/chat.test.js +64 -0
  821. package/clis/xianyu/item.js +1 -0
  822. package/clis/xianyu/publish.js +485 -0
  823. package/clis/xianyu/publish.test.js +220 -0
  824. package/clis/xianyu/search.js +1 -0
  825. package/clis/xiaoe/catalog.js +105 -39
  826. package/clis/xiaoe/content.js +165 -29
  827. package/clis/xiaoe/courses.js +86 -28
  828. package/clis/xiaoe/detail.js +1 -0
  829. package/clis/xiaoe/play-url.js +1 -0
  830. package/clis/xiaoe/xiaoe.test.js +486 -0
  831. package/clis/xiaohongshu/comments.js +1 -0
  832. package/clis/xiaohongshu/creator-note-detail.js +1 -0
  833. package/clis/xiaohongshu/creator-notes-summary.js +2 -1
  834. package/clis/xiaohongshu/creator-notes.js +1 -0
  835. package/clis/xiaohongshu/creator-profile.js +1 -0
  836. package/clis/xiaohongshu/creator-stats.js +1 -0
  837. package/clis/xiaohongshu/download.js +1 -0
  838. package/clis/xiaohongshu/feed.js +2 -1
  839. package/clis/xiaohongshu/note.js +1 -0
  840. package/clis/xiaohongshu/notifications.js +1 -0
  841. package/clis/xiaohongshu/publish.js +17 -3
  842. package/clis/xiaohongshu/publish.test.js +46 -1
  843. package/clis/xiaohongshu/search.js +1 -0
  844. package/clis/xiaohongshu/user.js +1 -0
  845. package/clis/xiaoyuzhou/download.js +1 -0
  846. package/clis/xiaoyuzhou/episode.js +1 -0
  847. package/clis/xiaoyuzhou/podcast-episodes.js +1 -0
  848. package/clis/xiaoyuzhou/podcast.js +1 -0
  849. package/clis/xiaoyuzhou/transcript.js +1 -0
  850. package/clis/xueqiu/comments.js +1 -0
  851. package/clis/xueqiu/earnings-date.js +1 -0
  852. package/clis/xueqiu/feed.js +1 -0
  853. package/clis/xueqiu/fund-holdings.js +1 -0
  854. package/clis/xueqiu/fund-snapshot.js +1 -0
  855. package/clis/xueqiu/groups.js +1 -0
  856. package/clis/xueqiu/hot-stock.js +1 -0
  857. package/clis/xueqiu/hot.js +1 -0
  858. package/clis/xueqiu/kline.js +1 -0
  859. package/clis/xueqiu/search.js +1 -0
  860. package/clis/xueqiu/stock.js +1 -0
  861. package/clis/xueqiu/watchlist.js +1 -0
  862. package/clis/yahoo-finance/quote.js +1 -0
  863. package/clis/yollomi/background.js +1 -0
  864. package/clis/yollomi/edit.js +1 -0
  865. package/clis/yollomi/face-swap.js +1 -0
  866. package/clis/yollomi/generate.js +1 -0
  867. package/clis/yollomi/models.js +1 -0
  868. package/clis/yollomi/object-remover.js +1 -0
  869. package/clis/yollomi/remove-bg.js +1 -0
  870. package/clis/yollomi/restore.js +1 -0
  871. package/clis/yollomi/try-on.js +1 -0
  872. package/clis/yollomi/upload.js +1 -0
  873. package/clis/yollomi/upscale.js +1 -0
  874. package/clis/yollomi/video.js +1 -0
  875. package/clis/youtube/channel.js +1 -0
  876. package/clis/youtube/comments.js +1 -0
  877. package/clis/youtube/feed.js +8 -7
  878. package/clis/youtube/feed.test.js +131 -0
  879. package/clis/youtube/history.js +1 -0
  880. package/clis/youtube/like.js +1 -0
  881. package/clis/youtube/playlist.js +1 -0
  882. package/clis/youtube/search.js +1 -0
  883. package/clis/youtube/subscribe.js +1 -0
  884. package/clis/youtube/subscriptions.js +1 -0
  885. package/clis/youtube/transcript.js +14 -19
  886. package/clis/youtube/transcript.test.js +17 -0
  887. package/clis/youtube/unlike.js +1 -0
  888. package/clis/youtube/unsubscribe.js +1 -0
  889. package/clis/youtube/video.js +1 -0
  890. package/clis/youtube/watch-later.js +1 -0
  891. package/clis/yuanbao/ask.js +18 -66
  892. package/clis/yuanbao/ask.test.js +5 -5
  893. package/clis/yuanbao/detail.js +65 -0
  894. package/clis/yuanbao/history.js +51 -0
  895. package/clis/yuanbao/new.js +2 -0
  896. package/clis/yuanbao/read.js +38 -0
  897. package/clis/yuanbao/send.js +57 -0
  898. package/clis/yuanbao/shared.js +297 -5
  899. package/clis/yuanbao/shared.test.js +80 -0
  900. package/clis/yuanbao/status.js +44 -0
  901. package/clis/zhihu/answer.js +1 -0
  902. package/clis/zhihu/collection.js +1 -0
  903. package/clis/zhihu/collections.js +1 -0
  904. package/clis/zhihu/comment.js +1 -0
  905. package/clis/zhihu/download.js +1 -0
  906. package/clis/zhihu/favorite.js +1 -0
  907. package/clis/zhihu/follow.js +1 -0
  908. package/clis/zhihu/hot.js +1 -0
  909. package/clis/zhihu/like.js +1 -0
  910. package/clis/zhihu/question.js +1 -0
  911. package/clis/zhihu/search.js +1 -0
  912. package/clis/zlibrary/commands.test.js +1 -11
  913. package/clis/zlibrary/info.js +1 -0
  914. package/clis/zlibrary/search.js +1 -0
  915. package/clis/zsxq/dynamics.js +1 -0
  916. package/clis/zsxq/groups.js +1 -0
  917. package/clis/zsxq/search.js +1 -0
  918. package/clis/zsxq/topic.js +1 -0
  919. package/clis/zsxq/topics.js +1 -0
  920. package/dist/src/adapter-source.test.js +1 -1
  921. package/dist/src/browser/analyze.test.js +2 -0
  922. package/dist/src/browser/base-page.d.ts +18 -0
  923. package/dist/src/browser/base-page.js +116 -4
  924. package/dist/src/browser/base-page.test.js +108 -0
  925. package/dist/src/browser/cdp.d.ts +1 -0
  926. package/dist/src/browser/cdp.js +57 -9
  927. package/dist/src/browser/cdp.test.js +3 -0
  928. package/dist/src/browser/daemon-client.d.ts +4 -0
  929. package/dist/src/browser/errors.js +1 -1
  930. package/dist/src/browser/page.d.ts +2 -1
  931. package/dist/src/browser/page.js +9 -1
  932. package/dist/src/browser/page.test.js +46 -0
  933. package/dist/src/browser/target-errors.d.ts +2 -1
  934. package/dist/src/browser/target-errors.js +1 -0
  935. package/dist/src/browser/target-resolver.d.ts +35 -3
  936. package/dist/src/browser/target-resolver.js +59 -10
  937. package/dist/src/browser/verify-fixture.d.ts +6 -1
  938. package/dist/src/browser/verify-fixture.js +87 -0
  939. package/dist/src/browser/verify-fixture.test.js +44 -1
  940. package/dist/src/build-manifest.js +12 -4
  941. package/dist/src/build-manifest.test.js +20 -20
  942. package/dist/src/capabilityRouting.d.ts +16 -1
  943. package/dist/src/capabilityRouting.js +24 -1
  944. package/dist/src/capabilityRouting.test.js +20 -2
  945. package/dist/src/cli.js +213 -12
  946. package/dist/src/cli.test.js +329 -0
  947. package/dist/src/commanderAdapter.d.ts +1 -1
  948. package/dist/src/commanderAdapter.js +17 -12
  949. package/dist/src/commanderAdapter.test.js +7 -7
  950. package/dist/src/convention-audit.d.ts +50 -0
  951. package/dist/src/convention-audit.js +546 -0
  952. package/dist/src/convention-audit.test.d.ts +1 -0
  953. package/dist/src/convention-audit.test.js +226 -0
  954. package/dist/src/discovery.js +4 -5
  955. package/dist/src/doctor.js +0 -1
  956. package/dist/src/doctor.test.js +1 -1
  957. package/dist/src/engine.test.js +10 -10
  958. package/dist/src/errors.js +1 -1
  959. package/dist/src/execution.d.ts +1 -1
  960. package/dist/src/execution.js +111 -27
  961. package/dist/src/execution.test.js +334 -25
  962. package/dist/src/help.d.ts +36 -0
  963. package/dist/src/help.js +171 -0
  964. package/dist/src/help.test.d.ts +1 -0
  965. package/dist/src/help.test.js +54 -0
  966. package/dist/src/main.js +27 -8
  967. package/dist/src/manifest-types.d.ts +7 -3
  968. package/dist/src/pipeline/executor.js +1 -1
  969. package/dist/src/pipeline/executor.test.js +8 -0
  970. package/dist/src/pipeline/registry.d.ts +9 -0
  971. package/dist/src/pipeline/registry.js +13 -1
  972. package/dist/src/pipeline/steps/browser.d.ts +1 -0
  973. package/dist/src/pipeline/steps/browser.js +10 -0
  974. package/dist/src/pipeline/steps/download.test.js +1 -0
  975. package/dist/src/plugin.test.js +26 -26
  976. package/dist/src/registry-api.d.ts +1 -1
  977. package/dist/src/registry.d.ts +17 -11
  978. package/dist/src/registry.js +24 -6
  979. package/dist/src/registry.test.js +29 -22
  980. package/dist/src/runtime.d.ts +2 -1
  981. package/dist/src/runtime.js +1 -1
  982. package/dist/src/serialization.d.ts +6 -2
  983. package/dist/src/serialization.js +31 -7
  984. package/dist/src/serialization.test.js +41 -2
  985. package/dist/src/types.d.ts +19 -0
  986. package/dist/src/validate.js +15 -11
  987. package/dist/src/validate.test.d.ts +9 -0
  988. package/dist/src/validate.test.js +90 -0
  989. package/package.json +4 -1
  990. package/scripts/check-listing-id-pairing.mjs +193 -0
  991. package/scripts/check-silent-column-drop.mjs +105 -0
  992. package/scripts/check-typed-error-lint.mjs +118 -0
  993. package/scripts/fetch-adapters.js +1 -1
  994. package/scripts/silent-column-drop-baseline.json +962 -0
  995. package/scripts/typed-error-lint-baseline.json +1514 -0
  996. package/clis/ctrip/search.test.js +0 -64
  997. package/clis/gov-policy/commands.test.js +0 -27
  998. package/clis/linux-do/category.js +0 -36
  999. package/clis/linux-do/hot.js +0 -25
  1000. package/clis/linux-do/latest.js +0 -18
  1001. package/clis/pixiv/test-utils.js +0 -23
  1002. package/clis/toutiao/articles.test.js +0 -30
  1003. package/dist/src/analysis.d.ts +0 -40
  1004. package/dist/src/analysis.js +0 -172
@@ -0,0 +1,80 @@
1
+ // flathub search — keyword search the Flathub app registry.
2
+ //
3
+ // POSTs to `/api/v2/search` with `{query}`. The `appId` column round-trips into
4
+ // `flathub app` for full appstream detail. Note: the search hit's `id` is
5
+ // underscored (e.g. `org_mozilla_firefox`); the actual reverse-DNS appId lives
6
+ // at `app_id`. We surface the dotted form so it round-trips without translation.
7
+ import { cli, Strategy } from '@jackwener/opencli/registry';
8
+ import { EmptyResultError } from '@jackwener/opencli/errors';
9
+ import {
10
+ FLATHUB_API_BASE,
11
+ FLATHUB_APP_BASE,
12
+ flathubFetch,
13
+ joinList,
14
+ requireBoundedInt,
15
+ requireString,
16
+ } from './utils.js';
17
+
18
+ cli({
19
+ site: 'flathub',
20
+ name: 'search',
21
+ access: 'read',
22
+ description: 'Search Flathub apps by keyword',
23
+ domain: 'flathub.org',
24
+ strategy: Strategy.PUBLIC,
25
+ browser: false,
26
+ args: [
27
+ { name: 'query', positional: true, required: true, help: 'Search keyword' },
28
+ { name: 'limit', type: 'int', default: 25, help: 'Max apps (1-100)' },
29
+ ],
30
+ columns: [
31
+ 'rank',
32
+ 'appId',
33
+ 'name',
34
+ 'summary',
35
+ 'developer',
36
+ 'license',
37
+ 'isFreeLicense',
38
+ 'mainCategories',
39
+ 'installsLastMonth',
40
+ 'updatedAt',
41
+ 'url',
42
+ ],
43
+ func: async (args) => {
44
+ const query = requireString(args.query, 'query');
45
+ const limit = requireBoundedInt(args.limit, 25, 100);
46
+ const url = `${FLATHUB_API_BASE}/search`;
47
+ const body = await flathubFetch(url, 'flathub search', {
48
+ method: 'POST',
49
+ headers: { 'content-type': 'application/json' },
50
+ body: JSON.stringify({ query, hitsPerPage: limit, page: 1 }),
51
+ });
52
+ const list = Array.isArray(body?.hits) ? body.hits : [];
53
+ if (!list.length) {
54
+ throw new EmptyResultError('flathub search', `No Flathub apps matched "${query}".`);
55
+ }
56
+ return list.slice(0, limit).map((hit, i) => {
57
+ const appId = typeof hit?.app_id === 'string' ? hit.app_id : null;
58
+ return {
59
+ rank: i + 1,
60
+ appId,
61
+ name: typeof hit?.name === 'string' ? hit.name : null,
62
+ summary: typeof hit?.summary === 'string' ? hit.summary : null,
63
+ developer: typeof hit?.developer_name === 'string' ? hit.developer_name : null,
64
+ license: typeof hit?.project_license === 'string' ? hit.project_license : null,
65
+ isFreeLicense: hit?.is_free_license === true,
66
+ // `main_categories` comes back as a string (single value) on /search, not an array.
67
+ mainCategories: typeof hit?.main_categories === 'string'
68
+ ? hit.main_categories
69
+ : joinList(hit?.main_categories),
70
+ installsLastMonth: typeof hit?.installs_last_month === 'number' ? hit.installs_last_month : null,
71
+ // /search emits `updated_at` as unix-seconds int; /appstream emits ISO strings.
72
+ // Normalise to ISO date here so both surfaces look consistent.
73
+ updatedAt: typeof hit?.updated_at === 'number' && hit.updated_at > 0
74
+ ? new Date(hit.updated_at * 1000).toISOString().slice(0, 10)
75
+ : (typeof hit?.updated_at === 'string' ? hit.updated_at : null),
76
+ url: appId ? `${FLATHUB_APP_BASE}/${appId}` : '',
77
+ };
78
+ });
79
+ },
80
+ });
@@ -0,0 +1,114 @@
1
+ // Shared helpers for the Flathub adapters (https://flathub.org).
2
+ //
3
+ // Flathub is the canonical Linux flatpak app registry. Public REST API at
4
+ // `flathub.org/api/v2`, no auth, no key. Two endpoints we surface:
5
+ // • POST /search → keyword search, returns app metadata
6
+ // • GET /appstream/<appId> → full appstream metadata for one app
7
+ import { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';
8
+
9
+ export const FLATHUB_API_BASE = 'https://flathub.org/api/v2';
10
+ export const FLATHUB_APP_BASE = 'https://flathub.org/apps';
11
+ const UA = 'opencli-flathub-adapter/1.0 (+https://github.com/jackwener/opencli; mailto:opencli@example.com)';
12
+
13
+ // AppStream IDs are reverse-DNS (e.g. "org.gnome.Calculator"); the spec allows
14
+ // letters, digits, `.`, `_`, `-`. Min two segments separated by `.`.
15
+ const APP_ID_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*(?:\.[A-Za-z0-9_][A-Za-z0-9_-]*){1,}$/;
16
+
17
+ export function requireString(value, label) {
18
+ const s = String(value ?? '').trim();
19
+ if (!s) throw new ArgumentError(`flathub ${label} cannot be empty`);
20
+ return s;
21
+ }
22
+
23
+ export function requireBoundedInt(value, defaultValue, maxValue, label = 'limit') {
24
+ const raw = value ?? defaultValue;
25
+ const n = typeof raw === 'number' ? raw : Number(raw);
26
+ if (!Number.isInteger(n) || n <= 0) {
27
+ throw new ArgumentError(`flathub ${label} must be a positive integer`);
28
+ }
29
+ if (n > maxValue) {
30
+ throw new ArgumentError(`flathub ${label} must be <= ${maxValue}`);
31
+ }
32
+ return n;
33
+ }
34
+
35
+ export function requireAppId(value) {
36
+ const raw = String(value ?? '').trim();
37
+ if (!raw) throw new ArgumentError('flathub appId is required (e.g. "org.mozilla.firefox")');
38
+ if (!APP_ID_PATTERN.test(raw)) {
39
+ throw new ArgumentError(
40
+ `flathub appId "${value}" is not a valid AppStream identifier`,
41
+ 'AppStream IDs use reverse-DNS like "org.mozilla.firefox" — letters/digits/`._-` with at least one dot.',
42
+ );
43
+ }
44
+ return raw;
45
+ }
46
+
47
+ export async function flathubFetch(url, label, init) {
48
+ let resp;
49
+ try {
50
+ resp = await fetch(url, {
51
+ method: init?.method ?? 'GET',
52
+ headers: { 'user-agent': UA, accept: 'application/json', ...(init?.headers ?? {}) },
53
+ body: init?.body,
54
+ });
55
+ }
56
+ catch (err) {
57
+ throw new CommandExecutionError(
58
+ `${label} request failed: ${err?.message ?? err}`,
59
+ 'Check that flathub.org is reachable from this network.',
60
+ );
61
+ }
62
+ if (resp.status === 404) {
63
+ throw new EmptyResultError(label, `Flathub returned 404 for ${url}.`);
64
+ }
65
+ if (resp.status === 429) {
66
+ throw new CommandExecutionError(`${label} returned HTTP 429 (rate limited)`);
67
+ }
68
+ if (!resp.ok) {
69
+ throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);
70
+ }
71
+ let body;
72
+ try {
73
+ body = await resp.json();
74
+ }
75
+ catch (err) {
76
+ throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
77
+ }
78
+ return body;
79
+ }
80
+
81
+ export function joinList(value, max = 10) {
82
+ if (!Array.isArray(value)) return '';
83
+ const items = value.filter((v) => typeof v === 'string' && v.trim());
84
+ if (items.length === 0) return '';
85
+ if (items.length > max) return [...items.slice(0, max), `(+${items.length - max})`].join(', ');
86
+ return items.join(', ');
87
+ }
88
+
89
+ // Coerce flathub's `timestamp` field (sometimes int, sometimes numeric string).
90
+ function timestampNumber(value) {
91
+ if (typeof value === 'number' && Number.isFinite(value)) return value;
92
+ if (typeof value === 'string' && /^\d+$/.test(value.trim())) return Number(value.trim());
93
+ return 0;
94
+ }
95
+
96
+ /** Pick the most recent appstream `releases[].version` if present. */
97
+ export function pickLatestRelease(releases) {
98
+ if (!Array.isArray(releases) || releases.length === 0) return { version: null, date: null };
99
+ // releases[].timestamp is unix-seconds; flathub returns either int or numeric string.
100
+ const sorted = [...releases].filter((r) => r && typeof r === 'object').sort((a, b) => {
101
+ return timestampNumber(b?.timestamp) - timestampNumber(a?.timestamp);
102
+ });
103
+ const top = sorted[0];
104
+ if (!top) return { version: null, date: null };
105
+ let date = null;
106
+ const ts = timestampNumber(top.timestamp);
107
+ if (ts > 0) {
108
+ const d = new Date(ts * 1000);
109
+ if (!Number.isNaN(d.getTime())) date = d.toISOString().slice(0, 10);
110
+ }
111
+ // Fallback: appstream emits `date` as ISO string ("2024-12-09") for some apps.
112
+ if (!date && typeof top.date === 'string' && top.date.trim()) date = top.date.trim();
113
+ return { version: typeof top.version === 'string' ? top.version : null, date };
114
+ }
@@ -1,4 +1,5 @@
1
1
  import { cli, Strategy } from '@jackwener/opencli/registry';
2
+ import { ArgumentError } from '@jackwener/opencli/errors';
2
3
  import { GEMINI_DOMAIN, readGeminiSnapshot, sendGeminiMessage, startNewGeminiChat, waitForGeminiResponse, waitForGeminiSubmission } from './utils.js';
3
4
  function normalizeBooleanFlag(value) {
4
5
  if (typeof value === 'boolean')
@@ -10,22 +11,26 @@ const NO_RESPONSE_PREFIX = '[NO RESPONSE]';
10
11
  export const askCommand = cli({
11
12
  site: 'gemini',
12
13
  name: 'ask',
14
+ access: 'write',
13
15
  description: 'Send a prompt to Gemini and return only the assistant response',
14
16
  domain: GEMINI_DOMAIN,
15
17
  strategy: Strategy.COOKIE,
16
18
  browser: true,
19
+ browserSession: { reuse: 'site' },
17
20
  navigateBefore: false,
18
21
  defaultFormat: 'plain',
19
- timeoutSeconds: 180,
20
22
  args: [
21
23
  { name: 'prompt', required: true, positional: true, help: 'Prompt to send' },
22
- { name: 'timeout', required: false, help: 'Max seconds to wait (default: 60)', default: '60' },
24
+ { name: 'timeout', type: 'int', required: false, help: 'Max seconds to wait (default: 60)', default: 60 },
23
25
  { name: 'new', required: false, help: 'Start a new chat first (true/false, default: false)', default: 'false' },
24
26
  ],
25
27
  columns: ['response'],
26
28
  func: async (page, kwargs) => {
27
29
  const prompt = kwargs.prompt;
28
- const timeout = parseInt(kwargs.timeout, 10) || 60;
30
+ const timeout = kwargs.timeout;
31
+ if (!Number.isInteger(timeout) || timeout < 1) {
32
+ throw new ArgumentError('--timeout must be a positive integer (seconds)');
33
+ }
29
34
  const startFresh = normalizeBooleanFlag(kwargs.new);
30
35
  if (startFresh)
31
36
  await startNewGeminiChat(page);
@@ -79,7 +79,7 @@ describe('gemini ask orchestration', () => {
79
79
  mocks.sendGeminiMessage.mockResolvedValueOnce('button');
80
80
  mocks.waitForGeminiSubmission.mockResolvedValueOnce(submission);
81
81
  mocks.waitForGeminiResponse.mockResolvedValueOnce('OK');
82
- const result = await askCommand.func(page, { prompt: '请只回复:OK', timeout: '20', new: 'false' });
82
+ const result = await askCommand.func(page, { prompt: '请只回复:OK', timeout: 20, new: 'false' });
83
83
  expect(mocks.readGeminiSnapshot).toHaveBeenCalledWith(page);
84
84
  expect(mocks.waitForGeminiSubmission).toHaveBeenCalledWith(page, baseline, 20);
85
85
  expect(mocks.waitForGeminiResponse).toHaveBeenCalledWith(page, submission, '请只回复:OK', 18);
@@ -94,7 +94,7 @@ describe('gemini ask orchestration', () => {
94
94
  mocks.sendGeminiMessage.mockResolvedValueOnce('button');
95
95
  mocks.waitForGeminiSubmission.mockResolvedValueOnce(submission);
96
96
  mocks.waitForGeminiResponse.mockResolvedValueOnce('');
97
- await askCommand.func(page, { prompt: '请只回复:OK', timeout: '20', new: 'false' });
97
+ await askCommand.func(page, { prompt: '请只回复:OK', timeout: 20, new: 'false' });
98
98
  expect(mocks.waitForGeminiResponse).toHaveBeenCalledWith(page, submission, '请只回复:OK', 0);
99
99
  });
100
100
  });
@@ -1,4 +1,5 @@
1
1
  import { cli, Strategy } from '@jackwener/opencli/registry';
2
+ import { ArgumentError } from '@jackwener/opencli/errors';
2
3
  import { GEMINI_DOMAIN, clickGeminiConversationByTitle, exportGeminiDeepResearchReport, getLatestGeminiAssistantResponse, getGeminiPageState, parseGeminiConversationUrl, parseGeminiTitleMatchMode, readGeminiSnapshot, resolveGeminiConversationForQuery, waitForGeminiTranscript, getGeminiConversationList, } from './utils.js';
3
4
  const DEEP_RESEARCH_WAITING_MESSAGE = 'Deep Research is still running. Please wait and retry later.';
4
5
  const DEEP_RESEARCH_NO_DOCS_MESSAGE = 'No Docs URL found. Please check Share & Export -> Export to Docs in Gemini UI.';
@@ -38,10 +39,12 @@ async function resolveDeepResearchExportResponse(page, timeoutSeconds) {
38
39
  export const deepResearchResultCommand = cli({
39
40
  site: 'gemini',
40
41
  name: 'deep-research-result',
42
+ access: 'read',
41
43
  description: 'Export Deep Research report URL from a Gemini conversation',
42
44
  domain: GEMINI_DOMAIN,
43
45
  strategy: Strategy.COOKIE,
44
46
  browser: true,
47
+ browserSession: { reuse: 'site' },
45
48
  navigateBefore: false,
46
49
  defaultFormat: 'plain',
47
50
  args: [
@@ -53,8 +56,10 @@ export const deepResearchResultCommand = cli({
53
56
  func: async (page, kwargs) => {
54
57
  const query = String(kwargs.query ?? '').trim();
55
58
  const matchMode = parseGeminiTitleMatchMode(kwargs.match);
56
- const timeoutRaw = Number.parseInt(String(kwargs.timeout ?? ''), 10);
57
- const timeoutSeconds = Number.isFinite(timeoutRaw) && timeoutRaw > 0 ? timeoutRaw : 120;
59
+ const timeoutSeconds = kwargs.timeout;
60
+ if (!Number.isInteger(timeoutSeconds) || timeoutSeconds < 1) {
61
+ throw new ArgumentError('--timeout must be a positive integer (seconds)');
62
+ }
58
63
  if (!matchMode) {
59
64
  return [{ response: 'Invalid match mode. Use contains or exact.' }];
60
65
  }
@@ -53,52 +53,53 @@ describe('gemini/deep-research-result', () => {
53
53
  structuredTurnsTrusted: true,
54
54
  });
55
55
  });
56
+ const runCommand = (kwargs) => deepResearchResultCommand.func(page, { timeout: 120, ...kwargs });
56
57
  it('uses latest conversation when query is empty', async () => {
57
- const result = await deepResearchResultCommand.func(page, { query: ' ' });
58
+ const result = await runCommand({ query: ' ' });
58
59
  expect(page.goto).toHaveBeenCalledWith('https://gemini.google.com/app/abc', { waitUntil: 'load', settleMs: 2500 });
59
60
  expect(result).toEqual([{ response: 'https://files.example.com/report.md' }]);
60
61
  });
61
62
  it('falls back to current page response when query is empty and sidebar has no conversations', async () => {
62
63
  mockGetGeminiConversationList.mockResolvedValue([]);
63
64
  mockResolveGeminiConversationForQuery.mockReturnValue(null);
64
- const result = await deepResearchResultCommand.func(page, { query: '' });
65
+ const result = await runCommand({ query: '' });
65
66
  expect(page.goto).not.toHaveBeenCalled();
66
67
  expect(result).toEqual([{ response: 'https://files.example.com/report.md' }]);
67
68
  });
68
69
  it('returns a validation message when match mode is invalid', async () => {
69
- const result = await deepResearchResultCommand.func(page, { query: 'A', match: 'prefix' });
70
+ const result = await runCommand({ query: 'A', match: 'prefix' });
70
71
  expect(result).toEqual([{ response: 'Invalid match mode. Use contains or exact.' }]);
71
72
  });
72
73
  it('returns a signed-out message when Gemini page state indicates logged out', async () => {
73
74
  mockGetGeminiPageState.mockResolvedValue({ isSignedIn: false });
74
- const result = await deepResearchResultCommand.func(page, { query: 'A' });
75
+ const result = await runCommand({ query: 'A' });
75
76
  expect(result).toEqual([{ response: 'Not signed in to Gemini.' }]);
76
77
  });
77
78
  it('opens matched conversation by URL and returns exported report url', async () => {
78
- const result = await deepResearchResultCommand.func(page, { query: 'A title', match: 'exact' });
79
+ const result = await runCommand({ query: 'A title', match: 'exact' });
79
80
  expect(page.goto).toHaveBeenCalledWith('https://gemini.google.com/app/abc', { waitUntil: 'load', settleMs: 2500 });
80
81
  expect(result).toEqual([{ response: 'https://files.example.com/report.md' }]);
81
82
  });
82
83
  it('accepts a direct conversation URL and reads response from that page', async () => {
83
84
  const url = 'https://gemini.google.com/app/direct-id';
84
- const result = await deepResearchResultCommand.func(page, { query: url, match: 'contains' });
85
+ const result = await runCommand({ query: url, match: 'contains' });
85
86
  expect(page.goto).toHaveBeenCalledWith(url, { waitUntil: 'load', settleMs: 2500 });
86
87
  expect(result).toEqual([{ response: 'https://files.example.com/report.md' }]);
87
88
  });
88
89
  it('passes query and mode into resolveGeminiConversationForQuery', async () => {
89
- const result = await deepResearchResultCommand.func(page, { query: 'title', match: 'contains' });
90
+ const result = await runCommand({ query: 'title', match: 'contains' });
90
91
  expect(mockResolveGeminiConversationForQuery).toHaveBeenCalledWith([{ Title: 'A title', Url: 'https://gemini.google.com/app/abc' }], 'title', 'contains');
91
92
  expect(result).toEqual([{ response: 'https://files.example.com/report.md' }]);
92
93
  });
93
94
  it('falls back to click-by-title and returns not-found when click fails', async () => {
94
95
  mockResolveGeminiConversationForQuery.mockReturnValue(null);
95
96
  mockClickGeminiConversationByTitle.mockResolvedValue(false);
96
- const result = await deepResearchResultCommand.func(page, { query: 'missing', match: 'contains' });
97
+ const result = await runCommand({ query: 'missing', match: 'contains' });
97
98
  expect(result).toEqual([{ response: 'No conversation matched: missing' }]);
98
99
  });
99
100
  it('returns pending message when export url is unavailable and completion is not confirmed', async () => {
100
101
  mockExportGeminiDeepResearchReport.mockResolvedValue({ url: '', source: 'none' });
101
- const result = await deepResearchResultCommand.func(page, { query: 'A title' });
102
+ const result = await runCommand({ query: 'A title' });
102
103
  expect(result).toEqual([{ response: 'Deep Research may still be running or preparing export. Please wait and retry later.' }]);
103
104
  });
104
105
  it('returns waiting message when deep research is still generating', async () => {
@@ -110,13 +111,13 @@ describe('gemini/deep-research-result', () => {
110
111
  isGenerating: true,
111
112
  structuredTurnsTrusted: true,
112
113
  });
113
- const result = await deepResearchResultCommand.func(page, { query: 'A title' });
114
+ const result = await runCommand({ query: 'A title' });
114
115
  expect(result).toEqual([{ response: 'Deep Research is still running. Please wait and retry later.' }]);
115
116
  });
116
117
  it('returns waiting message when assistant response indicates research in progress', async () => {
117
118
  mockExportGeminiDeepResearchReport.mockResolvedValue({ url: '', source: 'none' });
118
119
  mockGetLatestGeminiAssistantResponse.mockResolvedValue('正在研究中,请稍候。');
119
- const result = await deepResearchResultCommand.func(page, { query: 'A title' });
120
+ const result = await runCommand({ query: 'A title' });
120
121
  expect(result).toEqual([{ response: 'Deep Research is still running. Please wait and retry later.' }]);
121
122
  });
122
123
  it('returns waiting message when transcript indicates in-progress status', async () => {
@@ -129,7 +130,7 @@ describe('gemini/deep-research-result', () => {
129
130
  isGenerating: false,
130
131
  structuredTurnsTrusted: true,
131
132
  });
132
- const result = await deepResearchResultCommand.func(page, { query: 'A title' });
133
+ const result = await runCommand({ query: 'A title' });
133
134
  expect(result).toEqual([{ response: 'Deep Research is still running. Please wait and retry later.' }]);
134
135
  });
135
136
  it('returns no-docs message when text indicates completed state', async () => {
@@ -142,13 +143,13 @@ describe('gemini/deep-research-result', () => {
142
143
  isGenerating: false,
143
144
  structuredTurnsTrusted: true,
144
145
  });
145
- const result = await deepResearchResultCommand.func(page, { query: 'A title' });
146
+ const result = await runCommand({ query: 'A title' });
146
147
  expect(result).toEqual([{ response: 'No Docs URL found. Please check Share & Export -> Export to Docs in Gemini UI.' }]);
147
148
  });
148
149
  it('returns pending message when assistant response is empty', async () => {
149
150
  mockExportGeminiDeepResearchReport.mockResolvedValue({ url: '', source: 'none' });
150
151
  mockGetLatestGeminiAssistantResponse.mockResolvedValue('');
151
- const result = await deepResearchResultCommand.func(page, { query: 'A title' });
152
+ const result = await runCommand({ query: 'A title' });
152
153
  expect(result).toEqual([{ response: 'Deep Research may still be running or preparing export. Please wait and retry later.' }]);
153
154
  });
154
155
  });
@@ -1,5 +1,6 @@
1
1
  import { cli, Strategy } from '@jackwener/opencli/registry';
2
- import { GEMINI_DEEP_RESEARCH_DEFAULT_CONFIRM_LABELS, GEMINI_DEEP_RESEARCH_DEFAULT_TOOL_LABELS, GEMINI_APP_URL, GEMINI_DOMAIN, getCurrentGeminiUrl, getLatestGeminiAssistantResponse, parseGeminiPositiveInt, readGeminiSnapshot, resolveGeminiLabels, selectGeminiTool, sendGeminiMessage, startNewGeminiChat, waitForGeminiSubmission, waitForGeminiConfirmButton, } from './utils.js';
2
+ import { ArgumentError } from '@jackwener/opencli/errors';
3
+ import { GEMINI_DEEP_RESEARCH_DEFAULT_CONFIRM_LABELS, GEMINI_DEEP_RESEARCH_DEFAULT_TOOL_LABELS, GEMINI_APP_URL, GEMINI_DOMAIN, getCurrentGeminiUrl, getLatestGeminiAssistantResponse, readGeminiSnapshot, resolveGeminiLabels, selectGeminiTool, sendGeminiMessage, startNewGeminiChat, waitForGeminiSubmission, waitForGeminiConfirmButton, } from './utils.js';
3
4
  function isGeminiRootAppUrl(url) {
4
5
  try {
5
6
  const parsed = new URL(url);
@@ -17,23 +18,27 @@ function parseDeepResearchProgress(text) {
17
18
  export const deepResearchCommand = cli({
18
19
  site: 'gemini',
19
20
  name: 'deep-research',
21
+ access: 'write',
20
22
  description: 'Start a Gemini Deep Research run and confirm it',
21
23
  domain: GEMINI_DOMAIN,
22
24
  strategy: Strategy.COOKIE,
23
25
  browser: true,
26
+ browserSession: { reuse: 'site' },
24
27
  navigateBefore: false,
25
28
  defaultFormat: 'plain',
26
- timeoutSeconds: 180,
27
29
  args: [
28
30
  { name: 'prompt', positional: true, required: true, help: 'Prompt to send' },
29
- { name: 'timeout', type: 'int', required: false, help: 'Max seconds to wait for confirm (default: 30)', default: 30 },
31
+ { name: 'timeout', type: 'int', required: false, help: 'Max seconds for the overall command (default: 180; confirm-wait clamps internally to 6-20s)', default: 180 },
30
32
  { name: 'tool', required: false, help: 'Override tool label (default: Deep Research)' },
31
33
  { name: 'confirm', required: false, help: 'Override confirm button label (default: Start research)' },
32
34
  ],
33
35
  columns: ['status', 'url'],
34
36
  func: async (page, kwargs) => {
35
37
  const prompt = kwargs.prompt;
36
- const timeout = parseGeminiPositiveInt(kwargs.timeout, 30);
38
+ const timeout = kwargs.timeout;
39
+ if (!Number.isInteger(timeout) || timeout < 1) {
40
+ throw new ArgumentError('--timeout must be a positive integer (seconds)');
41
+ }
37
42
  const submitTimeout = Math.min(Math.max(timeout, 6), 20);
38
43
  await startNewGeminiChat(page);
39
44
  const toolLabels = resolveGeminiLabels(kwargs.tool, GEMINI_DEEP_RESEARCH_DEFAULT_TOOL_LABELS);
@@ -28,10 +28,6 @@ vi.mock('./utils.js', () => ({
28
28
  '\u751f\u6210\u7814\u7a76\u8ba1\u5212',
29
29
  '\u751f\u6210\u8c03\u7814\u8ba1\u5212',
30
30
  ],
31
- parseGeminiPositiveInt: (value, fallback) => {
32
- const parsed = Number.parseInt(String(value ?? ''), 10);
33
- return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
34
- },
35
31
  resolveGeminiLabels: (value, fallback) => {
36
32
  const label = String(value ?? '').trim();
37
33
  return label ? [label] : fallback;
@@ -48,6 +44,7 @@ vi.mock('./utils.js', () => ({
48
44
  import { deepResearchCommand } from './deep-research.js';
49
45
  describe('gemini/deep-research', () => {
50
46
  const page = {};
47
+ const runCommand = (kwargs) => deepResearchCommand.func(page, { timeout: 180, ...kwargs });
51
48
  beforeEach(() => {
52
49
  vi.clearAllMocks();
53
50
  mockGetCurrentGeminiUrl.mockResolvedValue('https://gemini.google.com/app/chat');
@@ -71,17 +68,17 @@ describe('gemini/deep-research', () => {
71
68
  mockGetLatestGeminiAssistantResponse.mockResolvedValue('');
72
69
  });
73
70
  it('starts a new chat by default, then sends prompt and confirms deep research', async () => {
74
- const result = await deepResearchCommand.func(page, { prompt: 'research this topic' });
71
+ const result = await runCommand({ prompt: 'research this topic' });
75
72
  expect(mockStartNewGeminiChat).toHaveBeenCalledTimes(1);
76
73
  expect(mockSelectGeminiTool).toHaveBeenCalledTimes(1);
77
74
  expect(mockSendGeminiMessage).toHaveBeenCalledWith(page, 'research this topic');
78
75
  expect(mockWaitForGeminiSubmission).toHaveBeenCalledTimes(1);
79
- expect(mockWaitForGeminiConfirmButton).toHaveBeenCalledWith(page, expect.arrayContaining(['Start research', 'Start deep research', 'Generate research plan', '\u751f\u6210\u7814\u7a76\u8ba1\u5212']), 30);
76
+ expect(mockWaitForGeminiConfirmButton).toHaveBeenCalledWith(page, expect.arrayContaining(['Start research', 'Start deep research', 'Generate research plan', '\u751f\u6210\u7814\u7a76\u8ba1\u5212']), 180);
80
77
  expect(result).toEqual([{ status: 'started', url: 'https://gemini.google.com/app/chat' }]);
81
78
  });
82
79
  it('returns tool-not-found when the tool cannot be selected', async () => {
83
80
  mockSelectGeminiTool.mockResolvedValue('');
84
- const result = await deepResearchCommand.func(page, { prompt: 'research this topic' });
81
+ const result = await runCommand({ prompt: 'research this topic' });
85
82
  expect(result).toEqual([{ status: 'tool-not-found', url: 'https://gemini.google.com/app/chat' }]);
86
83
  expect(mockSendGeminiMessage).not.toHaveBeenCalled();
87
84
  expect(mockWaitForGeminiSubmission).not.toHaveBeenCalled();
@@ -94,7 +91,7 @@ describe('gemini/deep-research', () => {
94
91
  userAnchorTurn: null,
95
92
  reason: 'user_turn',
96
93
  });
97
- const result = await deepResearchCommand.func(page, { prompt: 'research this topic' });
94
+ const result = await runCommand({ prompt: 'research this topic' });
98
95
  expect(mockSelectGeminiTool).toHaveBeenCalledTimes(2);
99
96
  expect(mockReadGeminiSnapshot).toHaveBeenCalledTimes(2);
100
97
  expect(mockSendGeminiMessage).toHaveBeenCalledTimes(2);
@@ -103,7 +100,7 @@ describe('gemini/deep-research', () => {
103
100
  });
104
101
  it('returns submit-not-found when submission cannot be confirmed after retry', async () => {
105
102
  mockWaitForGeminiSubmission.mockResolvedValue(null);
106
- const result = await deepResearchCommand.func(page, { prompt: 'research this topic' });
103
+ const result = await runCommand({ prompt: 'research this topic' });
107
104
  expect(mockSelectGeminiTool).toHaveBeenCalledTimes(2);
108
105
  expect(mockSendGeminiMessage).toHaveBeenCalledTimes(2);
109
106
  expect(mockWaitForGeminiConfirmButton).not.toHaveBeenCalled();
@@ -111,27 +108,27 @@ describe('gemini/deep-research', () => {
111
108
  });
112
109
  it('returns confirm-not-found when no confirm button is found', async () => {
113
110
  mockWaitForGeminiConfirmButton.mockResolvedValue('');
114
- const result = await deepResearchCommand.func(page, { prompt: 'research this topic' });
111
+ const result = await runCommand({ prompt: 'research this topic' });
115
112
  expect(result).toEqual([{ status: 'confirm-not-found', url: 'https://gemini.google.com/app/chat' }]);
116
113
  });
117
114
  it('returns started when confirm is missing but research appears to be running', async () => {
118
115
  mockWaitForGeminiConfirmButton.mockResolvedValue('');
119
116
  mockGetCurrentGeminiUrl.mockResolvedValue('https://gemini.google.com/app/abc123');
120
117
  mockGetLatestGeminiAssistantResponse.mockResolvedValue('Researching websites now');
121
- const result = await deepResearchCommand.func(page, { prompt: 'research this topic' });
118
+ const result = await runCommand({ prompt: 'research this topic' });
122
119
  expect(result).toEqual([{ status: 'started', url: 'https://gemini.google.com/app/abc123' }]);
123
120
  });
124
121
  it('does not treat conversation url alone as started when confirm is missing', async () => {
125
122
  mockWaitForGeminiConfirmButton.mockResolvedValue('');
126
123
  mockGetCurrentGeminiUrl.mockResolvedValue('https://gemini.google.com/app/abc999');
127
124
  mockGetLatestGeminiAssistantResponse.mockResolvedValue('I drafted a plan. Start research');
128
- const result = await deepResearchCommand.func(page, { prompt: 'research this topic' });
125
+ const result = await runCommand({ prompt: 'research this topic' });
129
126
  expect(result).toEqual([{ status: 'confirm-not-found', url: 'https://gemini.google.com/app/abc999' }]);
130
127
  });
131
128
  it('retries once when stuck on root app URL and starts successfully on second confirm', async () => {
132
129
  mockWaitForGeminiConfirmButton.mockResolvedValueOnce('').mockResolvedValueOnce('Start research');
133
130
  mockGetCurrentGeminiUrl.mockResolvedValueOnce('https://gemini.google.com/app').mockResolvedValueOnce('https://gemini.google.com/app/retry123');
134
- const result = await deepResearchCommand.func(page, { prompt: 'research this topic', timeout: '20' });
131
+ const result = await runCommand({ prompt: 'research this topic', timeout: 20 });
135
132
  expect(mockSelectGeminiTool).toHaveBeenCalledTimes(2);
136
133
  expect(mockSendGeminiMessage).toHaveBeenCalledTimes(1);
137
134
  expect(mockWaitForGeminiConfirmButton).toHaveBeenCalledTimes(2);
@@ -140,7 +137,7 @@ describe('gemini/deep-research', () => {
140
137
  it('treats root-url confirm as false-positive and retries', async () => {
141
138
  mockWaitForGeminiConfirmButton.mockResolvedValueOnce('Start research').mockResolvedValueOnce('Start research');
142
139
  mockGetCurrentGeminiUrl.mockResolvedValueOnce('https://gemini.google.com/app').mockResolvedValueOnce('https://gemini.google.com/app/retry456');
143
- const result = await deepResearchCommand.func(page, { prompt: 'research this topic', timeout: '20' });
140
+ const result = await runCommand({ prompt: 'research this topic', timeout: 20 });
144
141
  expect(mockSelectGeminiTool).toHaveBeenCalledTimes(2);
145
142
  expect(mockSendGeminiMessage).toHaveBeenCalledTimes(1);
146
143
  expect(result).toEqual([{ status: 'started', url: 'https://gemini.google.com/app/retry456' }]);
@@ -149,7 +146,7 @@ describe('gemini/deep-research', () => {
149
146
  mockWaitForGeminiConfirmButton.mockResolvedValueOnce('Start research').mockResolvedValueOnce('');
150
147
  mockGetCurrentGeminiUrl.mockResolvedValueOnce('https://gemini.google.com/app').mockResolvedValueOnce('https://gemini.google.com/app');
151
148
  mockGetLatestGeminiAssistantResponse.mockResolvedValue('');
152
- const result = await deepResearchCommand.func(page, { prompt: 'research this topic', timeout: '20' });
149
+ const result = await runCommand({ prompt: 'research this topic', timeout: 20 });
153
150
  expect(mockSelectGeminiTool).toHaveBeenCalledTimes(2);
154
151
  expect(mockSendGeminiMessage).toHaveBeenCalledTimes(1);
155
152
  expect(mockWaitForGeminiConfirmButton).toHaveBeenCalledTimes(2);
@@ -165,18 +162,18 @@ describe('gemini/deep-research', () => {
165
162
  mockGetLatestGeminiAssistantResponse
166
163
  .mockResolvedValueOnce('I drafted a plan. Start research')
167
164
  .mockResolvedValueOnce('Researching websites now');
168
- const result = await deepResearchCommand.func(page, { prompt: 'research this topic', timeout: '20' });
165
+ const result = await runCommand({ prompt: 'research this topic', timeout: 20 });
169
166
  expect(mockWaitForGeminiConfirmButton).toHaveBeenCalledTimes(2);
170
167
  expect(mockWaitForGeminiConfirmButton).toHaveBeenNthCalledWith(2, page, expect.arrayContaining(['Start research', 'Start deep research', '开始研究', '开始深度研究']), 8);
171
168
  expect(mockSendGeminiMessage).toHaveBeenCalledTimes(1);
172
169
  expect(result).toEqual([{ status: 'started', url: 'https://gemini.google.com/app/xyz123' }]);
173
170
  });
174
171
  it('uses custom tool/confirm labels when provided', async () => {
175
- await deepResearchCommand.func(page, {
172
+ await runCommand({
176
173
  prompt: 'research this topic',
177
174
  tool: 'Custom Tool',
178
175
  confirm: 'Custom Confirm',
179
- timeout: '42',
176
+ timeout: 42,
180
177
  });
181
178
  expect(mockSelectGeminiTool).toHaveBeenCalledWith(page, ['Custom Tool']);
182
179
  expect(mockWaitForGeminiConfirmButton).toHaveBeenCalledWith(page, ['Custom Confirm'], 42);
@@ -2,6 +2,7 @@ import * as os from 'node:os';
2
2
  import * as path from 'node:path';
3
3
  import { cli, Strategy } from '@jackwener/opencli/registry';
4
4
  import { saveBase64ToFile } from '@jackwener/opencli/utils';
5
+ import { ArgumentError } from '@jackwener/opencli/errors';
5
6
  import { GEMINI_DOMAIN, exportGeminiImages, getGeminiVisibleImageUrls, sendGeminiMessage, startNewGeminiChat, waitForGeminiImages } from './utils.js';
6
7
  function extFromMime(mime) {
7
8
  if (mime.includes('png'))
@@ -46,19 +47,21 @@ async function currentGeminiLink(page) {
46
47
  export const imageCommand = cli({
47
48
  site: 'gemini',
48
49
  name: 'image',
50
+ access: 'write',
49
51
  description: 'Generate images with Gemini web and save them locally',
50
52
  domain: GEMINI_DOMAIN,
51
53
  strategy: Strategy.COOKIE,
52
54
  browser: true,
55
+ browserSession: { reuse: 'site' },
53
56
  navigateBefore: false,
54
57
  defaultFormat: 'plain',
55
- timeoutSeconds: 240,
56
58
  args: [
57
59
  { name: 'prompt', positional: true, required: true, help: 'Image prompt to send to Gemini' },
58
60
  { name: 'rt', default: '1:1', help: 'Ratio shorthand for aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3)' },
59
61
  { name: 'st', default: '', help: 'Style shorthand, e.g. anime, icon, watercolor' },
60
62
  { name: 'op', default: '~/tmp/gemini-images', help: 'Output directory shorthand' },
61
63
  { name: 'sd', type: 'boolean', default: false, help: 'Skip download shorthand; only show Gemini page link' },
64
+ { name: 'timeout', type: 'int', required: false, default: 240, help: 'Max seconds for the overall command (default: 240)' },
62
65
  ],
63
66
  columns: ['status', 'file', 'link'],
64
67
  func: async (page, kwargs) => {
@@ -66,7 +69,10 @@ export const imageCommand = cli({
66
69
  const ratio = normalizeRatio(String(kwargs.rt ?? '1:1'));
67
70
  const style = String(kwargs.st ?? '').trim();
68
71
  const outputDir = kwargs.op || path.join(os.homedir(), 'tmp', 'gemini-images');
69
- const timeout = 120;
72
+ const timeout = kwargs.timeout;
73
+ if (!Number.isInteger(timeout) || timeout < 1) {
74
+ throw new ArgumentError('--timeout must be a positive integer (seconds)');
75
+ }
70
76
  const startFresh = true;
71
77
  const skipDownloadRaw = kwargs.sd;
72
78
  const skipDownload = skipDownloadRaw === '' || skipDownloadRaw === true || normalizeBooleanFlag(skipDownloadRaw);
@@ -3,10 +3,12 @@ import { GEMINI_DOMAIN, startNewGeminiChat } from './utils.js';
3
3
  export const newCommand = cli({
4
4
  site: 'gemini',
5
5
  name: 'new',
6
+ access: 'read',
6
7
  description: 'Start a new conversation in Gemini web chat',
7
8
  domain: GEMINI_DOMAIN,
8
9
  strategy: Strategy.COOKIE,
9
10
  browser: true,
11
+ browserSession: { reuse: 'site' },
10
12
  navigateBefore: false,
11
13
  args: [],
12
14
  columns: ['Status', 'Action'],
@@ -78,10 +78,6 @@ export function resolveGeminiLabels(value, fallback) {
78
78
  const label = String(value ?? '').trim();
79
79
  return label ? [label] : fallback;
80
80
  }
81
- export function parseGeminiPositiveInt(value, fallback) {
82
- const parsed = Number.parseInt(String(value ?? ''), 10);
83
- return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
84
- }
85
81
  export function parseGeminiTitleMatchMode(value, fallback = 'contains') {
86
82
  const raw = String(value ?? fallback).trim().toLowerCase();
87
83
  if (raw === 'contains' || raw === 'exact')
@@ -64,6 +64,7 @@ function normalizeUrl(value) {
64
64
  cli({
65
65
  site: 'gitee',
66
66
  name: 'search',
67
+ access: 'read',
67
68
  description: 'Search repositories on Gitee',
68
69
  domain: 'gitee.com',
69
70
  strategy: Strategy.PUBLIC,