@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
@@ -277,12 +277,13 @@ export function resolveTargetJs(ref, opts = {}) {
277
277
  * Generate JS for click that uses the unified resolver.
278
278
  * Assumes resolveTargetJs has been called and __resolved is set.
279
279
  */
280
- export function clickResolvedJs() {
280
+ export function clickResolvedJs(opts = {}) {
281
+ const shouldScroll = opts.skipScroll ? 'false' : 'true';
281
282
  return `
282
283
  (() => {
283
284
  const el = window.__resolved;
284
285
  if (!el) throw new Error('No resolved element');
285
- el.scrollIntoView({ behavior: 'instant', block: 'center' });
286
+ if (${shouldScroll}) el.scrollIntoView({ behavior: 'instant', block: 'center' });
286
287
  const rect = el.getBoundingClientRect();
287
288
  const x = Math.round(rect.left + rect.width / 2);
288
289
  const y = Math.round(rect.top + rect.height / 2);
@@ -331,16 +332,96 @@ export function typeResolvedJs(text) {
331
332
  })()
332
333
  `;
333
334
  }
335
+ /**
336
+ * Prepare the resolved element for native CDP Input.insertText.
337
+ *
338
+ * This preserves `browser type`'s existing "replace current text" semantics:
339
+ * focus the editable target, select its current contents, then let CDP insert
340
+ * real browser text input so rich editors can update their internal state.
341
+ */
342
+ export function prepareNativeTypeResolvedJs(opts = {}) {
343
+ const shouldScroll = opts.skipScroll ? 'false' : 'true';
344
+ const shouldFocus = opts.skipFocus ? 'false' : 'true';
345
+ return `
346
+ (() => {
347
+ const original = window.__resolved;
348
+ if (!original) throw new Error('No resolved element');
349
+
350
+ function nearestContentEditableHost(el) {
351
+ let current = el;
352
+ while (current && current.nodeType === 1) {
353
+ if (current.hasAttribute && current.hasAttribute('contenteditable')) return current;
354
+ current = current.parentElement;
355
+ }
356
+ return el.isContentEditable ? el : null;
357
+ }
358
+
359
+ const editableHost = original.isContentEditable ? nearestContentEditableHost(original) : null;
360
+ const inputTypes = new Set(['', 'text', 'search', 'url', 'tel', 'email', 'password']);
361
+ const isInput = original instanceof HTMLInputElement;
362
+ const isTextarea = original instanceof HTMLTextAreaElement;
363
+ const isTextControl = isTextarea || (isInput && inputTypes.has((original.getAttribute('type') || original.type || '').toLowerCase()));
364
+ const el = editableHost || (isTextControl ? original : null);
365
+
366
+ if (!el) {
367
+ return {
368
+ ok: false,
369
+ reason: 'not_editable',
370
+ tag: original.tagName ? original.tagName.toLowerCase() : '',
371
+ };
372
+ }
373
+
374
+ window.__resolved = el;
375
+ if (${shouldScroll}) el.scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });
376
+ if (${shouldFocus}) {
377
+ try {
378
+ el.focus({ preventScroll: true });
379
+ } catch (_) {
380
+ el.focus();
381
+ }
382
+ }
383
+
384
+ if (editableHost) {
385
+ const sel = window.getSelection();
386
+ if (!sel) return { ok: false, reason: 'selection_unavailable', mode: 'contenteditable' };
387
+ const range = document.createRange();
388
+ range.selectNodeContents(el);
389
+ sel.removeAllRanges();
390
+ sel.addRange(range);
391
+ return { ok: true, mode: 'contenteditable' };
392
+ }
393
+
394
+ let selected = false;
395
+ try {
396
+ if (typeof el.setSelectionRange === 'function') {
397
+ el.setSelectionRange(0, String(el.value || '').length);
398
+ selected = true;
399
+ }
400
+ } catch (_) {}
401
+ try {
402
+ if (!selected && typeof el.select === 'function') {
403
+ el.select();
404
+ selected = true;
405
+ }
406
+ } catch (_) {}
407
+
408
+ return selected
409
+ ? { ok: true, mode: isTextarea ? 'textarea' : 'input' }
410
+ : { ok: false, reason: 'selection_unavailable', mode: isTextarea ? 'textarea' : 'input' };
411
+ })()
412
+ `;
413
+ }
334
414
  /**
335
415
  * Generate JS for scrollTo that uses the unified resolver.
336
416
  * Assumes resolveTargetJs has been called and __resolved is set.
337
417
  */
338
- export function scrollResolvedJs() {
418
+ export function scrollResolvedJs(opts = {}) {
419
+ const shouldScroll = opts.skipScroll ? 'false' : 'true';
339
420
  return `
340
421
  (() => {
341
422
  const el = window.__resolved;
342
423
  if (!el) throw new Error('No resolved element');
343
- el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
424
+ if (${shouldScroll}) el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
344
425
  return { scrolled: true, tag: el.tagName.toLowerCase(), text: (el.textContent || '').trim().slice(0, 80) };
345
426
  })()
346
427
  `;
@@ -31,11 +31,15 @@ export type Fixture = {
31
31
  expect?: FixtureExpect;
32
32
  };
33
33
  export type ValidationFailure = {
34
- rule: 'rowCount' | 'column' | 'type' | 'pattern' | 'notEmpty' | 'mustNotContain' | 'mustBeTruthy';
34
+ rule: 'rowCount' | 'column' | 'type' | 'pattern' | 'notEmpty' | 'mustNotContain' | 'mustBeTruthy' | 'shapeKeyCount' | 'shapeDepth' | 'shapeNestedId';
35
35
  detail: string;
36
36
  rowIndex?: number;
37
37
  };
38
38
  export type Row = Record<string, unknown>;
39
+ export type RowShapeOptions = {
40
+ maxTopLevelKeys?: number;
41
+ maxNestedDepth?: number;
42
+ };
39
43
  export declare function fixturePath(site: string, command: string): string;
40
44
  export declare function loadFixture(site: string, command: string): Fixture | null;
41
45
  export declare function writeFixture(site: string, command: string, fixture: Fixture): string;
@@ -51,9 +55,11 @@ export declare function writeFixture(site: string, command: string, fixture: Fix
51
55
  */
52
56
  export declare function deriveFixture(rows: Row[], args?: FixtureArgs): Fixture;
53
57
  export declare function validateRows(rows: Row[], fixture: Fixture): ValidationFailure[];
58
+ export declare function validateRowShape(rows: Row[], opts?: RowShapeOptions): ValidationFailure[];
54
59
  /**
55
60
  * Convert fixture args into argv tokens appended after the command name.
56
61
  * - Array form is passed through verbatim (stringified), supporting positional subjects.
57
62
  * - Object form is expanded to `--key value` pairs.
58
63
  */
59
64
  export declare function expandFixtureArgs(args: FixtureArgs | undefined): string[];
65
+ export declare function parseSeedArgs(raw: string | undefined): FixtureArgs | undefined;
@@ -30,6 +30,26 @@
30
30
  import * as fs from 'node:fs';
31
31
  import * as os from 'node:os';
32
32
  import * as path from 'node:path';
33
+ const DEFAULT_MAX_TOP_LEVEL_KEYS = 12;
34
+ const DEFAULT_MAX_NESTED_DEPTH = 1;
35
+ const ID_SHAPED_KEY_PATTERNS = [
36
+ /^id$/i,
37
+ /_id$/i,
38
+ /Id$/,
39
+ /^short_id$/i,
40
+ /^bvid$/i,
41
+ /^aid$/i,
42
+ /^tid$/i,
43
+ /^asin$/i,
44
+ /^sku$/i,
45
+ /^isbn$/i,
46
+ /^doi$/i,
47
+ /^slug$/i,
48
+ /^hn_id$/i,
49
+ /^username$/i,
50
+ /^handle$/i,
51
+ /^uri$/i,
52
+ ];
33
53
  export function fixturePath(site, command) {
34
54
  return path.join(os.homedir(), '.opencli', 'sites', site, 'verify', `${command}.json`);
35
55
  }
@@ -180,6 +200,39 @@ export function validateRows(rows, fixture) {
180
200
  });
181
201
  return failures;
182
202
  }
203
+ export function validateRowShape(rows, opts = {}) {
204
+ const failures = [];
205
+ const maxTopLevelKeys = opts.maxTopLevelKeys ?? DEFAULT_MAX_TOP_LEVEL_KEYS;
206
+ const maxNestedDepth = opts.maxNestedDepth ?? DEFAULT_MAX_NESTED_DEPTH;
207
+ rows.forEach((row, i) => {
208
+ const keys = Object.keys(row);
209
+ if (keys.length > maxTopLevelKeys) {
210
+ failures.push({
211
+ rule: 'shapeKeyCount',
212
+ detail: `row has ${keys.length} top-level keys, expected at most ${maxTopLevelKeys}`,
213
+ rowIndex: i,
214
+ });
215
+ }
216
+ for (const [key, value] of Object.entries(row)) {
217
+ const depth = nestedDepth(value);
218
+ if (depth > maxNestedDepth) {
219
+ failures.push({
220
+ rule: 'shapeDepth',
221
+ detail: `"${key}" nesting depth is ${depth}, expected at most ${maxNestedDepth}`,
222
+ rowIndex: i,
223
+ });
224
+ }
225
+ for (const path of nestedIdPaths(value, key)) {
226
+ failures.push({
227
+ rule: 'shapeNestedId',
228
+ detail: `id-shaped field "${path}" must be a top-level row key`,
229
+ rowIndex: i,
230
+ });
231
+ }
232
+ }
233
+ });
234
+ return failures;
235
+ }
183
236
  /**
184
237
  * Convert fixture args into argv tokens appended after the command name.
185
238
  * - Array form is passed through verbatim (stringified), supporting positional subjects.
@@ -196,6 +249,24 @@ export function expandFixtureArgs(args) {
196
249
  }
197
250
  return out;
198
251
  }
252
+ export function parseSeedArgs(raw) {
253
+ if (raw === undefined)
254
+ return undefined;
255
+ const trimmed = raw.trim();
256
+ if (!trimmed)
257
+ return undefined;
258
+ try {
259
+ const parsed = JSON.parse(trimmed);
260
+ if (Array.isArray(parsed))
261
+ return parsed;
262
+ if (parsed !== null && typeof parsed === 'object')
263
+ return parsed;
264
+ return [parsed];
265
+ }
266
+ catch {
267
+ return [raw];
268
+ }
269
+ }
199
270
  function jsType(v) {
200
271
  if (v === null)
201
272
  return 'null';
@@ -203,6 +274,40 @@ function jsType(v) {
203
274
  return 'array';
204
275
  return typeof v;
205
276
  }
277
+ function nestedDepth(value) {
278
+ if (value === null || value === undefined || typeof value !== 'object')
279
+ return 0;
280
+ if (Array.isArray(value)) {
281
+ if (value.length === 0)
282
+ return 1;
283
+ return 1 + Math.max(...value.map(nestedDepth));
284
+ }
285
+ const values = Object.values(value);
286
+ if (values.length === 0)
287
+ return 1;
288
+ return 1 + Math.max(...values.map(nestedDepth));
289
+ }
290
+ function nestedIdPaths(value, prefix) {
291
+ if (value === null || value === undefined || typeof value !== 'object')
292
+ return [];
293
+ const paths = [];
294
+ if (Array.isArray(value)) {
295
+ value.forEach((item, index) => {
296
+ paths.push(...nestedIdPaths(item, `${prefix}[${index}]`));
297
+ });
298
+ return paths;
299
+ }
300
+ for (const [key, nested] of Object.entries(value)) {
301
+ const childPath = `${prefix}.${key}`;
302
+ if (isIdShapedKey(key))
303
+ paths.push(childPath);
304
+ paths.push(...nestedIdPaths(nested, childPath));
305
+ }
306
+ return paths;
307
+ }
308
+ function isIdShapedKey(key) {
309
+ return ID_SHAPED_KEY_PATTERNS.some((pattern) => pattern.test(key));
310
+ }
206
311
  function typeMatches(actual, declared) {
207
312
  const allowed = declared.split('|').map((s) => s.trim()).filter(Boolean);
208
313
  if (allowed.length === 0)
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { deriveFixture, expandFixtureArgs, validateRows } from './verify-fixture.js';
2
+ import { deriveFixture, expandFixtureArgs, parseSeedArgs, validateRows, validateRowShape } from './verify-fixture.js';
3
3
  describe('validateRows', () => {
4
4
  it('passes when rows meet all expectations', () => {
5
5
  const fixture = {
@@ -98,6 +98,49 @@ describe('validateRows', () => {
98
98
  expect(failures.map((f) => f.rowIndex)).toEqual([1, 2, 3, 4]);
99
99
  });
100
100
  });
101
+ describe('validateRowShape', () => {
102
+ it('passes flat rows with a compact key set', () => {
103
+ const failures = validateRowShape([
104
+ { id: '1', title: 'A', author: { name: 'Ada' }, tags: ['ai', 'web'] },
105
+ ]);
106
+ expect(failures).toEqual([]);
107
+ });
108
+ it('reports rows with too many top-level keys', () => {
109
+ const row = Object.fromEntries(Array.from({ length: 13 }, (_, i) => [`k${i}`, i]));
110
+ const failures = validateRowShape([row]);
111
+ expect(failures).toEqual([
112
+ {
113
+ rule: 'shapeKeyCount',
114
+ detail: 'row has 13 top-level keys, expected at most 12',
115
+ rowIndex: 0,
116
+ },
117
+ ]);
118
+ });
119
+ it('reports nesting deeper than one level', () => {
120
+ const failures = validateRowShape([
121
+ { title: 'A', stats: { author: { name: 'Ada' } } },
122
+ ]);
123
+ expect(failures).toEqual([
124
+ {
125
+ rule: 'shapeDepth',
126
+ detail: '"stats" nesting depth is 2, expected at most 1',
127
+ rowIndex: 0,
128
+ },
129
+ ]);
130
+ });
131
+ it('reports nested id-shaped fields even when one-level nesting is otherwise allowed', () => {
132
+ const failures = validateRowShape([
133
+ { title: 'A', author: { user_id: 'u1', name: 'Ada' } },
134
+ ]);
135
+ expect(failures).toEqual([
136
+ {
137
+ rule: 'shapeNestedId',
138
+ detail: 'id-shaped field "author.user_id" must be a top-level row key',
139
+ rowIndex: 0,
140
+ },
141
+ ]);
142
+ });
143
+ });
101
144
  describe('deriveFixture', () => {
102
145
  it('returns rowCount.min=0 when rows are empty', () => {
103
146
  expect(deriveFixture([])).toEqual({ expect: { rowCount: { min: 0 } } });
@@ -159,3 +202,18 @@ describe('expandFixtureArgs', () => {
159
202
  ]);
160
203
  });
161
204
  });
205
+ describe('parseSeedArgs', () => {
206
+ it('treats plain text as one positional arg', () => {
207
+ expect(parseSeedArgs('opencli-verify')).toEqual(['opencli-verify']);
208
+ });
209
+ it('accepts JSON array seed args', () => {
210
+ expect(parseSeedArgs('["subject", "--limit", 3]')).toEqual(['subject', '--limit', 3]);
211
+ });
212
+ it('accepts JSON object seed args', () => {
213
+ expect(parseSeedArgs('{"limit":3,"sort":"hot"}')).toEqual({ limit: 3, sort: 'hot' });
214
+ });
215
+ it('ignores empty input', () => {
216
+ expect(parseSeedArgs(undefined)).toBeUndefined();
217
+ expect(parseSeedArgs(' ')).toBeUndefined();
218
+ });
219
+ });
@@ -5,41 +5,76 @@
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.
23
+ */
24
+ import type { ManifestEntry } from './manifest-types.js';
25
+ export type { ManifestEntry } from './manifest-types.js';
26
+ /**
27
+ * Thrown by `loadManifestEntries` when an adapter file looks like a CLI
28
+ * module (matches CLI_MODULE_PATTERN) but cannot be imported. Callers
29
+ * decide whether to abort or aggregate failures across the whole scan.
10
30
  */
11
- export interface ManifestEntry {
12
- site: string;
13
- name: string;
14
- aliases?: string[];
15
- description: string;
16
- domain?: string;
17
- strategy: string;
18
- browser: boolean;
19
- args: Array<{
20
- name: string;
21
- type?: string;
22
- default?: unknown;
23
- required?: boolean;
24
- valueRequired?: boolean;
25
- positional?: boolean;
26
- help?: string;
27
- choices?: string[];
28
- }>;
29
- columns?: string[];
30
- pipeline?: Record<string, unknown>[];
31
- timeout?: number;
32
- deprecated?: boolean | string;
33
- replacedBy?: string;
34
- type: 'js';
35
- /** Relative path from clis/ dir, e.g. 'bilibili/search.js' */
36
- modulePath?: string;
37
- /** Relative path to the source file from clis/ dir (e.g. 'site/cmd.js') */
38
- sourceFile?: string;
39
- /** Pre-navigation control — see CliCommand.navigateBefore */
40
- navigateBefore?: boolean | string;
31
+ export declare class ManifestImportError extends Error {
32
+ readonly filePath: string;
33
+ readonly cause: unknown;
34
+ constructor(filePath: string, cause: unknown);
35
+ }
36
+ export interface BuildManifestResult {
37
+ entries: ManifestEntry[];
38
+ /** Adapters that look like CLI modules but failed to import. */
39
+ failures: ManifestImportError[];
40
+ }
41
+ export interface BuildManifestArgs {
42
+ /** Maximum number of entries that may be removed vs the existing manifest.
43
+ * `Number.POSITIVE_INFINITY` disables the safety net entirely. */
44
+ allowRemovals: number;
41
45
  }
42
46
  export declare function normalizeManifestPath(relativePath: string): string;
43
- export declare function loadManifestEntries(filePath: string, site: string, importer?: (moduleHref: string) => Promise<unknown>): Promise<ManifestEntry[]>;
44
- export declare function buildManifest(): Promise<ManifestEntry[]>;
47
+ /**
48
+ * Load all manifest entries from a single adapter file.
49
+ *
50
+ * Returns `[]` for files that do not register a CLI command (helpers, types).
51
+ * Throws `ManifestImportError` when a file looks like a CLI module but its
52
+ * import or post-import processing fails — callers must decide whether to
53
+ * surface or aggregate the failure.
54
+ *
55
+ * The third argument `clisDir` is used to compute the POSIX-style
56
+ * `sourceFile` relative path; it defaults to the package's `clis/` dir so
57
+ * existing test callers stay backward-compatible.
58
+ */
59
+ export declare function loadManifestEntries(filePath: string, site: string, importer?: (moduleHref: string) => Promise<unknown>, clisDir?: string): Promise<ManifestEntry[]>;
60
+ /**
61
+ * Scan a `clis/` directory and aggregate per-adapter results. Import
62
+ * failures are collected in `failures` instead of crashing the whole scan,
63
+ * but the caller (e.g. `main()`) is expected to fail loud if any failure
64
+ * is present.
65
+ */
66
+ export declare function scanClisDir(clisDir: string, importer?: (moduleHref: string) => Promise<unknown>): Promise<BuildManifestResult>;
67
+ export declare function buildManifest(): Promise<BuildManifestResult>;
45
68
  export declare function serializeManifest(manifest: ManifestEntry[]): string;
69
+ /**
70
+ * Diff helper: returns site/name keys that exist in `prev` but not in
71
+ * `next`. Used as a safety net to detect accidental mass-deletions caused
72
+ * by silently failing adapter imports.
73
+ */
74
+ export declare function diffRemovedEntries(prev: readonly ManifestEntry[], next: readonly ManifestEntry[]): string[];
75
+ /**
76
+ * Parse `--allow-removals` and `--allow-removals=N` from argv.
77
+ * Bare `--allow-removals` disables the safety net (`Infinity`); the
78
+ * numeric form sets an explicit upper bound.
79
+ */
80
+ export declare function parseBuildManifestArgs(argv: readonly string[]): BuildManifestArgs;