@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
@@ -5,8 +5,21 @@
5
5
  * Scans all JS CLI definitions in clis/ and pre-compiles them into a single
6
6
  * manifest.json for instant cold-start registration.
7
7
  *
8
- * Usage: npx tsx src/build-manifest.ts
8
+ * Usage: npx tsx src/build-manifest.ts [--allow-removals[=N]]
9
+ *
9
10
  * Output: cli-manifest.json next to clis/
11
+ *
12
+ * Safety invariants:
13
+ * - Adapters whose source file does not call `cli(...)` are silently
14
+ * skipped (they are helpers / type modules, not commands).
15
+ * - Adapters that look like commands but fail to import are reported as
16
+ * failures, the manifest is NOT written, and the process exits 1. This
17
+ * prevents a stale dist or a broken adapter from silently dropping
18
+ * other adapters' entries (root cause of the "manifest lost 478 lines"
19
+ * incident).
20
+ * - Net-deletions vs the existing committed manifest abort the build by
21
+ * default; pass `--allow-removals=N` (or just `--allow-removals` for any
22
+ * amount) to confirm an intentional removal.
10
23
  */
11
24
  import * as fs from 'node:fs';
12
25
  import * as path from 'node:path';
@@ -14,12 +27,27 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
14
27
  import { getErrorMessage } from './errors.js';
15
28
  import { fullName, getRegistry } from './registry.js';
16
29
  import { findPackageRoot, getCliManifestPath } from './package-paths.js';
30
+ import { isRecord } from './utils.js';
17
31
  const PACKAGE_ROOT = findPackageRoot(fileURLToPath(import.meta.url));
18
32
  const CLIS_DIR = path.join(PACKAGE_ROOT, 'clis');
19
33
  // Write manifest next to clis/ so both dev and installed runtime can find it.
20
34
  const OUTPUT = getCliManifestPath(CLIS_DIR);
