@jackwener/opencli 1.7.10 → 1.7.12

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 (740) hide show
  1. package/README.md +3 -3
  2. package/README.zh-CN.md +5 -4
  3. package/cli-manifest.json +1443 -24
  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/36kr/article.js +1 -0
  10. package/clis/36kr/hot.js +1 -0
  11. package/clis/36kr/news.js +1 -0
  12. package/clis/36kr/search.js +1 -0
  13. package/clis/51job/company.js +1 -0
  14. package/clis/51job/detail.js +1 -0
  15. package/clis/51job/hot.js +1 -0
  16. package/clis/51job/search.js +1 -0
  17. package/clis/amazon/bestsellers.js +1 -0
  18. package/clis/amazon/discussion.js +1 -0
  19. package/clis/amazon/movers-shakers.js +1 -0
  20. package/clis/amazon/new-releases.js +1 -0
  21. package/clis/amazon/offer.js +1 -0
  22. package/clis/amazon/product.js +1 -0
  23. package/clis/amazon/rankings.js +1 -0
  24. package/clis/amazon/search.js +1 -0
  25. package/clis/antigravity/dump.js +1 -0
  26. package/clis/antigravity/extract-code.js +1 -0
  27. package/clis/antigravity/model.js +1 -0
  28. package/clis/antigravity/new.js +1 -0
  29. package/clis/antigravity/read.js +1 -0
  30. package/clis/antigravity/send.js +1 -0
  31. package/clis/antigravity/status.js +1 -0
  32. package/clis/antigravity/watch.js +1 -0
  33. package/clis/apple-podcasts/episodes.js +1 -0
  34. package/clis/apple-podcasts/search.js +1 -0
  35. package/clis/apple-podcasts/top.js +1 -0
  36. package/clis/arxiv/arxiv.test.js +112 -0
  37. package/clis/arxiv/paper.js +4 -3
  38. package/clis/arxiv/recent.js +33 -0
  39. package/clis/arxiv/search.js +19 -7
  40. package/clis/arxiv/utils.js +68 -5
  41. package/clis/baidu-scholar/search.js +1 -0
  42. package/clis/band/bands.js +1 -0
  43. package/clis/band/mentions.js +1 -0
  44. package/clis/band/post.js +1 -0
  45. package/clis/band/posts.js +1 -0
  46. package/clis/barchart/flow.js +1 -0
  47. package/clis/barchart/greeks.js +1 -0
  48. package/clis/barchart/options.js +1 -0
  49. package/clis/barchart/quote.js +1 -0
  50. package/clis/bbc/news.js +1 -0
  51. package/clis/bilibili/comments.js +1 -0
  52. package/clis/bilibili/download.js +1 -0
  53. package/clis/bilibili/dynamic.js +1 -0
  54. package/clis/bilibili/favorite.js +1 -0
  55. package/clis/bilibili/feed.js +2 -0
  56. package/clis/bilibili/following.js +1 -0
  57. package/clis/bilibili/history.js +1 -0
  58. package/clis/bilibili/hot.js +6 -1
  59. package/clis/bilibili/hot.test.js +17 -0
  60. package/clis/bilibili/me.js +1 -1
  61. package/clis/bilibili/ranking.js +1 -0
  62. package/clis/bilibili/search.js +1 -1
  63. package/clis/bilibili/subtitle.js +1 -0
  64. package/clis/bilibili/user-videos.js +1 -0
  65. package/clis/bilibili/video.js +1 -0
  66. package/clis/binance/asks.js +1 -0
  67. package/clis/binance/depth.js +1 -0
  68. package/clis/binance/gainers.js +1 -0
  69. package/clis/binance/klines.js +1 -0
  70. package/clis/binance/losers.js +1 -0
  71. package/clis/binance/pairs.js +1 -0
  72. package/clis/binance/price.js +1 -0
  73. package/clis/binance/prices.js +1 -0
  74. package/clis/binance/ticker.js +1 -0
  75. package/clis/binance/top.js +1 -0
  76. package/clis/binance/trades.js +1 -0
  77. package/clis/bloomberg/businessweek.js +1 -0
  78. package/clis/bloomberg/economics.js +1 -0
  79. package/clis/bloomberg/feeds.js +1 -0
  80. package/clis/bloomberg/industries.js +1 -0
  81. package/clis/bloomberg/main.js +1 -0
  82. package/clis/bloomberg/markets.js +1 -0
  83. package/clis/bloomberg/news.js +1 -0
  84. package/clis/bloomberg/opinions.js +1 -0
  85. package/clis/bloomberg/politics.js +1 -0
  86. package/clis/bloomberg/tech.js +1 -0
  87. package/clis/bluesky/feeds.js +1 -0
  88. package/clis/bluesky/followers.js +1 -0
  89. package/clis/bluesky/following.js +1 -0
  90. package/clis/bluesky/profile.js +1 -0
  91. package/clis/bluesky/search.js +1 -0
  92. package/clis/bluesky/starter-packs.js +1 -0
  93. package/clis/bluesky/thread.js +1 -0
  94. package/clis/bluesky/trending.js +1 -0
  95. package/clis/bluesky/user.js +3 -1
  96. package/clis/boss/batchgreet.js +1 -0
  97. package/clis/boss/chatlist.js +1 -0
  98. package/clis/boss/chatmsg.js +1 -0
  99. package/clis/boss/detail.js +1 -0
  100. package/clis/boss/exchange.js +1 -0
  101. package/clis/boss/greet.js +1 -0
  102. package/clis/boss/invite.js +1 -0
  103. package/clis/boss/joblist.js +1 -0
  104. package/clis/boss/mark.js +1 -0
  105. package/clis/boss/recommend.js +1 -0
  106. package/clis/boss/resume.js +1 -0
  107. package/clis/boss/search.js +1 -0
  108. package/clis/boss/send.js +1 -0
  109. package/clis/boss/stats.js +1 -0
  110. package/clis/chaoxing/assignments.js +1 -0
  111. package/clis/chaoxing/exams.js +1 -0
  112. package/clis/chatgpt/image.js +1 -0
  113. package/clis/chatgpt-app/ask.js +1 -0
  114. package/clis/chatgpt-app/model.js +1 -0
  115. package/clis/chatgpt-app/new.js +1 -0
  116. package/clis/chatgpt-app/read.js +1 -0
  117. package/clis/chatgpt-app/send.js +1 -0
  118. package/clis/chatgpt-app/status.js +1 -0
  119. package/clis/chatwise/ask.js +1 -0
  120. package/clis/chatwise/export.js +1 -0
  121. package/clis/chatwise/history.js +1 -0
  122. package/clis/chatwise/model.js +1 -0
  123. package/clis/chatwise/read.js +1 -0
  124. package/clis/chatwise/send.js +1 -0
  125. package/clis/claude/ask.js +1 -0
  126. package/clis/claude/detail.js +1 -0
  127. package/clis/claude/history.js +1 -0
  128. package/clis/claude/new.js +1 -0
  129. package/clis/claude/read.js +1 -0
  130. package/clis/claude/send.js +1 -0
  131. package/clis/claude/status.js +1 -0
  132. package/clis/cnki/search.js +1 -0
  133. package/clis/codex/ask.js +1 -0
  134. package/clis/codex/export.js +1 -0
  135. package/clis/codex/extract-diff.js +1 -0
  136. package/clis/codex/history.js +1 -0
  137. package/clis/codex/model.js +1 -0
  138. package/clis/codex/read.js +1 -0
  139. package/clis/codex/send.js +1 -0
  140. package/clis/coupang/add-to-cart.js +1 -0
  141. package/clis/coupang/search.js +1 -0
  142. package/clis/ctrip/search.js +1 -0
  143. package/clis/cursor/ask.js +1 -0
  144. package/clis/cursor/composer.js +1 -0
  145. package/clis/cursor/export.js +1 -0
  146. package/clis/cursor/extract-code.js +1 -0
  147. package/clis/cursor/history.js +1 -0
  148. package/clis/cursor/model.js +1 -0
  149. package/clis/cursor/read.js +1 -0
  150. package/clis/cursor/send.js +1 -0
  151. package/clis/dblp/dblp.test.js +397 -0
  152. package/clis/dblp/paper.js +40 -0
  153. package/clis/dblp/search.js +45 -0
  154. package/clis/dblp/utils.js +290 -0
  155. package/clis/deepseek/ask.js +1 -0
  156. package/clis/deepseek/history.js +1 -0
  157. package/clis/deepseek/new.js +1 -0
  158. package/clis/deepseek/read.js +1 -0
  159. package/clis/deepseek/status.js +1 -0
  160. package/clis/devto/devto.test.js +236 -0
  161. package/clis/devto/read.js +103 -0
  162. package/clis/devto/tag.js +5 -1
  163. package/clis/devto/top.js +5 -1
  164. package/clis/devto/user.js +5 -1
  165. package/clis/dianping/__fixtures__/search.html +168 -0
  166. package/clis/dianping/__fixtures__/shop.html +6 -0
  167. package/clis/dianping/dianping.test.js +424 -0
  168. package/clis/dianping/search.js +154 -0
  169. package/clis/dianping/shop.js +173 -0
  170. package/clis/dianping/utils.js +157 -0
  171. package/clis/dictionary/examples.js +1 -0
  172. package/clis/dictionary/search.js +1 -0
  173. package/clis/dictionary/synonyms.js +1 -0
  174. package/clis/discord-app/channels.js +1 -0
  175. package/clis/discord-app/delete.js +1 -0
  176. package/clis/discord-app/members.js +1 -0
  177. package/clis/discord-app/read.js +1 -0
  178. package/clis/discord-app/search.js +1 -0
  179. package/clis/discord-app/send.js +1 -0
  180. package/clis/discord-app/servers.js +1 -0
  181. package/clis/discord-app/status.js +1 -0
  182. package/clis/douban/book-hot.js +1 -0
  183. package/clis/douban/download.js +1 -0
  184. package/clis/douban/marks.js +1 -0
  185. package/clis/douban/movie-hot.js +2 -1
  186. package/clis/douban/movie-hot.test.js +14 -0
  187. package/clis/douban/photos.js +2 -1
  188. package/clis/douban/reviews.js +1 -0
  189. package/clis/douban/search.js +1 -0
  190. package/clis/douban/subject.js +1 -0
  191. package/clis/douban/top250.js +1 -0
  192. package/clis/douban/utils.js +11 -13
  193. package/clis/douban/utils.test.js +79 -0
  194. package/clis/doubao/ask.js +1 -0
  195. package/clis/doubao/detail.js +1 -0
  196. package/clis/doubao/history.js +1 -0
  197. package/clis/doubao/meeting-summary.js +1 -0
  198. package/clis/doubao/meeting-transcript.js +1 -0
  199. package/clis/doubao/new.js +1 -0
  200. package/clis/doubao/read.js +1 -0
  201. package/clis/doubao/send.js +1 -0
  202. package/clis/doubao/status.js +1 -0
  203. package/clis/doubao-app/ask.js +1 -0
  204. package/clis/doubao-app/dump.js +1 -0
  205. package/clis/doubao-app/new.js +1 -0
  206. package/clis/doubao-app/read.js +1 -0
  207. package/clis/doubao-app/screenshot.js +1 -0
  208. package/clis/doubao-app/send.js +1 -0
  209. package/clis/doubao-app/status.js +1 -0
  210. package/clis/douyin/activities.js +1 -0
  211. package/clis/douyin/collections.js +1 -0
  212. package/clis/douyin/delete.js +1 -0
  213. package/clis/douyin/draft.js +1 -0
  214. package/clis/douyin/drafts.js +1 -0
  215. package/clis/douyin/hashtag.js +1 -0
  216. package/clis/douyin/location.js +1 -0
  217. package/clis/douyin/profile.js +1 -0
  218. package/clis/douyin/publish.js +1 -0
  219. package/clis/douyin/stats.js +1 -0
  220. package/clis/douyin/update.js +1 -0
  221. package/clis/douyin/user-videos.js +1 -0
  222. package/clis/douyin/videos.js +1 -0
  223. package/clis/eastmoney/announcement.js +1 -0
  224. package/clis/eastmoney/convertible.js +1 -0
  225. package/clis/eastmoney/etf.js +1 -0
  226. package/clis/eastmoney/holders.js +1 -0
  227. package/clis/eastmoney/hot-rank.js +1 -0
  228. package/clis/eastmoney/index-board.js +1 -0
  229. package/clis/eastmoney/kline.js +1 -0
  230. package/clis/eastmoney/kuaixun.js +1 -0
  231. package/clis/eastmoney/longhu.js +1 -0
  232. package/clis/eastmoney/money-flow.js +1 -0
  233. package/clis/eastmoney/northbound.js +1 -0
  234. package/clis/eastmoney/quote.js +1 -0
  235. package/clis/eastmoney/rank.js +1 -0
  236. package/clis/eastmoney/sectors.js +1 -0
  237. package/clis/facebook/add-friend.js +1 -0
  238. package/clis/facebook/events.js +1 -0
  239. package/clis/facebook/feed.js +1 -0
  240. package/clis/facebook/friends.js +1 -0
  241. package/clis/facebook/groups.js +1 -0
  242. package/clis/facebook/join-group.js +1 -0
  243. package/clis/facebook/marketplace-inbox.js +1 -0
  244. package/clis/facebook/marketplace-listings.js +1 -0
  245. package/clis/facebook/memories.js +1 -0
  246. package/clis/facebook/notifications.js +1 -0
  247. package/clis/facebook/profile.js +1 -0
  248. package/clis/facebook/search.js +1 -0
  249. package/clis/gemini/ask.js +1 -0
  250. package/clis/gemini/deep-research-result.js +1 -0
  251. package/clis/gemini/deep-research.js +1 -0
  252. package/clis/gemini/image.js +1 -0
  253. package/clis/gemini/new.js +1 -0
  254. package/clis/gitee/search.js +1 -0
  255. package/clis/gitee/trending.js +1 -0
  256. package/clis/gitee/user.js +1 -0
  257. package/clis/google/news.js +1 -0
  258. package/clis/google/search.js +1 -0
  259. package/clis/google/suggest.js +1 -0
  260. package/clis/google/trends.js +1 -0
  261. package/clis/google-scholar/cite.js +1 -0
  262. package/clis/google-scholar/profile.js +1 -0
  263. package/clis/google-scholar/search.js +1 -0
  264. package/clis/gov-law/recent.js +1 -0
  265. package/clis/gov-law/search.js +1 -0
  266. package/clis/gov-policy/recent.js +1 -0
  267. package/clis/gov-policy/search.js +1 -0
  268. package/clis/grok/ask.js +1 -0
  269. package/clis/grok/image.ts +1 -0
  270. package/clis/hackernews/ask.js +3 -1
  271. package/clis/hackernews/best.js +3 -1
  272. package/clis/hackernews/hackernews.test.js +132 -0
  273. package/clis/hackernews/jobs.js +3 -1
  274. package/clis/hackernews/new.js +3 -1
  275. package/clis/hackernews/read.js +188 -0
  276. package/clis/hackernews/search.js +3 -1
  277. package/clis/hackernews/show.js +3 -1
  278. package/clis/hackernews/top.js +3 -1
  279. package/clis/hackernews/user.js +1 -0
  280. package/clis/hf/top.js +1 -0
  281. package/clis/hupu/detail.js +1 -0
  282. package/clis/hupu/hot.js +3 -1
  283. package/clis/hupu/like.js +1 -0
  284. package/clis/hupu/mentions.js +2 -1
  285. package/clis/hupu/reply.js +1 -0
  286. package/clis/hupu/search.js +3 -1
  287. package/clis/hupu/unlike.js +1 -0
  288. package/clis/imdb/person.js +1 -0
  289. package/clis/imdb/reviews.js +1 -0
  290. package/clis/imdb/search.js +1 -0
  291. package/clis/imdb/title.js +1 -0
  292. package/clis/imdb/top.js +1 -0
  293. package/clis/imdb/trending.js +1 -0
  294. package/clis/indeed/indeed.test.js +375 -0
  295. package/clis/indeed/job.js +86 -0
  296. package/clis/indeed/search.js +110 -0
  297. package/clis/indeed/utils.js +152 -0
  298. package/clis/instagram/collection-create.js +1 -0
  299. package/clis/instagram/collection-delete.js +92 -0
  300. package/clis/instagram/comment.js +1 -0
  301. package/clis/instagram/download.js +1 -0
  302. package/clis/instagram/explore.js +1 -0
  303. package/clis/instagram/follow.js +1 -0
  304. package/clis/instagram/followers.js +1 -0
  305. package/clis/instagram/following.js +1 -0
  306. package/clis/instagram/like.js +1 -0
  307. package/clis/instagram/note.js +1 -0
  308. package/clis/instagram/post.js +1 -0
  309. package/clis/instagram/profile.js +1 -0
  310. package/clis/instagram/reel.js +1 -0
  311. package/clis/instagram/save.js +1 -0
  312. package/clis/instagram/saved.js +1 -0
  313. package/clis/instagram/search.js +1 -0
  314. package/clis/instagram/story.js +1 -0
  315. package/clis/instagram/unfollow.js +1 -0
  316. package/clis/instagram/unlike.js +1 -0
  317. package/clis/instagram/unsave.js +1 -0
  318. package/clis/instagram/user.js +1 -0
  319. package/clis/jd/add-cart.js +1 -0
  320. package/clis/jd/cart.js +1 -0
  321. package/clis/jd/detail.js +1 -0
  322. package/clis/jd/item.js +1 -0
  323. package/clis/jd/reviews.js +1 -0
  324. package/clis/jd/search.js +1 -0
  325. package/clis/jianyu/detail.js +1 -0
  326. package/clis/jianyu/search.js +1 -0
  327. package/clis/jike/comment.js +1 -0
  328. package/clis/jike/create.js +1 -0
  329. package/clis/jike/feed.js +3 -1
  330. package/clis/jike/like.js +1 -0
  331. package/clis/jike/notifications.js +1 -0
  332. package/clis/jike/post.js +1 -0
  333. package/clis/jike/repost.js +1 -0
  334. package/clis/jike/search.js +3 -1
  335. package/clis/jike/topic.js +1 -0
  336. package/clis/jike/user.js +3 -1
  337. package/clis/jimeng/generate.js +1 -0
  338. package/clis/jimeng/history.js +1 -0
  339. package/clis/jimeng/new.js +1 -0
  340. package/clis/jimeng/workspaces.js +1 -0
  341. package/clis/ke/chengjiao.js +1 -0
  342. package/clis/ke/ershoufang.js +1 -0
  343. package/clis/ke/xiaoqu.js +1 -0
  344. package/clis/ke/zufang.js +1 -0
  345. package/clis/lesswrong/comments.js +1 -0
  346. package/clis/lesswrong/curated.js +1 -0
  347. package/clis/lesswrong/frontpage.js +1 -0
  348. package/clis/lesswrong/new.js +1 -0
  349. package/clis/lesswrong/read.js +1 -0
  350. package/clis/lesswrong/sequences.js +1 -0
  351. package/clis/lesswrong/shortform.js +1 -0
  352. package/clis/lesswrong/tag.js +1 -0
  353. package/clis/lesswrong/tags.js +1 -0
  354. package/clis/lesswrong/top-month.js +1 -0
  355. package/clis/lesswrong/top-week.js +1 -0
  356. package/clis/lesswrong/top-year.js +1 -0
  357. package/clis/lesswrong/top.js +1 -0
  358. package/clis/lesswrong/user-posts.js +1 -0
  359. package/clis/lesswrong/user.js +1 -0
  360. package/clis/linkedin/search.js +1 -0
  361. package/clis/linkedin/timeline.js +1 -0
  362. package/clis/linux-do/categories.js +1 -0
  363. package/clis/linux-do/category.js +1 -0
  364. package/clis/linux-do/feed.js +1 -0
  365. package/clis/linux-do/hot.js +1 -0
  366. package/clis/linux-do/latest.js +1 -0
  367. package/clis/linux-do/search.js +1 -0
  368. package/clis/linux-do/tags.js +2 -1
  369. package/clis/linux-do/topic-content.js +1 -0
  370. package/clis/linux-do/topic.js +1 -0
  371. package/clis/linux-do/user-posts.js +1 -0
  372. package/clis/linux-do/user-topics.js +1 -0
  373. package/clis/lobsters/active.js +4 -1
  374. package/clis/lobsters/hot.js +4 -1
  375. package/clis/lobsters/lobsters.test.js +169 -0
  376. package/clis/lobsters/newest.js +4 -1
  377. package/clis/lobsters/read.js +196 -0
  378. package/clis/lobsters/tag.js +4 -1
  379. package/clis/maimai/search-talents.js +1 -0
  380. package/clis/medium/feed.js +1 -0
  381. package/clis/medium/search.js +1 -0
  382. package/clis/medium/user.js +1 -0
  383. package/clis/mubu/doc.js +1 -0
  384. package/clis/mubu/docs.js +1 -0
  385. package/clis/mubu/notes.js +1 -0
  386. package/clis/mubu/recent.js +1 -0
  387. package/clis/mubu/search.js +1 -0
  388. package/clis/notebooklm/current.js +1 -0
  389. package/clis/notebooklm/get.js +1 -0
  390. package/clis/notebooklm/history.js +1 -0
  391. package/clis/notebooklm/list.js +1 -0
  392. package/clis/notebooklm/note-list.js +1 -0
  393. package/clis/notebooklm/notes-get.js +1 -0
  394. package/clis/notebooklm/open.js +1 -0
  395. package/clis/notebooklm/source-fulltext.js +1 -0
  396. package/clis/notebooklm/source-get.js +1 -0
  397. package/clis/notebooklm/source-guide.js +1 -0
  398. package/clis/notebooklm/source-list.js +1 -0
  399. package/clis/notebooklm/status.js +1 -0
  400. package/clis/notebooklm/summary.js +1 -0
  401. package/clis/notion/export.js +1 -0
  402. package/clis/notion/favorites.js +1 -0
  403. package/clis/notion/new.js +1 -0
  404. package/clis/notion/read.js +1 -0
  405. package/clis/notion/search.js +1 -0
  406. package/clis/notion/sidebar.js +1 -0
  407. package/clis/notion/status.js +1 -0
  408. package/clis/notion/write.js +1 -0
  409. package/clis/nowcoder/companies.js +1 -0
  410. package/clis/nowcoder/creators.js +1 -0
  411. package/clis/nowcoder/detail.js +1 -0
  412. package/clis/nowcoder/experience.js +1 -0
  413. package/clis/nowcoder/hot.js +1 -0
  414. package/clis/nowcoder/jobs.js +1 -0
  415. package/clis/nowcoder/notifications.js +1 -0
  416. package/clis/nowcoder/papers.js +1 -0
  417. package/clis/nowcoder/practice.js +1 -0
  418. package/clis/nowcoder/recommend.js +1 -0
  419. package/clis/nowcoder/referral.js +1 -0
  420. package/clis/nowcoder/salary.js +1 -0
  421. package/clis/nowcoder/search.js +1 -0
  422. package/clis/nowcoder/suggest.js +1 -0
  423. package/clis/nowcoder/topics.js +1 -0
  424. package/clis/nowcoder/trending.js +1 -0
  425. package/clis/ones/login.js +1 -0
  426. package/clis/ones/logout.js +1 -0
  427. package/clis/ones/me.js +1 -0
  428. package/clis/ones/my-tasks.js +1 -0
  429. package/clis/ones/task.js +1 -0
  430. package/clis/ones/tasks.js +1 -0
  431. package/clis/ones/token-info.js +1 -0
  432. package/clis/ones/worklog.js +1 -0
  433. package/clis/openreview/openreview.test.js +345 -0
  434. package/clis/openreview/paper.js +43 -0
  435. package/clis/openreview/reviews.js +131 -0
  436. package/clis/openreview/search.js +46 -0
  437. package/clis/openreview/utils.js +158 -0
  438. package/clis/openreview/venue.js +63 -0
  439. package/clis/paperreview/feedback.js +1 -0
  440. package/clis/paperreview/review.js +1 -0
  441. package/clis/paperreview/submit.js +1 -0
  442. package/clis/pixiv/detail.js +1 -0
  443. package/clis/pixiv/download.js +1 -0
  444. package/clis/pixiv/illusts.js +2 -1
  445. package/clis/pixiv/ranking.js +2 -1
  446. package/clis/pixiv/search.js +2 -1
  447. package/clis/pixiv/user.js +2 -0
  448. package/clis/powerchina/search.js +1 -0
  449. package/clis/producthunt/browse.js +1 -0
  450. package/clis/producthunt/hot.js +1 -0
  451. package/clis/producthunt/posts.js +1 -0
  452. package/clis/producthunt/today.js +1 -0
  453. package/clis/quark/ls.js +1 -0
  454. package/clis/quark/mkdir.js +1 -0
  455. package/clis/quark/mv.js +1 -0
  456. package/clis/quark/rename.js +1 -0
  457. package/clis/quark/rm.js +1 -0
  458. package/clis/quark/save.js +1 -0
  459. package/clis/quark/share-tree.js +1 -0
  460. package/clis/reddit/comment.js +1 -0
  461. package/clis/reddit/frontpage.js +1 -0
  462. package/clis/reddit/hot.js +6 -1
  463. package/clis/reddit/hot.test.js +18 -0
  464. package/clis/reddit/popular.js +1 -0
  465. package/clis/reddit/read.js +1 -0
  466. package/clis/reddit/save.js +1 -0
  467. package/clis/reddit/saved.js +1 -0
  468. package/clis/reddit/search.js +1 -0
  469. package/clis/reddit/subreddit.js +1 -0
  470. package/clis/reddit/subscribe.js +1 -0
  471. package/clis/reddit/upvote.js +1 -0
  472. package/clis/reddit/upvoted.js +1 -0
  473. package/clis/reddit/user-comments.js +1 -0
  474. package/clis/reddit/user-posts.js +1 -0
  475. package/clis/reddit/user.js +1 -0
  476. package/clis/reuters/search.js +1 -0
  477. package/clis/sinablog/article.js +1 -0
  478. package/clis/sinablog/hot.js +1 -0
  479. package/clis/sinablog/search.js +1 -0
  480. package/clis/sinablog/user.js +1 -0
  481. package/clis/sinafinance/news.js +1 -0
  482. package/clis/sinafinance/rolling-news.js +1 -0
  483. package/clis/sinafinance/stock-rank.js +1 -0
  484. package/clis/sinafinance/stock.js +1 -0
  485. package/clis/smzdm/search.js +1 -0
  486. package/clis/spotify/spotify.js +11 -0
  487. package/clis/stackoverflow/bounties.js +11 -3
  488. package/clis/stackoverflow/hot.js +10 -2
  489. package/clis/stackoverflow/read.js +314 -0
  490. package/clis/stackoverflow/search.js +10 -2
  491. package/clis/stackoverflow/stackoverflow.test.js +346 -0
  492. package/clis/stackoverflow/unanswered.js +9 -2
  493. package/clis/steam/top-sellers.js +1 -0
  494. package/clis/substack/feed.js +1 -0
  495. package/clis/substack/publication.js +1 -0
  496. package/clis/substack/search.js +1 -0
  497. package/clis/taobao/add-cart.js +1 -0
  498. package/clis/taobao/cart.js +1 -0
  499. package/clis/taobao/detail.js +1 -0
  500. package/clis/taobao/reviews.js +1 -0
  501. package/clis/taobao/search.js +1 -0
  502. package/clis/tdx/hot-rank.js +1 -0
  503. package/clis/ths/hot-rank.js +1 -0
  504. package/clis/tieba/hot.js +2 -1
  505. package/clis/tieba/posts.js +1 -0
  506. package/clis/tieba/read.js +1 -0
  507. package/clis/tieba/search.js +2 -1
  508. package/clis/tiktok/comment.js +1 -0
  509. package/clis/tiktok/explore.js +1 -0
  510. package/clis/tiktok/follow.js +1 -0
  511. package/clis/tiktok/following.js +1 -0
  512. package/clis/tiktok/friends.js +1 -0
  513. package/clis/tiktok/like.js +1 -0
  514. package/clis/tiktok/live.js +1 -0
  515. package/clis/tiktok/notifications.js +1 -0
  516. package/clis/tiktok/profile.js +1 -0
  517. package/clis/tiktok/save.js +1 -0
  518. package/clis/tiktok/search.js +1 -0
  519. package/clis/tiktok/unfollow.js +1 -0
  520. package/clis/tiktok/unlike.js +1 -0
  521. package/clis/tiktok/unsave.js +1 -0
  522. package/clis/tiktok/user.js +1 -0
  523. package/clis/toutiao/articles.js +1 -0
  524. package/clis/twitter/accept.js +1 -0
  525. package/clis/twitter/article.js +1 -0
  526. package/clis/twitter/block.js +1 -0
  527. package/clis/twitter/bookmark.js +1 -0
  528. package/clis/twitter/bookmarks.js +2 -1
  529. package/clis/twitter/delete.js +1 -0
  530. package/clis/twitter/download.js +1 -0
  531. package/clis/twitter/follow.js +1 -0
  532. package/clis/twitter/followers.js +1 -0
  533. package/clis/twitter/following.js +1 -0
  534. package/clis/twitter/hide-reply.js +1 -0
  535. package/clis/twitter/like.js +1 -0
  536. package/clis/twitter/likes.js +2 -1
  537. package/clis/twitter/list-add.js +1 -0
  538. package/clis/twitter/list-remove.js +1 -0
  539. package/clis/twitter/list-tweets.js +1 -0
  540. package/clis/twitter/lists.js +1 -0
  541. package/clis/twitter/notifications.js +1 -0
  542. package/clis/twitter/post.js +1 -0
  543. package/clis/twitter/profile.js +1 -0
  544. package/clis/twitter/reply-dm.js +1 -0
  545. package/clis/twitter/reply.js +1 -0
  546. package/clis/twitter/search.js +1 -0
  547. package/clis/twitter/thread.js +1 -0
  548. package/clis/twitter/timeline.js +1 -0
  549. package/clis/twitter/trending.js +11 -12
  550. package/clis/twitter/trending.test.js +15 -0
  551. package/clis/twitter/tweets.js +2 -1
  552. package/clis/twitter/tweets.test.js +2 -2
  553. package/clis/twitter/unblock.js +1 -0
  554. package/clis/twitter/unbookmark.js +1 -0
  555. package/clis/twitter/unfollow.js +1 -0
  556. package/clis/uiverse/code.js +1 -0
  557. package/clis/uiverse/preview.js +1 -0
  558. package/clis/v2ex/daily.js +1 -0
  559. package/clis/v2ex/hot.js +1 -0
  560. package/clis/v2ex/latest.js +1 -0
  561. package/clis/v2ex/me.js +1 -0
  562. package/clis/v2ex/member.js +1 -0
  563. package/clis/v2ex/node.js +1 -0
  564. package/clis/v2ex/nodes.js +1 -0
  565. package/clis/v2ex/notifications.js +1 -0
  566. package/clis/v2ex/replies.js +1 -0
  567. package/clis/v2ex/topic.js +1 -0
  568. package/clis/v2ex/user.js +1 -0
  569. package/clis/wanfang/search.js +1 -0
  570. package/clis/web/read.js +1 -0
  571. package/clis/weibo/comments.js +1 -0
  572. package/clis/weibo/favorites.js +1 -0
  573. package/clis/weibo/feed.js +3 -1
  574. package/clis/weibo/hot.js +1 -0
  575. package/clis/weibo/me.js +1 -0
  576. package/clis/weibo/post.js +1 -0
  577. package/clis/weibo/publish.js +1 -0
  578. package/clis/weibo/search.js +8 -2
  579. package/clis/weibo/user.js +1 -0
  580. package/clis/weixin/create-draft.js +1 -0
  581. package/clis/weixin/download.js +1 -0
  582. package/clis/weixin/drafts.js +1 -0
  583. package/clis/weread/ai-outline.js +1 -0
  584. package/clis/weread/book.js +1 -0
  585. package/clis/weread/highlights.js +1 -0
  586. package/clis/weread/notebooks.js +1 -0
  587. package/clis/weread/notes.js +1 -0
  588. package/clis/weread/ranking.js +1 -0
  589. package/clis/weread/search.js +1 -0
  590. package/clis/weread/shelf.js +1 -0
  591. package/clis/wikipedia/random.js +1 -0
  592. package/clis/wikipedia/search.js +1 -0
  593. package/clis/wikipedia/summary.js +1 -0
  594. package/clis/wikipedia/trending.js +1 -0
  595. package/clis/xianyu/chat.js +1 -0
  596. package/clis/xianyu/item.js +1 -0
  597. package/clis/xianyu/search.js +1 -0
  598. package/clis/xiaoe/catalog.js +2 -1
  599. package/clis/xiaoe/content.js +1 -0
  600. package/clis/xiaoe/courses.js +1 -0
  601. package/clis/xiaoe/detail.js +1 -0
  602. package/clis/xiaoe/play-url.js +1 -0
  603. package/clis/xiaohongshu/comments.js +1 -0
  604. package/clis/xiaohongshu/creator-note-detail.js +1 -0
  605. package/clis/xiaohongshu/creator-notes-summary.js +1 -0
  606. package/clis/xiaohongshu/creator-notes.js +1 -0
  607. package/clis/xiaohongshu/creator-profile.js +1 -0
  608. package/clis/xiaohongshu/creator-stats.js +1 -0
  609. package/clis/xiaohongshu/download.js +1 -0
  610. package/clis/xiaohongshu/feed.js +2 -1
  611. package/clis/xiaohongshu/note.js +1 -0
  612. package/clis/xiaohongshu/notifications.js +1 -0
  613. package/clis/xiaohongshu/publish.js +1 -0
  614. package/clis/xiaohongshu/search.js +1 -0
  615. package/clis/xiaohongshu/user.js +1 -0
  616. package/clis/xiaoyuzhou/download.js +1 -0
  617. package/clis/xiaoyuzhou/episode.js +1 -0
  618. package/clis/xiaoyuzhou/podcast-episodes.js +1 -0
  619. package/clis/xiaoyuzhou/podcast.js +1 -0
  620. package/clis/xiaoyuzhou/transcript.js +1 -0
  621. package/clis/xueqiu/comments.js +1 -0
  622. package/clis/xueqiu/earnings-date.js +1 -0
  623. package/clis/xueqiu/feed.js +1 -0
  624. package/clis/xueqiu/fund-holdings.js +1 -0
  625. package/clis/xueqiu/fund-snapshot.js +1 -0
  626. package/clis/xueqiu/groups.js +1 -0
  627. package/clis/xueqiu/hot-stock.js +1 -0
  628. package/clis/xueqiu/hot.js +1 -0
  629. package/clis/xueqiu/kline.js +1 -0
  630. package/clis/xueqiu/search.js +1 -0
  631. package/clis/xueqiu/stock.js +1 -0
  632. package/clis/xueqiu/watchlist.js +1 -0
  633. package/clis/yahoo-finance/quote.js +1 -0
  634. package/clis/yollomi/background.js +1 -0
  635. package/clis/yollomi/edit.js +1 -0
  636. package/clis/yollomi/face-swap.js +1 -0
  637. package/clis/yollomi/generate.js +1 -0
  638. package/clis/yollomi/models.js +1 -0
  639. package/clis/yollomi/object-remover.js +1 -0
  640. package/clis/yollomi/remove-bg.js +1 -0
  641. package/clis/yollomi/restore.js +1 -0
  642. package/clis/yollomi/try-on.js +1 -0
  643. package/clis/yollomi/upload.js +1 -0
  644. package/clis/yollomi/upscale.js +1 -0
  645. package/clis/yollomi/video.js +1 -0
  646. package/clis/youtube/channel.js +1 -0
  647. package/clis/youtube/comments.js +1 -0
  648. package/clis/youtube/feed.js +8 -7
  649. package/clis/youtube/feed.test.js +131 -0
  650. package/clis/youtube/history.js +1 -0
  651. package/clis/youtube/like.js +1 -0
  652. package/clis/youtube/playlist.js +1 -0
  653. package/clis/youtube/search.js +1 -0
  654. package/clis/youtube/subscribe.js +1 -0
  655. package/clis/youtube/subscriptions.js +1 -0
  656. package/clis/youtube/transcript.js +1 -0
  657. package/clis/youtube/unlike.js +1 -0
  658. package/clis/youtube/unsubscribe.js +1 -0
  659. package/clis/youtube/video.js +1 -0
  660. package/clis/youtube/watch-later.js +1 -0
  661. package/clis/yuanbao/ask.js +1 -0
  662. package/clis/yuanbao/new.js +1 -0
  663. package/clis/zhihu/answer.js +1 -0
  664. package/clis/zhihu/collection.js +1 -0
  665. package/clis/zhihu/collections.js +1 -0
  666. package/clis/zhihu/comment.js +1 -0
  667. package/clis/zhihu/download.js +1 -0
  668. package/clis/zhihu/favorite.js +1 -0
  669. package/clis/zhihu/follow.js +1 -0
  670. package/clis/zhihu/hot.js +1 -0
  671. package/clis/zhihu/like.js +1 -0
  672. package/clis/zhihu/question.js +1 -0
  673. package/clis/zhihu/search.js +1 -0
  674. package/clis/zlibrary/info.js +1 -0
  675. package/clis/zlibrary/search.js +1 -0
  676. package/clis/zsxq/dynamics.js +1 -0
  677. package/clis/zsxq/groups.js +1 -0
  678. package/clis/zsxq/search.js +1 -0
  679. package/clis/zsxq/topic.js +1 -0
  680. package/clis/zsxq/topics.js +1 -0
  681. package/dist/src/adapter-shadow.d.ts +11 -0
  682. package/dist/src/adapter-shadow.js +72 -0
  683. package/dist/src/adapter-shadow.test.d.ts +1 -0
  684. package/dist/src/adapter-shadow.test.js +49 -0
  685. package/dist/src/adapter-source.test.js +1 -1
  686. package/dist/src/browser/analyze.test.js +2 -0
  687. package/dist/src/browser/base-page.d.ts +15 -2
  688. package/dist/src/browser/base-page.js +159 -8
  689. package/dist/src/browser/base-page.test.js +103 -1
  690. package/dist/src/browser/cdp.js +54 -0
  691. package/dist/src/browser/cdp.test.js +26 -0
  692. package/dist/src/browser/dom-helpers.d.ts +1 -1
  693. package/dist/src/browser/dom-helpers.js +15 -3
  694. package/dist/src/browser/page.d.ts +1 -0
  695. package/dist/src/browser/page.js +7 -1
  696. package/dist/src/browser/page.test.js +17 -0
  697. package/dist/src/browser/target-resolver.d.ts +17 -2
  698. package/dist/src/browser/target-resolver.js +85 -4
  699. package/dist/src/browser/verify-fixture.d.ts +7 -1
  700. package/dist/src/browser/verify-fixture.js +105 -0
  701. package/dist/src/browser/verify-fixture.test.js +59 -1
  702. package/dist/src/build-manifest.d.ts +68 -33
  703. package/dist/src/build-manifest.js +178 -29
  704. package/dist/src/build-manifest.test.js +85 -5
  705. package/dist/src/capabilityRouting.test.js +1 -1
  706. package/dist/src/cli.js +150 -11
  707. package/dist/src/cli.test.js +237 -0
  708. package/dist/src/commanderAdapter.d.ts +1 -1
  709. package/dist/src/commanderAdapter.js +17 -7
  710. package/dist/src/commanderAdapter.test.js +7 -6
  711. package/dist/src/convention-audit.d.ts +50 -0
  712. package/dist/src/convention-audit.js +546 -0
  713. package/dist/src/convention-audit.test.d.ts +1 -0
  714. package/dist/src/convention-audit.test.js +226 -0
  715. package/dist/src/discovery.js +2 -0
  716. package/dist/src/doctor.d.ts +2 -0
  717. package/dist/src/doctor.js +6 -0
  718. package/dist/src/doctor.test.js +36 -1
  719. package/dist/src/engine.test.js +10 -10
  720. package/dist/src/execution.js +1 -1
  721. package/dist/src/execution.test.js +9 -9
  722. package/dist/src/help.d.ts +15 -0
  723. package/dist/src/help.js +149 -0
  724. package/dist/src/main.js +13 -7
  725. package/dist/src/manifest-types.d.ts +41 -0
  726. package/dist/src/manifest-types.js +9 -0
  727. package/dist/src/plugin.test.js +26 -26
  728. package/dist/src/registry.d.ts +5 -0
  729. package/dist/src/registry.js +8 -0
  730. package/dist/src/registry.test.js +27 -20
  731. package/dist/src/serialization.d.ts +4 -0
  732. package/dist/src/serialization.js +27 -1
  733. package/dist/src/serialization.test.js +24 -2
  734. package/dist/src/types.d.ts +2 -0
  735. package/package.json +5 -2
  736. package/scripts/check-listing-id-pairing.mjs +193 -0
  737. package/scripts/check-silent-column-drop.mjs +105 -0
  738. package/scripts/check-typed-error-lint.mjs +118 -0
  739. package/scripts/silent-column-drop-baseline.json +962 -0
  740. package/scripts/typed-error-lint-baseline.json +1586 -0
