@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
@@ -1,5 +1,9 @@
1
1
  import { cli, Strategy } from '@jackwener/opencli/registry';
2
- import { ArgumentError, CommandExecutionError } from '@jackwener/opencli/errors';
2
+ import { ArgumentError, AuthRequiredError, CommandExecutionError } from '@jackwener/opencli/errors';
3
+ const LINKEDIN_DOMAIN = 'linkedin.com';
4
+ const MIN_LIMIT = 1;
5
+ const MAX_LIMIT = 100;
6
+ const MIN_START = 0;
3
7
  // ── Filter value mappings ──────────────────────────────────────────────
4
8
  const EXPERIENCE_LEVELS = {
5
9
  internship: '1',
@@ -66,6 +70,19 @@ function mapFilterValues(input, mapping, label) {
66
70
  function normalizeWhitespace(value) {
67
71
  return String(value ?? '').replace(/\s+/g, ' ').trim();
68
72
  }
73
+ function parseIntegerArg(value, label, fallback, min, max = Infinity) {
74
+ if (value === undefined || value === null || value === '')
75
+ return fallback;
76
+ const parsed = Number(value);
77
+ if (!Number.isFinite(parsed) || !Number.isInteger(parsed)) {
78
+ throw new ArgumentError(`${label} must be an integer, got ${JSON.stringify(value)}`);
79
+ }
80
+ if (parsed < min || parsed > max) {
81
+ const range = Number.isFinite(max) ? `between ${min} and ${max}` : `at least ${min}`;
82
+ throw new ArgumentError(`${label} must be ${range}, got ${parsed}`);
83
+ }
84
+ return parsed;
85
+ }
69
86
  function decodeLinkedinRedirect(url) {
70
87
  if (!url)
71
88
  return '';
@@ -120,6 +137,31 @@ function buildVoyagerUrl(input, offset, count) {
120
137
  .replace(/%29/gi, ')');
121
138
  return '/voyager/api/voyagerJobsDashJobCards?' + params.toString() + '&query=' + query + '&start=' + offset;
122
139
  }
140
+ function looksLinkedInAuthWallText(value) {
141
+ const text = String(value ?? '').replace(/\s+/g, ' ').trim().toLowerCase();
142
+ if (!text)
143
+ return false;
144
+ return /\b(sign in|log in|join linkedin)\b/.test(text) ||
145
+ /linkedin\.com\/(login|checkpoint|authwall)/i.test(text) ||
146
+ /\b(captcha|verification required)\b/.test(text) ||
147
+ /(请登录|登录领英|安全验证)/.test(text);
148
+ }
149
+ function buildLinkedInAuthProbeScript() {
150
+ return `(() => {
151
+ const text = [
152
+ window.location.href || '',
153
+ document.title || '',
154
+ document.body ? (document.body.innerText || '').slice(0, 4000) : '',
155
+ ].join('\\n');
156
+ return ${looksLinkedInAuthWallText.toString()}(text);
157
+ })()`;
158
+ }
159
+ async function assertLinkedInAuthenticated(page, context) {
160
+ const authRequired = await page.evaluate(buildLinkedInAuthProbeScript());
161
+ if (authRequired) {
162
+ throw new AuthRequiredError(LINKEDIN_DOMAIN, `${context} requires an active signed-in LinkedIn browser session`);
163
+ }
164
+ }
123
165
  // ── Company ID resolution (requires DOM interaction) ──────────────────
124
166
  async function resolveCompanyIds(page, input) {
125
167
  const rawValues = parseCsvArg(input);
@@ -209,13 +251,25 @@ async function fetchJobCards(page, input) {
209
251
  const batch = await page.evaluate(`(async () => {
210
252
  const jsession = document.cookie.split(';').map(p => p.trim())
211
253
  .find(p => p.startsWith('JSESSIONID='))?.slice('JSESSIONID='.length);
212
- if (!jsession) return { error: 'LinkedIn JSESSIONID cookie not found. Please sign in to LinkedIn in the browser.' };
254
+ if (!jsession) {
255
+ return {
256
+ authRequired: true,
257
+ error: 'LinkedIn JSESSIONID cookie not found. Please sign in to LinkedIn in the browser.'
258
+ };
259
+ }
213
260
 
214
261
  const csrf = jsession.replace(/^"|"$/g, '');
215
262
  const res = await fetch(${JSON.stringify(apiPath)}, {
216
263
  credentials: 'include',
217
264
  headers: { 'csrf-token': csrf, 'x-restli-protocol-version': '2.0.0' },
218
265
  });
266
+ if (res.status === 401 || res.status === 403) {
267
+ const text = await res.text();
268
+ return {
269
+ authRequired: true,
270
+ error: 'LinkedIn API authentication failed: HTTP ' + res.status + ' ' + text.slice(0, 200)
271
+ };
272
+ }
219
273
  if (!res.ok) {
220
274
  const text = await res.text();
221
275
  return { error: 'LinkedIn API error: HTTP ' + res.status + ' ' + text.slice(0, 200) };
@@ -223,6 +277,9 @@ async function fetchJobCards(page, input) {
223
277
  return res.json();
224
278
  })()`);
225
279
  if (!batch || batch.error) {
280
+ if (batch?.authRequired) {
281
+ throw new AuthRequiredError(LINKEDIN_DOMAIN, batch.error);
282
+ }
226
283
  throw new CommandExecutionError(batch?.error || 'LinkedIn search returned an unexpected response');
227
284
  }
228
285
  const elements = Array.isArray(batch?.elements) ? batch.elements : [];
@@ -259,17 +316,31 @@ async function fetchJobCards(page, input) {
259
316
  }));
260
317
  }
261
318
  // ── Job detail enrichment (--details flag) ────────────────────────────
319
+ //
320
+ // Per-row failures should NOT abort the whole list (--details enriches N rows;
321
+ // partial failure is expected). But silent empty-string fields hide the failure
322
+ // from callers — previously `catch {}` and the `if (!job.url)` early-return
323
+ // both produced indistinguishable `description: '', apply_url: ''` payloads,
324
+ // so users could not tell "fetch failed" from "upstream had no description".
325
+ //
326
+ // The fix: surface `null` instead of `''` for missing/failed rows, set
327
+ // `detail_error` to a short reason ("no url" / "fetch failed: <message>" /
328
+ // "missing description"), and log every failure to stderr with the offending
329
+ // URL so debugging is possible. Successful rows have `detail_error: null`.
262
330
  async function enrichJobDetails(page, jobs) {
263
331
  const enriched = [];
264
332
  for (let i = 0; i < jobs.length; i++) {
265
333
  const job = jobs[i];
266
334
  console.error(`[opencli:linkedin] Fetching details ${i + 1}/${jobs.length}: ${job.title}`);
267
335
  if (!job.url) {
268
- enriched.push({ ...job, description: '', apply_url: '' });
336
+ const reason = 'no url';
337
+ console.error(`[opencli:linkedin] Skipping detail for "${job.title}": ${reason}`);
338
+ enriched.push({ ...job, description: null, apply_url: null, detail_error: reason });
269
339
  continue;
270
340
  }
271
341
  try {
272
342
  await page.goto(job.url);
343
+ await assertLinkedInAuthenticated(page, 'LinkedIn job detail');
273
344
  await page.wait({ text: 'About the job', timeout: 8 });
274
345
  // Expand "Show more" button if present
275
346
  await page.evaluate(`(() => {
@@ -301,14 +372,25 @@ async function enrichJobDetails(page, jobs) {
301
372
 
302
373
  return { description, applyUrl: applyLink?.href || '' };
303
374
  })()`);
375
+ const description = normalizeWhitespace(detail?.description);
376
+ const apply_url = decodeLinkedinRedirect(String(detail?.applyUrl ?? ''));
377
+ // Empty description after a successful fetch is itself a
378
+ // recognizable signal — surface it via detail_error instead of
379
+ // silently emitting an empty string.
380
+ const detail_error = description ? null : 'missing description';
304
381
  enriched.push({
305
382
  ...job,
306
- description: normalizeWhitespace(detail?.description),
307
- apply_url: decodeLinkedinRedirect(String(detail?.applyUrl ?? '')),
383
+ description: description || null,
384
+ apply_url: apply_url || null,
385
+ detail_error,
308
386
  });
309
387
  }
310
- catch {
311
- enriched.push({ ...job, description: '', apply_url: '' });
388
+ catch (err) {
389
+ if (err instanceof AuthRequiredError)
390
+ throw err;
391
+ const reason = `fetch failed: ${err?.message || err}`;
392
+ console.error(`[opencli:linkedin] Detail fetch failed for ${job.url}: ${reason}`);
393
+ enriched.push({ ...job, description: null, apply_url: null, detail_error: reason });
312
394
  }
313
395
  }
314
396
  return enriched;
@@ -317,9 +399,10 @@ async function enrichJobDetails(page, jobs) {
317
399
  cli({
318
400
  site: 'linkedin',
319
401
  name: 'search',
402
+ access: 'read',
320
403
  description: 'Search LinkedIn jobs',
321
404
  domain: 'www.linkedin.com',
322
- strategy: Strategy.HEADER,
405
+ strategy: Strategy.COOKIE,
323
406
  browser: true,
324
407
  args: [
325
408
  { name: 'query', type: 'string', required: true, positional: true, help: 'Job search keywords' },
@@ -335,8 +418,8 @@ cli({
335
418
  ],
336
419
  columns: ['rank', 'title', 'company', 'location', 'listed', 'salary', 'url'],
337
420
  func: async (page, kwargs) => {
338
- const limit = Math.max(1, Math.min(kwargs.limit ?? 10, 100));
339
- const start = Math.max(0, kwargs.start ?? 0);
421
+ const limit = parseIntegerArg(kwargs.limit, '--limit', 10, MIN_LIMIT, MAX_LIMIT);
422
+ const start = parseIntegerArg(kwargs.start, '--start', 0, MIN_START);
340
423
  const includeDetails = Boolean(kwargs.details);
341
424
  const location = (kwargs.location ?? '').trim();
342
425
  const keywords = String(kwargs.query ?? '').trim();
@@ -346,6 +429,7 @@ cli({
346
429
  if (location)
347
430
  searchParams.set('location', location);
348
431
  await page.goto(`https://www.linkedin.com/jobs/search/?${searchParams.toString()}`);
432
+ await assertLinkedInAuthenticated(page, 'LinkedIn search');
349
433
  await page.wait({ text: 'Jobs', timeout: 10 });
350
434
  const companyIds = await resolveCompanyIds(page, kwargs.company);
351
435
  const input = {
@@ -365,3 +449,17 @@ cli({
365
449
  return enrichJobDetails(page, data);
366
450
  },
367
451
  });
452
+
453
+ export const __test__ = {
454
+ parseCsvArg,
455
+ parseIntegerArg,
456
+ mapFilterValues,
457
+ decodeLinkedinRedirect,
458
+ looksLinkedInAuthWallText,
459
+ assertLinkedInAuthenticated,
460
+ enrichJobDetails,
461
+ EXPERIENCE_LEVELS,
462
+ JOB_TYPES,
463
+ DATE_POSTED,
464
+ REMOTE_TYPES,
465
+ };
@@ -0,0 +1,222 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { getRegistry } from '@jackwener/opencli/registry';
3
+ import { ArgumentError, AuthRequiredError } from '@jackwener/opencli/errors';
4
+ import { __test__ } from './search.js';
5
+
6
+ const {
7
+ parseCsvArg,
8
+ parseIntegerArg,
9
+ mapFilterValues,
10
+ decodeLinkedinRedirect,
11
+ looksLinkedInAuthWallText,
12
+ enrichJobDetails,
13
+ EXPERIENCE_LEVELS,
14
+ JOB_TYPES,
15
+ DATE_POSTED,
16
+ REMOTE_TYPES,
17
+ } = __test__;
18
+
19
+ const getSearchCommand = () => getRegistry().get('linkedin/search');
20
+
21
+ describe('linkedin parseCsvArg', () => {
22
+ it('returns empty array for empty / null / undefined', () => {
23
+ expect(parseCsvArg(undefined)).toEqual([]);
24
+ expect(parseCsvArg(null)).toEqual([]);
25
+ expect(parseCsvArg('')).toEqual([]);
26
+ });
27
+
28
+ it('splits and trims comma-separated values', () => {
29
+ expect(parseCsvArg('full-time, contract')).toEqual(['full-time', 'contract']);
30
+ expect(parseCsvArg(' a , b , , c ')).toEqual(['a', 'b', 'c']);
31
+ });
32
+ });
33
+
34
+ describe('linkedin mapFilterValues', () => {
35
+ it('maps known values to upstream codes and dedupes', () => {
36
+ expect(mapFilterValues('full-time, contract, full', JOB_TYPES, 'job_type')).toEqual(['F', 'C']);
37
+ expect(mapFilterValues('remote, hybrid', REMOTE_TYPES, 'remote')).toEqual(['2', '3']);
38
+ });
39
+
40
+ it('throws ArgumentError on unknown filter values (no silent drop)', () => {
41
+ expect(() => mapFilterValues('martian', JOB_TYPES, 'job_type')).toThrow(ArgumentError);
42
+ expect(() => mapFilterValues('full-time, ufo', JOB_TYPES, 'job_type')).toThrow(ArgumentError);
43
+ });
44
+
45
+ it('returns empty array for empty input', () => {
46
+ expect(mapFilterValues('', EXPERIENCE_LEVELS, 'experience_level')).toEqual([]);
47
+ expect(mapFilterValues(undefined, DATE_POSTED, 'date_posted')).toEqual([]);
48
+ });
49
+ });
50
+
51
+ describe('linkedin argument validation', () => {
52
+ it('rejects --limit outside 1..100 instead of silently clamping', () => {
53
+ expect(() => parseIntegerArg(0, '--limit', 10, 1, 100)).toThrow(ArgumentError);
54
+ expect(() => parseIntegerArg(101, '--limit', 10, 1, 100)).toThrow(ArgumentError);
55
+ expect(() => parseIntegerArg('10.5', '--limit', 10, 1, 100)).toThrow(ArgumentError);
56
+ });
57
+
58
+ it('rejects negative --start instead of silently clamping to zero', () => {
59
+ expect(() => parseIntegerArg(-1, '--start', 0, 0)).toThrow(ArgumentError);
60
+ expect(parseIntegerArg(undefined, '--start', 0, 0)).toBe(0);
61
+ expect(parseIntegerArg('25', '--start', 0, 0)).toBe(25);
62
+ });
63
+
64
+ it('validates command args before browser navigation', async () => {
65
+ const command = getSearchCommand();
66
+ const page = { goto: vi.fn(), wait: vi.fn(), evaluate: vi.fn() };
67
+
68
+ await expect(command.func(page, { query: 'engineer', limit: 0 })).rejects.toBeInstanceOf(ArgumentError);
69
+ await expect(command.func(page, { query: 'engineer', start: -1 })).rejects.toBeInstanceOf(ArgumentError);
70
+ expect(page.goto).not.toHaveBeenCalled();
71
+ });
72
+ });
73
+
74
+ describe('linkedin auth wall detection', () => {
75
+ it('recognizes login/authwall signals', () => {
76
+ expect(looksLinkedInAuthWallText('https://www.linkedin.com/authwall?trk=guest Sign in to continue')).toBe(true);
77
+ expect(looksLinkedInAuthWallText('LinkedIn Login, Sign in')).toBe(true);
78
+ expect(looksLinkedInAuthWallText('About the job Senior infrastructure engineer')).toBe(false);
79
+ });
80
+
81
+ it('throws AuthRequiredError when search lands on a login wall', async () => {
82
+ const command = getSearchCommand();
83
+ const page = {
84
+ goto: vi.fn().mockResolvedValue(undefined),
85
+ wait: vi.fn().mockResolvedValue(undefined),
86
+ evaluate: vi.fn().mockResolvedValue(true),
87
+ };
88
+
89
+ await expect(command.func(page, { query: 'engineer', limit: 5 })).rejects.toBeInstanceOf(AuthRequiredError);
90
+ });
91
+ });
92
+
93
+ describe('linkedin decodeLinkedinRedirect', () => {
94
+ it('extracts the underlying url from a /redir/redirect/ wrapper', () => {
95
+ const target = 'https://example.com/jobs/apply?id=42';
96
+ const wrapped = `https://www.linkedin.com/redir/redirect/?url=${encodeURIComponent(target)}&source=jobs`;
97
+ expect(decodeLinkedinRedirect(wrapped)).toBe(target);
98
+ });
99
+
100
+ it('returns the input unchanged for non-redirect urls', () => {
101
+ const direct = 'https://example.com/jobs/42/';
102
+ expect(decodeLinkedinRedirect(direct)).toBe(direct);
103
+ });
104
+
105
+ it('returns empty string for falsy input', () => {
106
+ expect(decodeLinkedinRedirect('')).toBe('');
107
+ expect(decodeLinkedinRedirect(null)).toBe('');
108
+ });
109
+ });
110
+
111
+ describe('linkedin enrichJobDetails (silent failure fix)', () => {
112
+ function makeFakePage({ evaluateResults = [], gotoFails = [], evaluateFails = [] } = {}) {
113
+ let evalCall = 0;
114
+ let gotoCall = 0;
115
+ return {
116
+ goto: vi.fn(async () => {
117
+ if (gotoFails[gotoCall++]) {
118
+ throw new Error(gotoFails[gotoCall - 1]);
119
+ }
120
+ }),
121
+ wait: vi.fn(async () => undefined),
122
+ evaluate: vi.fn(async () => {
123
+ const idx = evalCall++;
124
+ if (evaluateFails[idx]) throw new Error(evaluateFails[idx]);
125
+ return evaluateResults[idx];
126
+ }),
127
+ };
128
+ }
129
+
130
+ it('surfaces detail_error="no url" when row has no URL (instead of silent empty string)', async () => {
131
+ const page = makeFakePage();
132
+ const out = await enrichJobDetails(page, [
133
+ { rank: 1, title: 'No URL Job', company: 'X', url: '' },
134
+ ]);
135
+ expect(out).toHaveLength(1);
136
+ expect(out[0]).toMatchObject({
137
+ description: null,
138
+ apply_url: null,
139
+ detail_error: 'no url',
140
+ });
141
+ expect(page.goto).not.toHaveBeenCalled();
142
+ });
143
+
144
+ it('surfaces detail_error="fetch failed: ..." when goto throws (no silent swallow)', async () => {
145
+ const page = makeFakePage({ gotoFails: ['network down'] });
146
+ const out = await enrichJobDetails(page, [
147
+ { rank: 1, title: 'Fetch Fail', company: 'X', url: 'https://www.linkedin.com/jobs/view/1' },
148
+ ]);
149
+ expect(out).toHaveLength(1);
150
+ expect(out[0].description).toBeNull();
151
+ expect(out[0].apply_url).toBeNull();
152
+ expect(out[0].detail_error).toMatch(/^fetch failed: .*network down/);
153
+ });
154
+
155
+ it('surfaces detail_error="missing description" on empty description (signals upstream gap, not crash)', async () => {
156
+ const page = makeFakePage({
157
+ evaluateResults: [
158
+ false, // auth wall probe
159
+ undefined, // expand-show-more click result (ignored)
160
+ { description: '', applyUrl: '' },
161
+ ],
162
+ });
163
+ const out = await enrichJobDetails(page, [
164
+ { rank: 1, title: 'Empty Desc', company: 'X', url: 'https://www.linkedin.com/jobs/view/2' },
165
+ ]);
166
+ expect(out[0].description).toBeNull();
167
+ expect(out[0].apply_url).toBeNull();
168
+ expect(out[0].detail_error).toBe('missing description');
169
+ });
170
+
171
+ it('surfaces detail_error=null on a fully successful enrichment', async () => {
172
+ const page = makeFakePage({
173
+ evaluateResults: [
174
+ false, // auth wall probe
175
+ undefined,
176
+ { description: ' An interesting role ', applyUrl: 'https://example.com/apply' },
177
+ ],
178
+ });
179
+ const out = await enrichJobDetails(page, [
180
+ { rank: 1, title: 'OK', company: 'X', url: 'https://www.linkedin.com/jobs/view/3' },
181
+ ]);
182
+ expect(out[0]).toMatchObject({
183
+ description: 'An interesting role',
184
+ apply_url: 'https://example.com/apply',
185
+ detail_error: null,
186
+ });
187
+ });
188
+
189
+ it('processes multiple rows with mixed outcomes without aborting the batch', async () => {
190
+ const page = makeFakePage({
191
+ evaluateResults: [
192
+ // Row 1 (success)
193
+ false,
194
+ undefined,
195
+ { description: 'Good', applyUrl: 'https://a.example/' },
196
+ // Row 3 (success — row 2 had no URL so didn't navigate)
197
+ false,
198
+ undefined,
199
+ { description: 'Also good', applyUrl: 'https://b.example/' },
200
+ ],
201
+ });
202
+ const out = await enrichJobDetails(page, [
203
+ { rank: 1, title: 'A', company: 'X', url: 'https://www.linkedin.com/jobs/view/10' },
204
+ { rank: 2, title: 'B', company: 'X', url: '' },
205
+ { rank: 3, title: 'C', company: 'X', url: 'https://www.linkedin.com/jobs/view/30' },
206
+ ]);
207
+ expect(out).toHaveLength(3);
208
+ expect(out[0].detail_error).toBeNull();
209
+ expect(out[1].detail_error).toBe('no url');
210
+ expect(out[2].detail_error).toBeNull();
211
+ // Goto was only called for rows with URL (1 and 3)
212
+ expect(page.goto).toHaveBeenCalledTimes(2);
213
+ });
214
+
215
+ it('throws AuthRequiredError on detail auth wall instead of burying it in detail_error', async () => {
216
+ const page = makeFakePage({ evaluateResults: [true] });
217
+
218
+ await expect(enrichJobDetails(page, [
219
+ { rank: 1, title: 'Needs Auth', company: 'X', url: 'https://www.linkedin.com/jobs/view/4' },
220
+ ])).rejects.toBeInstanceOf(AuthRequiredError);
221
+ });
222
+ });
@@ -460,6 +460,7 @@ async function extractVisiblePosts(page) {
460
460
  cli({
461
461
  site: 'linkedin',
462
462
  name: 'timeline',
463
+ access: 'read',
463
464
  description: 'Read LinkedIn home timeline posts',
464
465
  domain: 'www.linkedin.com',
465
466
  strategy: Strategy.COOKIE,
@@ -3,6 +3,7 @@ import { fetchLinuxDoJson } from './feed.js';
3
3
  cli({
4
4
  site: 'linux-do',
5
5
  name: 'categories',
6
+ access: 'read',
6
7
  description: 'linux.do 分类列表',
7
8
  domain: 'linux.do',
8
9
  strategy: Strategy.COOKIE,
@@ -355,26 +355,24 @@ export const LINUX_DO_FEED_ARGS = [
355
355
  choices: ['all', 'daily', 'weekly', 'monthly', 'quarterly', 'yearly'],
356
356
  },
357
357
  ];
358
- export async function executeLinuxDoFeed(page, kwargs) {
358
+ async function runLinuxDoFeed(page, kwargs) {
359
359
  const limit = (kwargs.limit || 20);
360
360
  await ensureLinuxDoHome(page);
361
361
  const request = await resolveFeedRequest(page, kwargs);
362
362
  const data = await fetchLinuxDoJson(page, request.url, { skipNavigate: true });
363
363
  return topicListRichFromJson(data, limit);
364
364
  }
365
- export function buildLinuxDoCompatFooter(replacement) {
366
- return `Deprecated compatibility command. Prefer: ${replacement}`;
367
- }
368
365
  cli({
369
366
  site: 'linux-do',
370
367
  name: 'feed',
368
+ access: 'read',
371
369
  description: 'linux.do 话题列表(需登录;支持全站、标签、分类)',
372
370
  domain: 'linux.do',
373
371
  strategy: Strategy.COOKIE,
374
372
  browser: true,
375
373
  columns: ['title', 'replies', 'created', 'likes', 'views', 'url'],
376
374
  args: LINUX_DO_FEED_ARGS,
377
- func: executeLinuxDoFeed,
375
+ func: runLinuxDoFeed,
378
376
  });
379
377
  export const __test__ = {
380
378
  resetMetadataCaches() {
@@ -7,6 +7,21 @@ describe('linux-do feed metadata resolution', () => {
7
7
  afterEach(() => {
8
8
  __test__.resetMetadataCaches();
9
9
  });
10
+ it('builds the replacement URL for legacy latest', async () => {
11
+ const request = await __test__.resolveFeedRequest(null, {
12
+ view: 'latest',
13
+ limit: 20,
14
+ });
15
+ expect(request.url).toBe('/latest.json?per_page=20');
16
+ });
17
+ it('builds the replacement URL for legacy hot weekly', async () => {
18
+ const request = await __test__.resolveFeedRequest(null, {
19
+ view: 'top',
20
+ period: 'weekly',
21
+ limit: 20,
22
+ });
23
+ expect(request.url).toBe('/top.json?per_page=20&period=weekly');
24
+ });
10
25
  it('prefers live tag metadata over the bundled snapshot', async () => {
11
26
  __test__.setLiveMetadataForTests({
12
27
  tags: [{ id: 9999, slug: 'fresh-tag', name: 'Fresh Tag' }],
@@ -86,6 +101,26 @@ describe('linux-do feed metadata resolution', () => {
86
101
  });
87
102
  expect(request.url).toBe('/c/parent/fresh-child/11.json?per_page=20');
88
103
  });
104
+ it('builds the replacement URL for legacy category id', async () => {
105
+ __test__.setLiveMetadataForTests({
106
+ categories: [
107
+ {
108
+ id: 4,
109
+ name: '开发调优',
110
+ description: '',
111
+ slug: 'develop',
112
+ parentCategoryId: null,
113
+ parent: null,
114
+ },
115
+ ],
116
+ });
117
+ const request = await __test__.resolveFeedRequest(null, {
118
+ category: '4',
119
+ view: 'latest',
120
+ limit: 20,
121
+ });
122
+ expect(request.url).toBe('/c/develop/4.json?per_page=20');
123
+ });
89
124
  it('falls back to cached metadata when live metadata is unavailable', async () => {
90
125
  const cacheDir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-linux-do-cache-'));
91
126
  __test__.setCacheDirForTests(cacheDir);
@@ -3,6 +3,7 @@ import { fetchLinuxDoJson } from './feed.js';
3
3
  cli({
4
4
  site: 'linux-do',
5
5
  name: 'search',
6
+ access: 'read',
6
7
  description: '搜索 linux.do',
7
8
  domain: 'linux.do',
8
9
  strategy: Strategy.COOKIE,
@@ -3,6 +3,7 @@ import { fetchLinuxDoJson } from './feed.js';
3
3
  cli({
4
4
  site: 'linux-do',
5
5
  name: 'tags',
6
+ access: 'read',
6
7
  description: 'linux.do 标签列表',
7
8
  domain: 'linux.do',
8
9
  strategy: Strategy.COOKIE,
@@ -10,7 +11,7 @@ cli({
10
11
  args: [
11
12
  { name: 'limit', type: 'int', default: 30, help: 'Number of tags' },
12
13
  ],
13
- columns: ['rank', 'name', 'count', 'url'],
14
+ columns: ['rank', 'name', 'slug', 'count', 'url'],
14
15
  func: async (page, kwargs) => {
15
16
  const data = await fetchLinuxDoJson(page, '/tags.json');
16
17
  const tags = (data?.tags || []);
@@ -128,6 +128,7 @@ async function fetchTopicPayload(page, id) {
128
128
  cli({
129
129
  site: 'linux-do',
130
130
  name: 'topic-content',
131
+ access: 'read',
131
132
  description: 'Get the main topic body as Markdown',
132
133
  domain: LINUX_DO_DOMAIN,
133
134
  strategy: Strategy.COOKIE,
@@ -30,6 +30,7 @@ function strip(html) {
30
30
  cli({
31
31
  site: 'linux-do',
32
32
  name: 'topic',
33
+ access: 'read',
33
34
  description: 'linux.do 帖子首页摘要和回复(首屏)',
34
35
  domain: 'linux.do',
35
36
  strategy: Strategy.COOKIE,
@@ -30,6 +30,7 @@ function strip(html) {
30
30
  cli({
31
31
  site: 'linux-do',
32
32
  name: 'user-posts',
33
+ access: 'read',
33
34
  description: 'linux.do 用户的帖子',
34
35
  domain: 'linux.do',
35
36
  strategy: Strategy.COOKIE,
@@ -9,6 +9,7 @@ function toLocalTime(utcStr) {
9
9
  cli({
10
10
  site: 'linux-do',
11
11
  name: 'user-topics',
12
+ access: 'read',
12
13
  description: 'linux.do 用户创建的话题',
13
14
  domain: 'linux.do',
14
15
  strategy: Strategy.COOKIE,
@@ -2,6 +2,7 @@ import { cli, Strategy } from '@jackwener/opencli/registry';
2
2
  cli({
3
3
  site: 'lobsters',
4
4
  name: 'active',
5
+ access: 'read',
5
6
  description: 'Lobste.rs most active discussions',
6
7
  domain: 'lobste.rs',
7
8
  strategy: Strategy.PUBLIC,
@@ -9,15 +10,17 @@ cli({
9
10
  args: [
10
11
  { name: 'limit', type: 'int', default: 20, help: 'Number of stories' },
11
12
  ],
12
- columns: ['rank', 'title', 'score', 'author', 'comments', 'tags'],
13
+ columns: ['rank', 'id', 'title', 'score', 'author', 'comments', 'created_at', 'tags', 'url'],
13
14
  pipeline: [
14
15
  { fetch: { url: 'https://lobste.rs/active.json' } },
15
16
  { map: {
16
17
  rank: '${{ index + 1 }}',
18
+ id: '${{ item.short_id }}',
17
19
  title: '${{ item.title }}',
18
20
  score: '${{ item.score }}',
19
21
  author: '${{ item.submitter_user }}',
20
22
  comments: '${{ item.comment_count }}',
23
+ created_at: '${{ item.created_at }}',
21
24
  tags: `\${{ item.tags | join(', ') }}`,
22
25
  url: '${{ item.comments_url }}',
23
26
  } },