21
- import { isRecord } from './utils.js';
22
35
  const CLI_MODULE_PATTERN = /\bcli\s*\(/;
36
+ /**
37
+ * Thrown by `loadManifestEntries` when an adapter file looks like a CLI
38
+ * module (matches CLI_MODULE_PATTERN) but cannot be imported. Callers
39
+ * decide whether to abort or aggregate failures across the whole scan.
40
+ */
41
+ export class ManifestImportError extends Error {
42
+ filePath;
43
+ cause;
44
+ constructor(filePath, cause) {
45
+ super(`failed to scan ${filePath}: ${getErrorMessage(cause)}`);
46
+ this.filePath = filePath;
47
+ this.cause = cause;
48
+ this.name = 'ManifestImportError';
49
+ }
50
+ }
23
51
  function toManifestArgs(args) {
24
52
  return args.map(arg => ({
25
53
  name: arg.name,
@@ -39,14 +67,15 @@ function toModulePath(filePath, site) {
39
67
  export function normalizeManifestPath(relativePath) {
40
68
  return relativePath.replace(/\\/g, '/');
41
69
  }
42
- function toManifestRelativePath(filePath) {
43
- return normalizeManifestPath(path.relative(CLIS_DIR, filePath));
70
+ function toManifestRelativePath(filePath, clisDir) {
71
+ return normalizeManifestPath(path.relative(clisDir, filePath));
44
72
  }
45
73
  function isCliCommandValue(value, site) {
46
74
  return isRecord(value)
47
75
  && typeof value.site === 'string'
48
76
  && value.site === site
49
77
  && typeof value.name === 'string'
78
+ && (value.access === 'read' || value.access === 'write')
50
79
  && Array.isArray(value.args);
51
80
  }
52
81
  function toManifestEntry(cmd, modulePath, sourceFile) {
@@ -55,6 +84,8 @@ function toManifestEntry(cmd, modulePath, sourceFile) {
55
84
  name: cmd.name,
56
85
  aliases: cmd.aliases,
57
86
  description: cmd.description ?? '',
87
+ access: cmd.access,
88
+ example: cmd.example,
58
89
  domain: cmd.domain,
59
90
  strategy: (cmd.strategy ?? 'public').toString().toLowerCase(),
60
91
  browser: cmd.browser ?? true,
@@ -69,12 +100,30 @@ function toManifestEntry(cmd, modulePath, sourceFile) {
69
100
  navigateBefore: cmd.navigateBefore,
70
101
  };
71
102
  }
72
- export async function loadManifestEntries(filePath, site, importer = moduleHref => import(moduleHref)) {
103
+ /**
104
+ * Load all manifest entries from a single adapter file.
105
+ *
106
+ * Returns `[]` for files that do not register a CLI command (helpers, types).
107
+ * Throws `ManifestImportError` when a file looks like a CLI module but its
108
+ * import or post-import processing fails — callers must decide whether to
109
+ * surface or aggregate the failure.
110
+ *
111
+ * The third argument `clisDir` is used to compute the POSIX-style
112
+ * `sourceFile` relative path; it defaults to the package's `clis/` dir so
113
+ * existing test callers stay backward-compatible.
114
+ */
115
+ export async function loadManifestEntries(filePath, site, importer = moduleHref => import(moduleHref), clisDir = CLIS_DIR) {
116
+ let src;
117
+ try {
118
+ src = fs.readFileSync(filePath, 'utf-8');
119
+ }
120
+ catch (err) {
121
+ throw new ManifestImportError(filePath, err);
122
+ }
123
+ // Helper / test modules that do not call cli() are not commands.
124
+ if (!CLI_MODULE_PATTERN.test(src))
125
+ return [];
73
126
  try {
74
- const src = fs.readFileSync(filePath, 'utf-8');
75
- // Helper/test modules should not appear as CLI commands in the manifest.
76
- if (!CLI_MODULE_PATTERN.test(src))
77
- return [];
78
127
  const modulePath = toModulePath(filePath, site);
79
128
  const registry = getRegistry();
80
129
  const before = new Map(registry.entries());
@@ -93,7 +142,7 @@ export async function loadManifestEntries(filePath, site, importer = moduleHref
93
142
  .map(([, cmd]) => cmd);
94
143
  // Manifest paths are cross-platform artifacts; keep them POSIX-style even
95
144
  // when build-manifest runs on Windows.
96
- const sourceRelative = toManifestRelativePath(filePath);
145
+ const sourceRelative = toManifestRelativePath(filePath, clisDir);
97
146
  const seen = new Set();
98
147
  return runtimeCommands
99
148
  .filter((cmd) => {
@@ -107,42 +156,142 @@ export async function loadManifestEntries(filePath, site, importer = moduleHref
107
156
  .map(cmd => toManifestEntry(cmd, modulePath, sourceRelative));
108
157
  }
109
158
  catch (err) {
110
- // If parsing fails, log a warning (matching scanYaml behaviour) and skip the entry.
111
- process.stderr.write(`Warning: failed to scan ${filePath}: ${getErrorMessage(err)}\n`);
112
- return [];
159
+ throw new ManifestImportError(filePath, err);
113
160
  }
114
161
  }
115
- export async function buildManifest() {
162
+ /**
163
+ * Scan a `clis/` directory and aggregate per-adapter results. Import
164
+ * failures are collected in `failures` instead of crashing the whole scan,
165
+ * but the caller (e.g. `main()`) is expected to fail loud if any failure
166
+ * is present.
167
+ */
168
+ export async function scanClisDir(clisDir, importer = moduleHref => import(moduleHref)) {
116
169
  const manifest = new Map();
117
- // Scan JS adapters directly from clis/.
118
- // Adapters are now JS-first — no compilation step needed.
119
- if (fs.existsSync(CLIS_DIR)) {
120
- for (const site of fs.readdirSync(CLIS_DIR)) {
121
- const siteDir = path.join(CLIS_DIR, site);
122
- if (!fs.statSync(siteDir).isDirectory())
123
- continue;
124
- for (const file of fs.readdirSync(siteDir)) {
125
- if (file.endsWith('.js') && !file.endsWith('.d.js') && !file.endsWith('.test.js') && file !== 'index.js') {
126
- const filePath = path.join(siteDir, file);
127
- const entries = await loadManifestEntries(filePath, site);
170
+ const failures = [];
171
+ if (!fs.existsSync(clisDir)) {
172
+ return { entries: [], failures };
173
+ }
174
+ for (const site of fs.readdirSync(clisDir)) {
175
+ const siteDir = path.join(clisDir, site);
176
+ if (!fs.statSync(siteDir).isDirectory())
177
+ continue;
178
+ for (const file of fs.readdirSync(siteDir)) {
179
+ if (file.endsWith('.js') && !file.endsWith('.d.js') && !file.endsWith('.test.js') && file !== 'index.js') {
180
+ const filePath = path.join(siteDir, file);
181
+ try {
182
+ const entries = await loadManifestEntries(filePath, site, importer, clisDir);
128
183
  for (const entry of entries) {
129
184
  const key = `${entry.site}/${entry.name}`;
130
185
  manifest.set(key, entry);
131
186
  }
132
187
  }
188
+ catch (err) {
189
+ if (err instanceof ManifestImportError) {
190
+ failures.push(err);
191
+ continue;
192
+ }
193
+ throw err;
194
+ }
133
195
  }
134
196
  }
135
197
  }
136
- return [...manifest.values()].sort((a, b) => a.site.localeCompare(b.site) || a.name.localeCompare(b.name));
198
+ const entries = [...manifest.values()].sort((a, b) => a.site.localeCompare(b.site) || a.name.localeCompare(b.name));
199
+ return { entries, failures };
200
+ }
201
+ export async function buildManifest() {
202
+ return scanClisDir(CLIS_DIR);
137
203
  }
138
204
  export function serializeManifest(manifest) {
139
205
  return `${JSON.stringify(manifest, null, 2)}\n`;
140
206
  }
207
+ /**
208
+ * Diff helper: returns site/name keys that exist in `prev` but not in
209
+ * `next`. Used as a safety net to detect accidental mass-deletions caused
210
+ * by silently failing adapter imports.
211
+ */
212
+ export function diffRemovedEntries(prev, next) {
213
+ const nextKeys = new Set(next.map(e => `${e.site}/${e.name}`));
214
+ return prev
215
+ .map(e => `${e.site}/${e.name}`)
216
+ .filter(key => !nextKeys.has(key))
217
+ .sort();
218
+ }
219
+ /**
220
+ * Parse `--allow-removals` and `--allow-removals=N` from argv.
221
+ * Bare `--allow-removals` disables the safety net (`Infinity`); the
222
+ * numeric form sets an explicit upper bound.
223
+ */
224
+ export function parseBuildManifestArgs(argv) {
225
+ let allowRemovals = 0;
226
+ for (const arg of argv) {
227
+ if (arg === '--allow-removals') {
228
+ allowRemovals = Number.POSITIVE_INFINITY;
229
+ continue;
230
+ }
231
+ const m = arg.match(/^--allow-removals=(\d+)$/);
232
+ if (m) {
233
+ allowRemovals = Number.parseInt(m[1], 10);
234
+ continue;
235
+ }
236
+ }
237
+ return { allowRemovals };
238
+ }
239
+ function readExistingManifest(filePath) {
240
+ try {
241
+ if (!fs.existsSync(filePath))
242
+ return null;
243
+ const raw = fs.readFileSync(filePath, 'utf-8');
244
+ const parsed = JSON.parse(raw);
245
+ return Array.isArray(parsed) ? parsed : null;
246
+ }
247
+ catch {
248
+ return null;
249
+ }
250
+ }
141
251
  async function main() {
142
- const manifest = await buildManifest();
252
+ // Runtime guard: refuse to run from dist/. tsc transitively emits this
253
+ // file (the test file imports from it) so dist/src/build-manifest.js
254
+ // physically exists. If a developer or agent runs that compiled copy,
255
+ // any stale dist will silently break adapter imports — the exact failure
256
+ // mode this script is meant to prevent. Direct them at the tsx entry
257
+ // before they can shoot themselves in the foot.
258
+ if (fileURLToPath(import.meta.url).includes(`${path.sep}dist${path.sep}`)) {
259
+ process.stderr.write(`❌ Refusing to run build-manifest from dist/.\n`
260
+ + ` Stale compiled output silently drops adapters that import renamed/removed exports.\n`
261
+ + ` Run \`npm run build-manifest\` (or \`tsx src/build-manifest.ts\`) from the source tree instead.\n`);
262
+ process.exit(1);
263
+ }
264
+ const args = parseBuildManifestArgs(process.argv.slice(2));
265
+ const { entries, failures } = await buildManifest();
266
+ if (failures.length > 0) {
267
+ process.stderr.write(`❌ ${failures.length} adapter(s) failed to load:\n`);
268
+ for (const failure of failures) {
269
+ const rel = path.relative(PACKAGE_ROOT, failure.filePath) || failure.filePath;
270
+ process.stderr.write(` - ${rel}: ${getErrorMessage(failure.cause)}\n`);
271
+ }
272
+ process.stderr.write(`\nManifest NOT written. Likely cause: stale dist/ or a broken adapter import.\n`
273
+ + `Always run via tsx (\`npm run build-manifest\`), not against compiled dist/.\n`);
274
+ process.exit(1);
275
+ }
276
+ const existing = readExistingManifest(OUTPUT);
277
+ if (existing) {
278
+ const removed = diffRemovedEntries(existing, entries);
279
+ if (removed.length > args.allowRemovals) {
280
+ process.stderr.write(`❌ ${removed.length} manifest entries would be removed; refusing to overwrite.\n`);
281
+ const preview = removed.slice(0, 20);
282
+ for (const key of preview)
283
+ process.stderr.write(` - ${key}\n`);
284
+ if (removed.length > preview.length) {
285
+ process.stderr.write(` ... ${removed.length - preview.length} more\n`);
286
+ }
287
+ process.stderr.write(`\nIf this removal is intentional, rerun with `
288
+ + `\`--allow-removals=${removed.length}\` (or \`--allow-removals\` to disable the check).\n`);
289
+ process.exit(1);
290
+ }
291
+ }
143
292
  fs.mkdirSync(path.dirname(OUTPUT), { recursive: true });
144
- fs.writeFileSync(OUTPUT, serializeManifest(manifest));
145
- console.log(`✅ Manifest compiled: ${manifest.length} entries → ${OUTPUT}`);
293
+ fs.writeFileSync(OUTPUT, serializeManifest(entries));
294
+ console.log(`✅ Manifest compiled: ${entries.length} entries → ${OUTPUT}`);
146
295
  // Restore executable permissions on bin entries.
147
296
  // tsc does not preserve the +x bit, so after a clean rebuild the CLI
148
297
  // entry-point loses its executable permission, causing "Permission denied".
@@ -3,7 +3,7 @@ import * as fs from 'node:fs';
3
3
  import * as os from 'node:os';
4
4
  import * as path from 'node:path';
5
5
  import { cli, getRegistry, Strategy } from './registry.js';
6
- import { loadManifestEntries, normalizeManifestPath, serializeManifest } from './build-manifest.js';
6
+ import { ManifestImportError, diffRemovedEntries, loadManifestEntries, normalizeManifestPath, parseBuildManifestArgs, scanClisDir, serializeManifest, } from './build-manifest.js';
7
7
  describe('manifest helper rules', () => {
8
8
  const tempDirs = [];
9
9
  afterEach(() => {
@@ -24,11 +24,12 @@ describe('manifest helper rules', () => {
24
24
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
25
25
  tempDirs.push(dir);
26
26
  const file = path.join(dir, `${site}.ts`);
27
- fs.writeFileSync(file, `export const command = cli({ site: '${site}', name: 'dynamic' });`);
27
+ fs.writeFileSync(file, `export const command = cli({ site: '${site}', name: 'dynamic', access: 'read' });`);
28
28
  const entries = await loadManifestEntries(file, site, async () => ({
29
29
  command: cli({
30
30
  site,
31
31
  name: 'dynamic',
32
+ access: 'read',
32
33
  description: 'dynamic command',
33
34
  strategy: Strategy.PUBLIC,
34
35
  browser: false,
@@ -53,6 +54,7 @@ describe('manifest helper rules', () => {
53
54
  {
54
55
  site,
55
56
  name: 'dynamic',
57
+ access: 'read',
56
58
  description: 'dynamic command',
57
59
  domain: 'localhost',
58
60
  strategy: 'public',
@@ -87,11 +89,12 @@ describe('manifest helper rules', () => {
87
89
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
88
90
  tempDirs.push(dir);
89
91
  const file = path.join(dir, `${site}.ts`);
90
- fs.writeFileSync(file, `cli({ site: '${site}', name: 'legacy' });`);
92
+ fs.writeFileSync(file, `cli({ site: '${site}', name: 'legacy', access: 'read' });`);
91
93
  const entries = await loadManifestEntries(file, site, async () => {
92
94
  cli({
93
95
  site,
94
96
  name: 'legacy',
97
+ access: 'read',
95
98
  description: 'legacy command',
96
99
  deprecated: 'legacy is deprecated',
97
100
  replacedBy: 'opencli demo new',
@@ -102,6 +105,7 @@ describe('manifest helper rules', () => {
102
105
  {
103
106
  site,
104
107
  name: 'legacy',
108
+ access: 'read',
105
109
  description: 'legacy command',
106
110
  strategy: 'cookie',
107
111
  browser: true,
@@ -123,16 +127,18 @@ describe('manifest helper rules', () => {
123
127
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
124
128
  tempDirs.push(dir);
125
129
  const file = path.join(dir, `${site}.ts`);
126
- fs.writeFileSync(file, `export const screen = cli({ site: '${site}', name: 'screen' });`);
130
+ fs.writeFileSync(file, `export const screen = cli({ site: '${site}', name: 'screen', access: 'read' });`);
127
131
  const entries = await loadManifestEntries(file, site, async () => ({
128
132
  screen: cli({
129
133
  site,
130
134
  name: 'screen',
135
+ access: 'read',
131
136
  description: 'capture screen',
132
137
  }),
133
138
  status: cli({
134
139
  site,
135
140
  name: 'status',
141
+ access: 'read',
136
142
  description: 'show status',
137
143
  }),
138
144
  }));
@@ -147,7 +153,7 @@ describe('manifest helper rules', () => {
147
153
  it('serializes manifest json with a trailing newline', () => {
148
154
  const serialized = serializeManifest([{
149
155
  site: 'demo',
150
- name: 'status',
156
+ name: 'status', access: 'read',
151
157
  description: '',
152
158
  strategy: 'public',
153
159
  browser: false,
@@ -157,4 +163,78 @@ describe('manifest helper rules', () => {
157
163
  expect(serialized.endsWith('\n')).toBe(true);
158
164
  expect(serialized).toContain('\n]');
159
165
  });
166
+ it('throws ManifestImportError when an adapter looks like a CLI module but fails to import', async () => {
167
+ // Reproduces the "stale dist drops adapters silently" incident: the file
168
+ // matches the cli() pattern (so it's not just a helper), but the importer
169
+ // throws — we want the failure surfaced, not swallowed.
170
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-fail-'));
171
+ tempDirs.push(dir);
172
+ const file = path.join(dir, 'broken.ts');
173
+ fs.writeFileSync(file, `export const command = cli({ site: 'demo', name: 'broken', access: 'read' });`);
174
+ const importer = async () => { throw new Error('boom: stale dist'); };
175
+ await expect(loadManifestEntries(file, 'demo', importer))
176
+ .rejects.toBeInstanceOf(ManifestImportError);
177
+ try {
178
+ await loadManifestEntries(file, 'demo', importer);
179
+ }
180
+ catch (err) {
181
+ expect(err).toBeInstanceOf(ManifestImportError);
182
+ const e = err;
183
+ expect(e.filePath).toBe(file);
184
+ expect(e.message).toContain('boom: stale dist');
185
+ }
186
+ });
187
+ it('still silently skips files that do not call cli() even if the importer would have thrown', async () => {
188
+ // The cli() pattern check happens before importing — we don't even ask
189
+ // the importer about helper modules, so a thrown import does not turn
190
+ // them into failures.
191
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-helper-'));
192
+ tempDirs.push(dir);
193
+ const file = path.join(dir, 'helper.ts');
194
+ fs.writeFileSync(file, `export const helper = () => 42;`);
195
+ const importer = async () => { throw new Error('should never be called'); };
196
+ await expect(loadManifestEntries(file, 'demo', importer)).resolves.toEqual([]);
197
+ });
198
+ it('scanClisDir aggregates per-adapter import failures instead of silently dropping them', async () => {
199
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-clis-'));
200
+ tempDirs.push(root);
201
+ const siteDir = path.join(root, 'demo');
202
+ fs.mkdirSync(siteDir);
203
+ fs.writeFileSync(path.join(siteDir, 'good.js'), `export const cmd = cli({ site: 'demo', name: 'good', access: 'read' });`);
204
+ fs.writeFileSync(path.join(siteDir, 'broken.js'), `export const cmd = cli({ site: 'demo', name: 'broken', access: 'read' });`);
205
+ const importer = async (href) => {
206
+ if (href.endsWith('broken.js'))
207
+ throw new Error('stale dist drops broken');
208
+ return { cmd: cli({ site: 'demo', name: 'good', access: 'read', description: 'ok' }) };
209
+ };
210
+ const result = await scanClisDir(root, importer);
211
+ expect(result.failures).toHaveLength(1);
212
+ expect(result.failures[0]).toBeInstanceOf(ManifestImportError);
213
+ expect(result.failures[0].filePath).toMatch(/broken\.js$/);
214
+ expect(result.failures[0].message).toContain('stale dist drops broken');
215
+ expect(result.entries.map(e => e.name)).toEqual(['good']);
216
+ getRegistry().delete('demo/good');
217
+ });
218
+ it('diffRemovedEntries returns site/name keys present only in prev', () => {
219
+ const prev = [
220
+ { site: 'a', name: '1', access: 'read', description: '', strategy: 'public', browser: false, args: [], type: 'js' },
221
+ { site: 'a', name: '2', access: 'read', description: '', strategy: 'public', browser: false, args: [], type: 'js' },
222
+ { site: 'b', name: '3', access: 'read', description: '', strategy: 'public', browser: false, args: [], type: 'js' },
223
+ ];
224
+ const next = [
225
+ { site: 'a', name: '1', access: 'read', description: '', strategy: 'public', browser: false, args: [], type: 'js' },
226
+ ];
227
+ expect(diffRemovedEntries(prev, next)).toEqual(['a/2', 'b/3']);
228
+ expect(diffRemovedEntries(prev, prev)).toEqual([]);
229
+ expect(diffRemovedEntries([], next)).toEqual([]);
230
+ });
231
+ it('parseBuildManifestArgs reads --allow-removals[=N]', () => {
232
+ expect(parseBuildManifestArgs([]).allowRemovals).toBe(0);
233
+ expect(parseBuildManifestArgs(['--allow-removals=5']).allowRemovals).toBe(5);
234
+ expect(parseBuildManifestArgs(['--allow-removals=0']).allowRemovals).toBe(0);
235
+ // Bare flag is the explicit "I know what I'm doing" escape hatch.
236
+ expect(parseBuildManifestArgs(['--allow-removals']).allowRemovals).toBe(Number.POSITIVE_INFINITY);
237
+ // Unknown flags are ignored.
238
+ expect(parseBuildManifestArgs(['--something-else']).allowRemovals).toBe(0);
239
+ });
160
240
  });
@@ -4,7 +4,7 @@ import { shouldUseBrowserSession } from './capabilityRouting.js';
4
4
  function makeCmd(partial) {
5
5
  return {
6
6
  site: 'test',
7
- name: 'command',
7
+ name: 'command', access: 'read',
8
8
  description: '',
9
9
  args: [],
10
10
  ...partial,