@@ -0,0 +1,226 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import * as fs from 'node:fs';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import { renderConventionAuditText, runConventionAudit } from './convention-audit.js';
6
+ describe('convention audit', () => {
7
+ const tempDirs = [];
8
+ afterEach(() => {
9
+ for (const dir of tempDirs.splice(0)) {
10
+ fs.rmSync(dir, { recursive: true, force: true });
11
+ }
12
+ });
13
+ function makeProject(manifest, files) {
14
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-convention-audit-'));
15
+ tempDirs.push(root);
16
+ fs.mkdirSync(path.join(root, 'clis'), { recursive: true });
17
+ fs.writeFileSync(path.join(root, 'cli-manifest.json'), JSON.stringify(manifest, null, 2));
18
+ for (const [relative, content] of Object.entries(files)) {
19
+ const file = path.join(root, 'clis', relative);
20
+ fs.mkdirSync(path.dirname(file), { recursive: true });
21
+ fs.writeFileSync(file, content);
22
+ }
23
+ return root;
24
+ }
25
+ it('reports column, metadata, typed-error, and write-pair violations', () => {
26
+ const root = makeProject([
27
+ {
28
+ site: 'demo',
29
+ name: 'search',
30
+ access: 'read',
31
+ columns: ['id', 'title', 'authorName'],
32
+ sourceFile: 'demo/search.js',
33
+ },
34
+ {
35
+ site: 'demo',
36
+ name: 'like',
37
+ access: 'write',
38
+ sourceFile: 'demo/like.js',
39
+ },
40
+ {
41
+ site: 'demo',
42
+ name: 'missing',
43
+ columns: [],
44
+ sourceFile: 'demo/missing.js',
45
+ },
46
+ {
47
+ site: 'clean',
48
+ name: 'search',
49
+ access: 'read',
50
+ columns: ['id', 'title'],
51
+ sourceFile: 'clean/search.js',
52
+ },
53
+ ], {
54
+ 'demo/search.js': `
55
+ export async function run(kwargs) {
56
+ const limit = Math.min(kwargs.limit, 100);
57
+ try {
58
+ await fetch('/items');
59
+ } catch {
60
+ return [];
61
+ }
62
+ rows.push({
63
+ id: item.id,
64
+ title: item.title ?? 'unknown',
65
+ url: item.url,
66
+ authorName: item.authorName,
67
+ });
68
+ return limit;
69
+ }
70
+ `,
71
+ 'demo/like.js': 'export async function run() { return { ok: true }; }',
72
+ 'demo/missing.js': 'export async function run() { return { ok: true }; }',
73
+ 'clean/search.js': 'export async function run() { rows.push({ id: item.id, title: item.title }); }',
74
+ });
75
+ const report = runConventionAudit({ projectRoot: root });
76
+ const category = (rule) => report.categories.find((item) => item.rule === rule);
77
+ expect(report.ok).toBe(false);
78
+ expect(category('silent-column-drop').violations[0]).toMatchObject({
79
+ command: 'demo/search',
80
+ details: expect.objectContaining({ missing: ['url'] }),
81
+ });
82
+ expect(category('camelCase-in-columns').violations[0]).toMatchObject({
83
+ command: 'demo/search',
84
+ details: { column: 'authorName' },
85
+ });
86
+ expect(category('missing-access-metadata').violations[0]).toMatchObject({ command: 'demo/missing' });
87
+ expect(category('silent-clamp').violations[0]).toMatchObject({ command: 'demo/search' });
88
+ expect(category('silent-empty-fallback').violations[0]).toMatchObject({ command: 'demo/search' });
89
+ expect(category('silent-sentinel').violations[0]).toMatchObject({ command: 'demo/search' });
90
+ expect(category('write-without-delete-pair').violations[0]).toMatchObject({
91
+ command: 'demo/like',
92
+ details: { expected_any_of: ['unlike'] },
93
+ });
94
+ });
95
+ it('supports site and command target filters', () => {
96
+ const root = makeProject([
97
+ { site: 'demo', name: 'search', access: 'read', columns: ['id'], sourceFile: 'demo/search.js' },
98
+ { site: 'other', name: 'search', access: 'read', columns: ['id'], sourceFile: 'other/search.js' },
99
+ ], {
100
+ 'demo/search.js': 'export async function run() { rows.push({ id: 1, hidden: true }); }',
101
+ 'other/search.js': 'export async function run() { rows.push({ id: 1, hidden: true }); }',
102
+ });
103
+ expect(runConventionAudit({ projectRoot: root, site: 'demo' }).summary.commands).toBe(1);
104
+ expect(runConventionAudit({ projectRoot: root, target: 'other/search' }).summary.commands).toBe(1);
105
+ expect(runConventionAudit({ projectRoot: root, target: 'missing' }).summary.commands).toBe(0);
106
+ });
107
+ it('renders a compact text report', () => {
108
+ const root = makeProject([
109
+ { site: 'demo', name: 'search', access: 'read', columns: ['id'], sourceFile: 'demo/search.js' },
110
+ ], {
111
+ 'demo/search.js': 'export async function run() { rows.push({ id: 1 }); }',
112
+ });
113
+ const text = renderConventionAuditText(runConventionAudit({ projectRoot: root }));
114
+ expect(text).toContain('Convention Audit Report');
115
+ expect(text).toContain('OK - no convention violations found.');
116
+ });
117
+ it('scans pipeline map blocks for silent column drops', () => {
118
+ const root = makeProject([
119
+ { site: 'demo', name: 'feed', access: 'read', columns: ['id', 'title'], sourceFile: 'demo/feed.js' },
120
+ ], {
121
+ 'demo/feed.js': `
122
+ cli({
123
+ site: 'demo',
124
+ name: 'feed',
125
+ access: 'read',
126
+ columns: ['id', 'title'],
127
+ pipeline: [
128
+ { map: {
129
+ id: '\${{ item.id }}',
130
+ title: '\${{ item.title }}',
131
+ url: '\${{ item.url }}',
132
+ } },
133
+ ],
134
+ });
135
+ `,
136
+ });
137
+ const report = runConventionAudit({ projectRoot: root });
138
+ const violations = report.categories.find((item) => item.rule === 'silent-column-drop').violations;
139
+ expect(violations[0]).toMatchObject({
140
+ command: 'demo/feed',
141
+ details: expect.objectContaining({ missing: ['url'] }),
142
+ });
143
+ });
144
+ it('ignores ok:false diagnostic objects when checking emitted rows', () => {
145
+ const root = makeProject([
146
+ { site: 'demo', name: 'search', access: 'read', columns: ['id', 'url'], sourceFile: 'demo/search.js' },
147
+ ], {
148
+ 'demo/search.js': `
149
+ export async function run() {
150
+ if (!document.querySelector('.items')) {
151
+ return { ok: false, bodyLen: document.body.innerText.length, sample: document.body.innerText.slice(0, 800), url: location.href };
152
+ }
153
+ return rows.map((row) => ({ id: row.id, url: row.url }));
154
+ }
155
+ `,
156
+ });
157
+ const report = runConventionAudit({ projectRoot: root });
158
+ const violations = report.categories.find((item) => item.rule === 'silent-column-drop').violations;
159
+ expect(violations).toEqual([]);
160
+ });
161
+ it('ignores raw intermediate keys when a final mapper converts them into columns', () => {
162
+ const root = makeProject([
163
+ {
164
+ site: 'demo',
165
+ name: 'search',
166
+ access: 'read',
167
+ columns: ['id', 'rating', 'reviews', 'price'],
168
+ sourceFile: 'demo/search.js',
169
+ },
170
+ ], {
171
+ 'demo/search.js': `
172
+ export async function run() {
173
+ rows.push({
174
+ id: item.id,
175
+ starClass: item.starClass,
176
+ reviewsRaw: item.reviewsRaw,
177
+ priceRaw: item.priceRaw,
178
+ });
179
+ return rows.map((r) => ({
180
+ id: r.id,
181
+ rating: r.starClass ? Number(r.starClass) / 10 : null,
182
+ reviews: parseReviewCount(r.reviewsRaw),
183
+ price: parsePrice(r.priceRaw),
184
+ }));
185
+ }
186
+ `,
187
+ });
188
+ const report = runConventionAudit({ projectRoot: root });
189
+ const violations = report.categories.find((item) => item.rule === 'silent-column-drop').violations;
190
+ expect(violations).toEqual([]);
191
+ });
192
+ it('still reports raw keys emitted directly without a final mapper', () => {
193
+ const root = makeProject([
194
+ { site: 'demo', name: 'search', access: 'read', columns: ['id', 'title'], sourceFile: 'demo/search.js' },
195
+ ], {
196
+ 'demo/search.js': `
197
+ export async function run() {
198
+ rows.push({
199
+ id: item.id,
200
+ title: titleRaw,
201
+ titleRaw,
202
+ });
203
+ return rows;
204
+ }
205
+ `,
206
+ });
207
+ const report = runConventionAudit({ projectRoot: root });
208
+ const violations = report.categories.find((item) => item.rule === 'silent-column-drop').violations;
209
+ expect(violations[0]).toMatchObject({
210
+ command: 'demo/search',
211
+ details: expect.objectContaining({ missing: ['titleRaw'] }),
212
+ });
213
+ });
214
+ it('only reports empty array fallbacks inside catch blocks', () => {
215
+ const root = makeProject([
216
+ { site: 'demo', name: 'guard', access: 'read', columns: ['id'], sourceFile: 'demo/guard.js' },
217
+ { site: 'demo', name: 'catch', access: 'read', columns: ['id'], sourceFile: 'demo/catch.js' },
218
+ ], {
219
+ 'demo/guard.js': 'export async function run() { if (!store) return []; rows.push({ id: 1 }); }',
220
+ 'demo/catch.js': 'export async function run() { try { await fetch("/"); } catch (err) { return []; } rows.push({ id: 1 }); }',
221
+ });
222
+ const report = runConventionAudit({ projectRoot: root });
223
+ const violations = report.categories.find((item) => item.rule === 'silent-empty-fallback').violations;
224
+ expect(violations.map((violation) => violation.command)).toEqual(['demo/catch']);
225
+ });
226
+ });
@@ -124,6 +124,8 @@ async function loadFromManifest(manifestPath, clisDir) {
124
124
  name: entry.name,
125
125
  aliases: entry.aliases,
126
126
  description: entry.description ?? '',
127
+ access: entry.access,
128
+ example: entry.example,
127
129
  domain: entry.domain,
128
130
  strategy: parseStrategy(entry.strategy),
129
131
  browser: entry.browser,
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import type { BrowserSessionInfo } from './types.js';
7
7
  import type { BrowserProfileStatus } from './browser/daemon-client.js';
8
+ import { type AdapterShadow } from './adapter-shadow.js';
8
9
  export type DoctorOptions = {
9
10
  yes?: boolean;
10
11
  live?: boolean;
@@ -29,6 +30,7 @@ export type DoctorReport = {
29
30
  connectivity?: ConnectivityResult;
30
31
  sessions?: BrowserSessionInfo[];
31
32
  profiles?: BrowserProfileStatus[];
33
+ adapterShadows?: AdapterShadow[];
32
34
  issues: string[];
33
35
  };
34
36
  /**
@@ -12,6 +12,7 @@ import { getRuntimeLabel } from './runtime-detect.js';
12
12
  import { getCachedLatestExtensionVersion } from './update-check.js';
13
13
  import { aliasForContextId, loadProfileConfig } from './browser/profile.js';
14
14
  import { formatDaemonVersion, isDaemonStale, staleDaemonIssue } from './browser/daemon-version.js';
15
+ import { findShadowedUserAdapters, formatAdapterShadowIssue } from './adapter-shadow.js';
15
16
  const DOCTOR_LIVE_TIMEOUT_SECONDS = 8;
16
17
  /** Parse a semver string into [major, minor, patch]. Returns null on invalid input. */
17
18
  function parseSemver(v) {
@@ -107,6 +108,7 @@ export async function runBrowserDoctor(opts = {}) {
107
108
  }
108
109
  }
109
110
  const extensionVersion = health.status?.extensionVersion;
111
+ const adapterShadows = findShadowedUserAdapters();
110
112
  const issues = [];
111
113
  if (daemonFlaky) {
112
114
  issues.push('Daemon connectivity is unstable. The live browser test succeeded, but the daemon was no longer running immediately afterward.\n' +
@@ -171,6 +173,9 @@ export async function runBrowserDoctor(opts = {}) {
171
173
  issues.push(`Extension update available: v${extensionVersion} → v${latestExtensionVersion}\n` +
172
174
  ' Download from: https://github.com/jackwener/opencli/releases');
173
175
  }
176
+ if (adapterShadows.length > 0) {
177
+ issues.push(formatAdapterShadowIssue(adapterShadows));
178
+ }
174
179
  return {
175
180
  cliVersion: opts.cliVersion,
176
181
  daemonRunning,
@@ -184,6 +189,7 @@ export async function runBrowserDoctor(opts = {}) {
184
189
  connectivity,
185
190
  sessions,
186
191
  profiles,
192
+ adapterShadows,
187
193
  issues,
188
194
  };
189
195
  }
@@ -1,9 +1,10 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest';
2
- const { mockGetDaemonHealth, mockListSessions, mockConnect, mockClose } = vi.hoisted(() => ({
2
+ const { mockGetDaemonHealth, mockListSessions, mockConnect, mockClose, mockFindShadowedUserAdapters } = vi.hoisted(() => ({
3
3
  mockGetDaemonHealth: vi.fn(),
4
4
  mockListSessions: vi.fn(),
5
5
  mockConnect: vi.fn(),
6
6
  mockClose: vi.fn(),
7
+ mockFindShadowedUserAdapters: vi.fn(),
7
8
  }));
8
9
  vi.mock('./browser/daemon-client.js', () => ({
9
10
  getDaemonHealth: mockGetDaemonHealth,
@@ -15,11 +16,19 @@ vi.mock('./browser/index.js', () => ({
15
16
  close = mockClose;
16
17
  },
17
18
  }));
19
+ vi.mock('./adapter-shadow.js', async () => {
20
+ const actual = await vi.importActual('./adapter-shadow.js');
21
+ return {
22
+ ...actual,
23
+ findShadowedUserAdapters: mockFindShadowedUserAdapters,
24
+ };
25
+ });
18
26
  import { renderBrowserDoctorReport, runBrowserDoctor } from './doctor.js';
19
27
  describe('doctor report rendering', () => {
20
28
  const strip = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
21
29
  beforeEach(() => {
22
30
  vi.clearAllMocks();
31
+ mockFindShadowedUserAdapters.mockReturnValue([]);
23
32
  });
24
33
  it('renders OK-style report when daemon and extension connected', () => {
25
34
  const text = strip(renderBrowserDoctorReport({
@@ -34,6 +43,7 @@ describe('doctor report rendering', () => {
34
43
  expect(text).toContain('(v1.7.9)');
35
44
  expect(text).toContain('[OK] Extension: connected (v1.6.8)');
36
45
  expect(text).toContain('Everything looks good!');
46
+ expect(text).not.toContain('opencli browser analyze <url>');
37
47
  });
38
48
  it('renders a warning when daemon version is stale', () => {
39
49
  const text = strip(renderBrowserDoctorReport({
@@ -273,6 +283,31 @@ describe('doctor report rendering', () => {
273
283
  expect.stringContaining('Stale daemon detected: daemon v1.7.6 != CLI v1.7.9'),
274
284
  ]));
275
285
  });
286
+ it('reports local adapter shadows as a warning issue', async () => {
287
+ const status = {
288
+ state: 'ready',
289
+ status: {
290
+ daemonVersion: '1.7.9',
291
+ extensionConnected: true,
292
+ extensionVersion: '1.0.3',
293
+ },
294
+ };
295
+ mockGetDaemonHealth
296
+ .mockResolvedValueOnce(status)
297
+ .mockResolvedValueOnce(status);
298
+ mockFindShadowedUserAdapters.mockReturnValueOnce([
299
+ {
300
+ name: 'instagram/saved',
301
+ userPath: '/home/me/.opencli/clis/instagram/saved.js',
302
+ builtinPath: '/pkg/clis/instagram/saved.js',
303
+ },
304
+ ]);
305
+ const report = await runBrowserDoctor({ live: false, cliVersion: '1.7.9' });
306
+ expect(report.adapterShadows).toHaveLength(1);
307
+ expect(report.issues).toEqual(expect.arrayContaining([
308
+ expect.stringContaining('Local adapter overrides shadow packaged adapters'),
309
+ ]));
310
+ });
276
311
  it('reports profile-required when multiple profiles are connected without a selection', async () => {
277
312
  const status = {
278
313
  state: 'profile-required',
@@ -27,7 +27,7 @@ export const helper = true;
27
27
  import { cli, Strategy } from '${pathToFileURL(path.join(process.cwd(), 'src', 'registry.ts')).href}';
28
28
  cli({
29
29
  site: 'temp-site',
30
- name: 'hello',
30
+ name: 'hello', access: 'read',
31
31
  description: 'hello command',
32
32
  strategy: Strategy.PUBLIC,
33
33
  browser: false,
@@ -57,7 +57,7 @@ cli({
57
57
  import { cli, Strategy } from '${pathToFileURL(path.join(process.cwd(), 'src', 'registry.ts')).href}';
58
58
  cli({
59
59
  site: 'fallback-site',
60
- name: 'hello',
60
+ name: 'hello', access: 'read',
61
61
  description: 'hello command',
62
62
  strategy: Strategy.PUBLIC,
63
63
  browser: false,
@@ -86,7 +86,7 @@ import { htmlToMarkdown } from '@jackwener/opencli/utils';
86
86
 
87
87
  cli({
88
88
  site: 'legacy-site',
89
- name: 'hello',
89
+ name: 'hello', access: 'read',
90
90
  description: 'hello command',
91
91
  strategy: Strategy.PUBLIC,
92
92
  browser: false,
@@ -204,7 +204,7 @@ describe('executeCommand', () => {
204
204
  it('accepts kebab-case option names after Commander camelCases them', async () => {
205
205
  const cmd = cli({
206
206
  site: 'test-engine',
207
- name: 'kebab-arg-test',
207
+ name: 'kebab-arg-test', access: 'read',
208
208
  description: 'test command with kebab-case arg',
209
209
  browser: false,
210
210
  strategy: Strategy.PUBLIC,
@@ -219,7 +219,7 @@ describe('executeCommand', () => {
219
219
  it('executes a command with func', async () => {
220
220
  const cmd = cli({
221
221
  site: 'test-engine',
222
- name: 'func-test',
222
+ name: 'func-test', access: 'read',
223
223
  description: 'test command with func',
224
224
  browser: false,
225
225
  strategy: Strategy.PUBLIC,
@@ -233,7 +233,7 @@ describe('executeCommand', () => {
233
233
  it('executes a command with pipeline', async () => {
234
234
  const cmd = cli({
235
235
  site: 'test-engine',
236
- name: 'pipe-test',
236
+ name: 'pipe-test', access: 'read',
237
237
  description: 'test command with pipeline',
238
238
  browser: false,
239
239
  strategy: Strategy.PUBLIC,
@@ -248,7 +248,7 @@ describe('executeCommand', () => {
248
248
  it('throws for command with no func or pipeline', async () => {
249
249
  const cmd = cli({
250
250
  site: 'test-engine',
251
- name: 'empty-test',
251
+ name: 'empty-test', access: 'read',
252
252
  description: 'empty command',
253
253
  browser: false,
254
254
  });
@@ -258,7 +258,7 @@ describe('executeCommand', () => {
258
258
  let receivedDebug = false;
259
259
  const cmd = cli({
260
260
  site: 'test-engine',
261
- name: 'debug-test',
261
+ name: 'debug-test', access: 'read',
262
262
  description: 'debug test',
263
263
  browser: false,
264
264
  func: async (_kwargs, debug) => {
@@ -276,7 +276,7 @@ describe('executeCommand', () => {
276
276
  });
277
277
  const cmd = cli({
278
278
  site: 'test-engine',
279
- name: 'failing-test',
279
+ name: 'failing-test', access: 'read',
280
280
  description: 'failing command',
281
281
  browser: false,
282
282
  strategy: Strategy.PUBLIC,
@@ -297,7 +297,7 @@ describe('executeCommand', () => {
297
297
  .mockResolvedValue('http://127.0.0.1:9228');
298
298
  const cmd = cli({
299
299
  site: 'chatwise',
300
- name: 'status',
300
+ name: 'status', access: 'read',
301
301
  description: 'chatwise status',
302
302
  browser: true,
303
303
  strategy: Strategy.PUBLIC,
@@ -301,7 +301,7 @@ export async function executeCommand(cmd, rawKwargs, debug = false, opts = {}) {
301
301
  await page.closeWindow?.().catch(() => { });
302
302
  throw err;
303
303
  }
304
- }, { workspace: `site:${cmd.site}`, cdpEndpoint, contextId });
304
+ }, { workspace: `site:${cmd.site}:${crypto.randomUUID()}`, cdpEndpoint, contextId });
305
305
  }
306
306
  else {
307
307
  // Non-browser commands: apply timeout only when explicitly configured.
@@ -12,7 +12,7 @@ describe('executeCommand — non-browser timeout', () => {
12
12
  it('applies timeoutSeconds to non-browser commands', async () => {
13
13
  const cmd = cli({
14
14
  site: 'test-execution',
15
- name: 'non-browser-timeout',
15
+ name: 'non-browser-timeout', access: 'read',
16
16
  description: 'test non-browser timeout',
17
17
  browser: false,
18
18
  strategy: Strategy.PUBLIC,
@@ -32,7 +32,7 @@ describe('executeCommand — non-browser timeout', () => {
32
32
  it('skips timeout when timeoutSeconds is 0', async () => {
33
33
  const cmd = cli({
34
34
  site: 'test-execution',
35
- name: 'non-browser-zero-timeout',
35
+ name: 'non-browser-zero-timeout', access: 'read',
36
36
  description: 'test zero timeout bypasses wrapping',
37
37
  browser: false,
38
38
  strategy: Strategy.PUBLIC,
@@ -53,7 +53,7 @@ describe('executeCommand — non-browser timeout', () => {
53
53
  });
54
54
  const cmd = cli({
55
55
  site: 'test-execution',
56
- name: 'browser-close-on-error',
56
+ name: 'browser-close-on-error', access: 'read',
57
57
  description: 'test closeWindow on failure',
58
58
  browser: true,
59
59
  strategy: Strategy.PUBLIC,
@@ -73,7 +73,7 @@ describe('executeCommand — non-browser timeout', () => {
73
73
  try {
74
74
  const cmd = cli({
75
75
  site: 'test-execution',
76
- name: 'browser-live-success',
76
+ name: 'browser-live-success', access: 'read',
77
77
  description: 'test closeWindow skipped with --live on success',
78
78
  browser: true,
79
79
  strategy: Strategy.PUBLIC,
@@ -100,7 +100,7 @@ describe('executeCommand — non-browser timeout', () => {
100
100
  try {
101
101
  const cmd = cli({
102
102
  site: 'test-execution',
103
- name: 'browser-live-failure',
103
+ name: 'browser-live-failure', access: 'read',
104
104
  description: 'test closeWindow skipped with --live on failure',
105
105
  browser: true,
106
106
  strategy: Strategy.PUBLIC,
@@ -121,7 +121,7 @@ describe('executeCommand — non-browser timeout', () => {
121
121
  const validateArgs = vi.fn();
122
122
  const cmd = {
123
123
  site: 'test-execution',
124
- name: 'prepared-validation',
124
+ name: 'prepared-validation', access: 'read',
125
125
  description: 'test prepared validation path',
126
126
  browser: false,
127
127
  strategy: Strategy.PUBLIC,
@@ -164,7 +164,7 @@ describe('executeCommand — non-browser timeout', () => {
164
164
  try {
165
165
  const cmd = cli({
166
166
  site: 'test-execution',
167
- name: 'browser-trace-failure',
167
+ name: 'browser-trace-failure', access: 'read',
168
168
  description: 'test trace export',
169
169
  browser: true,
170
170
  strategy: Strategy.PUBLIC,
@@ -224,7 +224,7 @@ describe('executeCommand — non-browser timeout', () => {
224
224
  try {
225
225
  const cmd = cli({
226
226
  site: 'test-execution',
227
- name: 'browser-trace-success',
227
+ name: 'browser-trace-success', access: 'read',
228
228
  description: 'test trace export on success',
229
229
  browser: true,
230
230
  strategy: Strategy.PUBLIC,
@@ -281,7 +281,7 @@ describe('executeCommand — non-browser timeout', () => {
281
281
  try {
282
282
  const cmd = cli({
283
283
  site: 'test-execution',
284
- name: 'browser-trace-export-fails',
284
+ name: 'browser-trace-export-fails', access: 'read',
285
285
  description: 'test trace export failure handling',
286
286
  browser: true,
287
287
  strategy: Strategy.PUBLIC,
@@ -0,0 +1,15 @@
1
+ import { Command } from 'commander';
2
+ import type { CliCommand } from './registry.js';
3
+ export type StructuredHelpFormat = 'yaml' | 'json';
4
+ export declare function getRequestedHelpFormat(argv?: readonly string[]): StructuredHelpFormat | undefined;
5
+ export declare function renderStructuredHelp(data: unknown, format: StructuredHelpFormat): string;
6
+ export declare function wrapCommaList(items: readonly string[], opts?: {
7
+ width?: number;
8
+ indent?: string;
9
+ }): string;
10
+ export declare function formatRootAdapterHelpText(siteNames: readonly string[]): string;
11
+ export declare function rootHelpData(program: Command, siteNames: readonly string[]): Record<string, unknown>;
12
+ export declare function siteHelpData(site: string, commands: readonly CliCommand[]): Record<string, unknown>;
13
+ export declare function commandHelpData(cmd: CliCommand): Record<string, unknown>;
14
+ export declare function installStructuredHelp(command: Command, data: () => unknown, textSuffix?: string | (() => string)): void;
15
+ export declare function formatSiteCommandDescription(cmd: CliCommand): string;