@jackwener/opencli 1.4.0 → 1.5.0

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 (514) hide show
  1. package/.github/actions/setup-chrome/action.yml +5 -4
  2. package/.github/workflows/build-extension.yml +2 -6
  3. package/.github/workflows/ci.yml +37 -3
  4. package/.github/workflows/e2e-headed.yml +16 -3
  5. package/CHANGELOG.md +23 -0
  6. package/PRIVACY.md +57 -0
  7. package/README.md +36 -7
  8. package/README.zh-CN.md +13 -6
  9. package/SKILL.md +103 -2
  10. package/dist/browser/cdp.d.ts +2 -1
  11. package/dist/browser/discover.d.ts +4 -1
  12. package/dist/browser/discover.js +6 -2
  13. package/dist/browser/errors.d.ts +2 -2
  14. package/dist/browser/errors.js +4 -12
  15. package/dist/browser/mcp.d.ts +2 -1
  16. package/dist/build-manifest.d.ts +2 -0
  17. package/dist/build-manifest.js +39 -14
  18. package/dist/build-manifest.test.js +21 -0
  19. package/dist/capabilityRouting.d.ts +2 -0
  20. package/dist/capabilityRouting.js +2 -1
  21. package/dist/cli-manifest.json +1838 -151
  22. package/dist/cli.js +34 -3
  23. package/dist/clis/36kr/article.d.ts +1 -0
  24. package/dist/clis/36kr/article.js +62 -0
  25. package/dist/clis/36kr/hot.d.ts +3 -0
  26. package/dist/clis/36kr/hot.js +80 -0
  27. package/dist/clis/36kr/hot.test.d.ts +1 -0
  28. package/dist/clis/36kr/hot.test.js +15 -0
  29. package/dist/clis/36kr/news.d.ts +1 -0
  30. package/dist/clis/36kr/news.js +51 -0
  31. package/dist/clis/36kr/news.test.d.ts +1 -0
  32. package/dist/clis/36kr/news.test.js +85 -0
  33. package/dist/clis/36kr/search.d.ts +1 -0
  34. package/dist/clis/36kr/search.js +72 -0
  35. package/dist/clis/apple-podcasts/search.js +2 -1
  36. package/dist/clis/arxiv/search.js +2 -2
  37. package/dist/clis/bbc/news.js +0 -1
  38. package/dist/clis/bilibili/comments.d.ts +5 -0
  39. package/dist/clis/bilibili/comments.js +40 -0
  40. package/dist/clis/bilibili/comments.test.d.ts +1 -0
  41. package/dist/clis/bilibili/comments.test.js +82 -0
  42. package/dist/clis/chatgpt/ask.js +29 -14
  43. package/dist/clis/chatgpt/ax.d.ts +6 -0
  44. package/dist/clis/chatgpt/ax.js +172 -1
  45. package/dist/clis/chatgpt/model.d.ts +1 -0
  46. package/dist/clis/chatgpt/model.js +24 -0
  47. package/dist/clis/chatgpt/send.js +12 -3
  48. package/dist/clis/ctrip/search.js +0 -1
  49. package/dist/clis/douban/download.d.ts +1 -0
  50. package/dist/clis/douban/download.js +67 -0
  51. package/dist/clis/douban/download.test.d.ts +1 -0
  52. package/dist/clis/douban/download.test.js +170 -0
  53. package/dist/clis/douban/photos.d.ts +1 -0
  54. package/dist/clis/douban/photos.js +34 -0
  55. package/dist/clis/douban/utils.d.ts +25 -0
  56. package/dist/clis/douban/utils.js +190 -1
  57. package/dist/clis/douban/utils.test.d.ts +1 -0
  58. package/dist/clis/douban/utils.test.js +64 -0
  59. package/dist/clis/douyin/_shared/browser-fetch.d.ts +10 -0
  60. package/dist/clis/douyin/_shared/browser-fetch.js +30 -0
  61. package/dist/clis/douyin/_shared/browser-fetch.test.d.ts +1 -0
  62. package/dist/clis/douyin/_shared/browser-fetch.test.js +31 -0
  63. package/dist/clis/douyin/_shared/creation-id.d.ts +1 -0
  64. package/dist/clis/douyin/_shared/creation-id.js +5 -0
  65. package/dist/clis/douyin/_shared/creation-id.test.d.ts +1 -0
  66. package/dist/clis/douyin/_shared/creation-id.test.js +22 -0
  67. package/dist/clis/douyin/_shared/imagex-upload.d.ts +20 -0
  68. package/dist/clis/douyin/_shared/imagex-upload.js +53 -0
  69. package/dist/clis/douyin/_shared/imagex-upload.test.d.ts +1 -0
  70. package/dist/clis/douyin/_shared/imagex-upload.test.js +87 -0
  71. package/dist/clis/douyin/_shared/sts2.d.ts +8 -0
  72. package/dist/clis/douyin/_shared/sts2.js +15 -0
  73. package/dist/clis/douyin/_shared/text-extra.d.ts +18 -0
  74. package/dist/clis/douyin/_shared/text-extra.js +15 -0
  75. package/dist/clis/douyin/_shared/text-extra.test.d.ts +1 -0
  76. package/dist/clis/douyin/_shared/text-extra.test.js +37 -0
  77. package/dist/clis/douyin/_shared/timing.d.ts +2 -0
  78. package/dist/clis/douyin/_shared/timing.js +22 -0
  79. package/dist/clis/douyin/_shared/timing.test.d.ts +1 -0
  80. package/dist/clis/douyin/_shared/timing.test.js +28 -0
  81. package/dist/clis/douyin/_shared/tos-upload-short-read.test.d.ts +11 -0
  82. package/dist/clis/douyin/_shared/tos-upload-short-read.test.js +83 -0
  83. package/dist/clis/douyin/_shared/tos-upload.d.ts +53 -0
  84. package/dist/clis/douyin/_shared/tos-upload.js +295 -0
  85. package/dist/clis/douyin/_shared/tos-upload.test.d.ts +1 -0
  86. package/dist/clis/douyin/_shared/tos-upload.test.js +229 -0
  87. package/dist/clis/douyin/_shared/transcode.d.ts +27 -0
  88. package/dist/clis/douyin/_shared/transcode.js +45 -0
  89. package/dist/clis/douyin/_shared/transcode.test.d.ts +1 -0
  90. package/dist/clis/douyin/_shared/transcode.test.js +93 -0
  91. package/dist/clis/douyin/_shared/types.d.ts +26 -0
  92. package/dist/clis/douyin/_shared/types.js +1 -0
  93. package/dist/clis/douyin/activities.d.ts +1 -0
  94. package/dist/clis/douyin/activities.js +20 -0
  95. package/dist/clis/douyin/activities.test.d.ts +1 -0
  96. package/dist/clis/douyin/activities.test.js +22 -0
  97. package/dist/clis/douyin/collections.d.ts +1 -0
  98. package/dist/clis/douyin/collections.js +22 -0
  99. package/dist/clis/douyin/collections.test.d.ts +1 -0
  100. package/dist/clis/douyin/collections.test.js +23 -0
  101. package/dist/clis/douyin/delete.d.ts +1 -0
  102. package/dist/clis/douyin/delete.js +18 -0
  103. package/dist/clis/douyin/delete.test.d.ts +1 -0
  104. package/dist/clis/douyin/delete.test.js +11 -0
  105. package/dist/clis/douyin/draft.d.ts +14 -0
  106. package/dist/clis/douyin/draft.js +237 -0
  107. package/dist/clis/douyin/draft.test.d.ts +1 -0
  108. package/dist/clis/douyin/draft.test.js +11 -0
  109. package/dist/clis/douyin/drafts.d.ts +1 -0
  110. package/dist/clis/douyin/drafts.js +23 -0
  111. package/dist/clis/douyin/drafts.test.d.ts +1 -0
  112. package/dist/clis/douyin/drafts.test.js +11 -0
  113. package/dist/clis/douyin/hashtag.d.ts +1 -0
  114. package/dist/clis/douyin/hashtag.js +45 -0
  115. package/dist/clis/douyin/hashtag.test.d.ts +1 -0
  116. package/dist/clis/douyin/hashtag.test.js +25 -0
  117. package/dist/clis/douyin/location.d.ts +1 -0
  118. package/dist/clis/douyin/location.js +24 -0
  119. package/dist/clis/douyin/location.test.d.ts +1 -0
  120. package/dist/clis/douyin/location.test.js +23 -0
  121. package/dist/clis/douyin/profile.d.ts +1 -0
  122. package/dist/clis/douyin/profile.js +28 -0
  123. package/dist/clis/douyin/profile.test.d.ts +1 -0
  124. package/dist/clis/douyin/profile.test.js +11 -0
  125. package/dist/clis/douyin/publish.d.ts +14 -0
  126. package/dist/clis/douyin/publish.js +288 -0
  127. package/dist/clis/douyin/publish.test.d.ts +1 -0
  128. package/dist/clis/douyin/publish.test.js +38 -0
  129. package/dist/clis/douyin/stats.d.ts +1 -0
  130. package/dist/clis/douyin/stats.js +27 -0
  131. package/dist/clis/douyin/stats.test.d.ts +1 -0
  132. package/dist/clis/douyin/stats.test.js +22 -0
  133. package/dist/clis/douyin/update.d.ts +1 -0
  134. package/dist/clis/douyin/update.js +31 -0
  135. package/dist/clis/douyin/update.test.d.ts +1 -0
  136. package/dist/clis/douyin/update.test.js +11 -0
  137. package/dist/clis/douyin/videos.d.ts +1 -0
  138. package/dist/clis/douyin/videos.js +34 -0
  139. package/dist/clis/douyin/videos.test.d.ts +1 -0
  140. package/dist/clis/douyin/videos.test.js +11 -0
  141. package/dist/clis/hackernews/search.yaml +1 -1
  142. package/dist/clis/imdb/person.d.ts +1 -0
  143. package/dist/clis/imdb/person.js +203 -0
  144. package/dist/clis/imdb/reviews.d.ts +1 -0
  145. package/dist/clis/imdb/reviews.js +88 -0
  146. package/dist/clis/imdb/search.d.ts +1 -0
  147. package/dist/clis/imdb/search.js +161 -0
  148. package/dist/clis/imdb/title.d.ts +1 -0
  149. package/dist/clis/imdb/title.js +93 -0
  150. package/dist/clis/imdb/top.d.ts +1 -0
  151. package/dist/clis/imdb/top.js +53 -0
  152. package/dist/clis/imdb/trending.d.ts +1 -0
  153. package/dist/clis/imdb/trending.js +52 -0
  154. package/dist/clis/imdb/utils.d.ts +46 -0
  155. package/dist/clis/imdb/utils.js +285 -0
  156. package/dist/clis/imdb/utils.test.d.ts +1 -0
  157. package/dist/clis/imdb/utils.test.js +88 -0
  158. package/dist/clis/instagram/search.yaml +2 -1
  159. package/dist/clis/jd/item.d.ts +4 -0
  160. package/dist/clis/jd/item.js +16 -15
  161. package/dist/clis/jd/item.test.js +16 -1
  162. package/dist/clis/linux-do/categories.yaml +38 -9
  163. package/dist/clis/linux-do/category.d.ts +1 -0
  164. package/dist/clis/linux-do/category.js +36 -0
  165. package/dist/clis/linux-do/feed.d.ts +45 -0
  166. package/dist/clis/linux-do/feed.js +397 -0
  167. package/dist/clis/linux-do/feed.test.d.ts +1 -0
  168. package/dist/clis/linux-do/feed.test.js +118 -0
  169. package/dist/clis/linux-do/hot.d.ts +1 -0
  170. package/dist/clis/linux-do/hot.js +25 -0
  171. package/dist/clis/linux-do/latest.d.ts +1 -0
  172. package/dist/clis/linux-do/latest.js +18 -0
  173. package/dist/clis/linux-do/search.yaml +3 -1
  174. package/dist/clis/linux-do/tags.yaml +41 -0
  175. package/dist/clis/linux-do/topic.yaml +41 -3
  176. package/dist/clis/linux-do/user-posts.yaml +67 -0
  177. package/dist/clis/linux-do/user-topics.yaml +54 -0
  178. package/dist/clis/medium/search.js +1 -1
  179. package/dist/clis/paperreview/commands.test.d.ts +3 -0
  180. package/dist/clis/paperreview/commands.test.js +243 -0
  181. package/dist/clis/paperreview/feedback.d.ts +1 -0
  182. package/dist/clis/paperreview/feedback.js +52 -0
  183. package/dist/clis/paperreview/review.d.ts +1 -0
  184. package/dist/clis/paperreview/review.js +37 -0
  185. package/dist/clis/paperreview/submit.d.ts +1 -0
  186. package/dist/clis/paperreview/submit.js +85 -0
  187. package/dist/clis/paperreview/utils.d.ts +46 -0
  188. package/dist/clis/paperreview/utils.js +197 -0
  189. package/dist/clis/paperreview/utils.test.d.ts +1 -0
  190. package/dist/clis/paperreview/utils.test.js +49 -0
  191. package/dist/clis/producthunt/browse.d.ts +1 -0
  192. package/dist/clis/producthunt/browse.js +99 -0
  193. package/dist/clis/producthunt/hot.d.ts +1 -0
  194. package/dist/clis/producthunt/hot.js +110 -0
  195. package/dist/clis/producthunt/posts.d.ts +1 -0
  196. package/dist/clis/producthunt/posts.js +28 -0
  197. package/dist/clis/producthunt/today.d.ts +1 -0
  198. package/dist/clis/producthunt/today.js +35 -0
  199. package/dist/clis/producthunt/utils.d.ts +29 -0
  200. package/dist/clis/producthunt/utils.js +99 -0
  201. package/dist/clis/producthunt/utils.test.d.ts +1 -0
  202. package/dist/clis/producthunt/utils.test.js +64 -0
  203. package/dist/clis/reuters/search.js +0 -1
  204. package/dist/clis/twitter/article.js +4 -28
  205. package/dist/clis/twitter/likes.d.ts +24 -0
  206. package/dist/clis/twitter/likes.js +217 -0
  207. package/dist/clis/twitter/likes.test.d.ts +1 -0
  208. package/dist/clis/twitter/likes.test.js +85 -0
  209. package/dist/clis/twitter/profile.js +4 -28
  210. package/dist/clis/twitter/search.js +7 -4
  211. package/dist/clis/twitter/search.test.js +56 -2
  212. package/dist/clis/twitter/shared.d.ts +6 -0
  213. package/dist/clis/twitter/shared.js +35 -0
  214. package/dist/clis/twitter/timeline.js +2 -13
  215. package/dist/clis/weibo/comments.d.ts +1 -0
  216. package/dist/clis/weibo/comments.js +53 -0
  217. package/dist/clis/weibo/feed.d.ts +1 -0
  218. package/dist/clis/weibo/feed.js +56 -0
  219. package/dist/clis/weibo/hot.js +0 -1
  220. package/dist/clis/weibo/me.d.ts +1 -0
  221. package/dist/clis/weibo/me.js +76 -0
  222. package/dist/clis/weibo/post.d.ts +1 -0
  223. package/dist/clis/weibo/post.js +75 -0
  224. package/dist/clis/weibo/user.d.ts +1 -0
  225. package/dist/clis/weibo/user.js +63 -0
  226. package/dist/clis/weibo/utils.d.ts +6 -0
  227. package/dist/clis/weibo/utils.js +30 -0
  228. package/dist/clis/weixin/download.d.ts +17 -0
  229. package/dist/clis/weixin/download.js +88 -20
  230. package/dist/clis/weread/book.js +2 -2
  231. package/dist/clis/weread/commands.test.d.ts +3 -0
  232. package/dist/clis/weread/commands.test.js +43 -0
  233. package/dist/clis/weread/highlights.js +2 -2
  234. package/dist/clis/weread/notebooks.js +2 -2
  235. package/dist/clis/weread/notes.js +3 -3
  236. package/dist/clis/weread/search.js +3 -2
  237. package/dist/clis/weread/shelf.js +2 -2
  238. package/dist/clis/weread/utils.d.ts +4 -4
  239. package/dist/clis/weread/utils.js +32 -14
  240. package/dist/clis/weread/utils.test.js +1 -28
  241. package/dist/clis/xiaohongshu/comments.d.ts +5 -0
  242. package/dist/clis/xiaohongshu/comments.js +74 -0
  243. package/dist/clis/xiaohongshu/comments.test.d.ts +1 -0
  244. package/dist/clis/xiaohongshu/comments.test.js +79 -0
  245. package/dist/clis/xiaohongshu/publish.js +114 -18
  246. package/dist/clis/xiaohongshu/publish.test.d.ts +1 -0
  247. package/dist/clis/xiaohongshu/publish.test.js +119 -0
  248. package/dist/clis/xueqiu/search.yaml +2 -1
  249. package/dist/clis/yahoo-finance/quote.js +0 -1
  250. package/dist/clis/youtube/channel.d.ts +1 -0
  251. package/dist/clis/youtube/channel.js +150 -0
  252. package/dist/clis/youtube/comments.d.ts +1 -0
  253. package/dist/clis/youtube/comments.js +95 -0
  254. package/dist/clis/youtube/search.js +0 -1
  255. package/dist/clis/zhihu/search.yaml +2 -1
  256. package/dist/commanderAdapter.d.ts +1 -0
  257. package/dist/commanderAdapter.js +176 -29
  258. package/dist/commanderAdapter.test.d.ts +1 -0
  259. package/dist/commanderAdapter.test.js +62 -0
  260. package/dist/daemon.js +17 -1
  261. package/dist/discovery.js +8 -14
  262. package/dist/doctor.d.ts +1 -0
  263. package/dist/doctor.js +9 -2
  264. package/dist/download/index.js +63 -51
  265. package/dist/download/index.test.js +17 -4
  266. package/dist/errors.d.ts +3 -1
  267. package/dist/errors.js +15 -32
  268. package/dist/execution.d.ts +1 -3
  269. package/dist/execution.js +21 -1
  270. package/dist/external-clis.yaml +0 -17
  271. package/dist/hooks.js +2 -0
  272. package/dist/main.js +5 -0
  273. package/dist/output.js +5 -1
  274. package/dist/pipeline/executor.js +3 -4
  275. package/dist/plugin-manifest.d.ts +70 -0
  276. package/dist/plugin-manifest.js +160 -0
  277. package/dist/plugin-manifest.test.d.ts +4 -0
  278. package/dist/plugin-manifest.test.js +179 -0
  279. package/dist/plugin.d.ts +38 -5
  280. package/dist/plugin.js +267 -33
  281. package/dist/plugin.test.js +220 -3
  282. package/dist/registry.d.ts +4 -0
  283. package/dist/registry.js +2 -0
  284. package/dist/runtime-detect.d.ts +21 -0
  285. package/dist/runtime-detect.js +32 -0
  286. package/dist/runtime-detect.test.d.ts +1 -0
  287. package/dist/runtime-detect.test.js +27 -0
  288. package/dist/runtime.js +1 -1
  289. package/dist/serialization.d.ts +2 -0
  290. package/dist/serialization.js +6 -0
  291. package/dist/types.d.ts +1 -0
  292. package/dist/update-check.d.ts +22 -0
  293. package/dist/update-check.js +112 -0
  294. package/dist/weixin-download.test.d.ts +1 -0
  295. package/dist/weixin-download.test.js +30 -0
  296. package/dist/weread-private-api-regression.test.d.ts +1 -0
  297. package/dist/weread-private-api-regression.test.js +122 -0
  298. package/dist/weread-search-regression.test.d.ts +1 -0
  299. package/dist/weread-search-regression.test.js +39 -0
  300. package/dist/yaml-schema.d.ts +3 -0
  301. package/dist/yaml-schema.js +18 -1
  302. package/docs/.vitepress/config.mts +17 -0
  303. package/docs/adapters/browser/36kr.md +47 -0
  304. package/docs/adapters/browser/douban.md +14 -0
  305. package/docs/adapters/browser/douyin.md +75 -0
  306. package/docs/adapters/browser/imdb.md +47 -0
  307. package/docs/adapters/browser/jd.md +2 -2
  308. package/docs/adapters/browser/linux-do.md +181 -20
  309. package/docs/adapters/browser/paperreview.md +43 -0
  310. package/docs/adapters/browser/producthunt.md +49 -0
  311. package/docs/adapters/browser/twitter.md +6 -0
  312. package/docs/adapters/desktop/chatgpt.md +5 -0
  313. package/docs/adapters/index.md +12 -3
  314. package/docs/advanced/download.md +4 -0
  315. package/docs/advanced/rate-limiter-plugin.md +99 -0
  316. package/docs/guide/electron-app-cli.md +200 -0
  317. package/docs/guide/getting-started.md +1 -0
  318. package/docs/guide/plugins.md +87 -0
  319. package/docs/zh/guide/electron-app-cli.md +188 -0
  320. package/docs/zh/guide/getting-started.md +1 -0
  321. package/docs/zh/guide/plugins.md +65 -0
  322. package/extension/dist/background.js +508 -518
  323. package/extension/manifest.json +6 -2
  324. package/extension/package.json +2 -1
  325. package/extension/popup.html +84 -0
  326. package/extension/popup.js +25 -0
  327. package/extension/scripts/package-release.mjs +179 -0
  328. package/extension/src/background.ts +22 -1
  329. package/package.json +4 -1
  330. package/scripts/postinstall.js +10 -0
  331. package/src/browser/cdp.ts +2 -1
  332. package/src/browser/discover.ts +8 -3
  333. package/src/browser/errors.ts +13 -14
  334. package/src/browser/mcp.ts +2 -1
  335. package/src/build-manifest.test.ts +23 -0
  336. package/src/build-manifest.ts +40 -15
  337. package/src/capabilityRouting.ts +2 -1
  338. package/src/cli.ts +35 -3
  339. package/src/clis/36kr/article.ts +69 -0
  340. package/src/clis/36kr/hot.test.ts +19 -0
  341. package/src/clis/36kr/hot.ts +100 -0
  342. package/src/clis/36kr/news.test.ts +90 -0
  343. package/src/clis/36kr/news.ts +54 -0
  344. package/src/clis/36kr/search.ts +78 -0
  345. package/src/clis/apple-podcasts/search.ts +2 -1
  346. package/src/clis/arxiv/search.ts +2 -2
  347. package/src/clis/bbc/news.ts +0 -1
  348. package/src/clis/bilibili/comments.test.ts +102 -0
  349. package/src/clis/bilibili/comments.ts +44 -0
  350. package/src/clis/chatgpt/ask.ts +28 -14
  351. package/src/clis/chatgpt/ax.ts +180 -1
  352. package/src/clis/chatgpt/model.ts +27 -0
  353. package/src/clis/chatgpt/send.ts +16 -6
  354. package/src/clis/ctrip/search.ts +0 -1
  355. package/src/clis/douban/download.test.ts +196 -0
  356. package/src/clis/douban/download.ts +78 -0
  357. package/src/clis/douban/photos.ts +36 -0
  358. package/src/clis/douban/utils.test.ts +97 -0
  359. package/src/clis/douban/utils.ts +232 -1
  360. package/src/clis/douyin/_shared/browser-fetch.test.ts +38 -0
  361. package/src/clis/douyin/_shared/browser-fetch.ts +45 -0
  362. package/src/clis/douyin/_shared/creation-id.test.ts +26 -0
  363. package/src/clis/douyin/_shared/creation-id.ts +8 -0
  364. package/src/clis/douyin/_shared/imagex-upload.test.ts +113 -0
  365. package/src/clis/douyin/_shared/imagex-upload.ts +76 -0
  366. package/src/clis/douyin/_shared/sts2.ts +20 -0
  367. package/src/clis/douyin/_shared/text-extra.test.ts +42 -0
  368. package/src/clis/douyin/_shared/text-extra.ts +33 -0
  369. package/src/clis/douyin/_shared/timing.test.ts +38 -0
  370. package/src/clis/douyin/_shared/timing.ts +22 -0
  371. package/src/clis/douyin/_shared/tos-upload-short-read.test.ts +102 -0
  372. package/src/clis/douyin/_shared/tos-upload.test.ts +281 -0
  373. package/src/clis/douyin/_shared/tos-upload.ts +444 -0
  374. package/src/clis/douyin/_shared/transcode.test.ts +117 -0
  375. package/src/clis/douyin/_shared/transcode.ts +78 -0
  376. package/src/clis/douyin/_shared/types.ts +29 -0
  377. package/src/clis/douyin/activities.test.ts +25 -0
  378. package/src/clis/douyin/activities.ts +23 -0
  379. package/src/clis/douyin/collections.test.ts +26 -0
  380. package/src/clis/douyin/collections.ts +25 -0
  381. package/src/clis/douyin/delete.test.ts +12 -0
  382. package/src/clis/douyin/delete.ts +20 -0
  383. package/src/clis/douyin/draft.test.ts +12 -0
  384. package/src/clis/douyin/draft.ts +282 -0
  385. package/src/clis/douyin/drafts.test.ts +12 -0
  386. package/src/clis/douyin/drafts.ts +27 -0
  387. package/src/clis/douyin/hashtag.test.ts +28 -0
  388. package/src/clis/douyin/hashtag.ts +56 -0
  389. package/src/clis/douyin/location.test.ts +26 -0
  390. package/src/clis/douyin/location.ts +27 -0
  391. package/src/clis/douyin/profile.test.ts +12 -0
  392. package/src/clis/douyin/profile.ts +37 -0
  393. package/src/clis/douyin/publish.test.ts +45 -0
  394. package/src/clis/douyin/publish.ts +340 -0
  395. package/src/clis/douyin/stats.test.ts +25 -0
  396. package/src/clis/douyin/stats.ts +30 -0
  397. package/src/clis/douyin/update.test.ts +12 -0
  398. package/src/clis/douyin/update.ts +43 -0
  399. package/src/clis/douyin/videos.test.ts +12 -0
  400. package/src/clis/douyin/videos.ts +49 -0
  401. package/src/clis/hackernews/search.yaml +1 -1
  402. package/src/clis/imdb/person.ts +232 -0
  403. package/src/clis/imdb/reviews.ts +111 -0
  404. package/src/clis/imdb/search.ts +179 -0
  405. package/src/clis/imdb/title.ts +121 -0
  406. package/src/clis/imdb/top.ts +67 -0
  407. package/src/clis/imdb/trending.ts +66 -0
  408. package/src/clis/imdb/utils.test.ts +117 -0
  409. package/src/clis/imdb/utils.ts +305 -0
  410. package/src/clis/instagram/search.yaml +2 -1
  411. package/src/clis/jd/item.test.ts +18 -1
  412. package/src/clis/jd/item.ts +18 -15
  413. package/src/clis/linux-do/categories.yaml +38 -9
  414. package/src/clis/linux-do/category.ts +37 -0
  415. package/src/clis/linux-do/feed.test.ts +132 -0
  416. package/src/clis/linux-do/feed.ts +501 -0
  417. package/src/clis/linux-do/hot.ts +26 -0
  418. package/src/clis/linux-do/latest.ts +19 -0
  419. package/src/clis/linux-do/search.yaml +3 -1
  420. package/src/clis/linux-do/tags.yaml +41 -0
  421. package/src/clis/linux-do/topic.yaml +41 -3
  422. package/src/clis/linux-do/user-posts.yaml +67 -0
  423. package/src/clis/linux-do/user-topics.yaml +54 -0
  424. package/src/clis/medium/search.ts +1 -1
  425. package/src/clis/paperreview/commands.test.ts +283 -0
  426. package/src/clis/paperreview/feedback.ts +64 -0
  427. package/src/clis/paperreview/review.ts +47 -0
  428. package/src/clis/paperreview/submit.ts +119 -0
  429. package/src/clis/paperreview/utils.test.ts +68 -0
  430. package/src/clis/paperreview/utils.ts +276 -0
  431. package/src/clis/producthunt/browse.ts +109 -0
  432. package/src/clis/producthunt/hot.ts +127 -0
  433. package/src/clis/producthunt/posts.ts +29 -0
  434. package/src/clis/producthunt/today.ts +37 -0
  435. package/src/clis/producthunt/utils.test.ts +72 -0
  436. package/src/clis/producthunt/utils.ts +122 -0
  437. package/src/clis/reuters/search.ts +0 -1
  438. package/src/clis/twitter/article.ts +5 -28
  439. package/src/clis/twitter/likes.test.ts +91 -0
  440. package/src/clis/twitter/likes.ts +256 -0
  441. package/src/clis/twitter/profile.ts +5 -28
  442. package/src/clis/twitter/search.test.ts +71 -2
  443. package/src/clis/twitter/search.ts +8 -4
  444. package/src/clis/twitter/shared.ts +45 -0
  445. package/src/clis/twitter/timeline.ts +2 -13
  446. package/src/clis/weibo/comments.ts +54 -0
  447. package/src/clis/weibo/feed.ts +57 -0
  448. package/src/clis/weibo/hot.ts +0 -1
  449. package/src/clis/weibo/me.ts +77 -0
  450. package/src/clis/weibo/post.ts +77 -0
  451. package/src/clis/weibo/user.ts +64 -0
  452. package/src/clis/weibo/utils.ts +32 -0
  453. package/src/clis/weixin/download.ts +114 -20
  454. package/src/clis/weread/book.ts +2 -2
  455. package/src/clis/weread/commands.test.ts +57 -0
  456. package/src/clis/weread/highlights.ts +2 -2
  457. package/src/clis/weread/notebooks.ts +2 -2
  458. package/src/clis/weread/notes.ts +3 -3
  459. package/src/clis/weread/search.ts +3 -2
  460. package/src/clis/weread/shelf.ts +2 -2
  461. package/src/clis/weread/utils.test.ts +1 -32
  462. package/src/clis/weread/utils.ts +41 -16
  463. package/src/clis/xiaohongshu/comments.test.ts +96 -0
  464. package/src/clis/xiaohongshu/comments.ts +81 -0
  465. package/src/clis/xiaohongshu/publish.test.ts +137 -0
  466. package/src/clis/xiaohongshu/publish.ts +129 -18
  467. package/src/clis/xueqiu/search.yaml +2 -1
  468. package/src/clis/yahoo-finance/quote.ts +0 -1
  469. package/src/clis/youtube/channel.ts +155 -0
  470. package/src/clis/youtube/comments.ts +97 -0
  471. package/src/clis/youtube/search.ts +0 -1
  472. package/src/clis/zhihu/search.yaml +2 -1
  473. package/src/commanderAdapter.test.ts +78 -0
  474. package/src/commanderAdapter.ts +188 -24
  475. package/src/daemon.ts +19 -1
  476. package/src/discovery.ts +8 -15
  477. package/src/doctor.ts +13 -2
  478. package/src/download/index.test.ts +14 -4
  479. package/src/download/index.ts +67 -55
  480. package/src/errors.ts +25 -66
  481. package/src/execution.ts +28 -3
  482. package/src/external-clis.yaml +0 -17
  483. package/src/hooks.ts +1 -0
  484. package/src/main.ts +6 -0
  485. package/src/output.ts +3 -1
  486. package/src/pipeline/executor.ts +4 -6
  487. package/src/plugin-manifest.test.ts +223 -0
  488. package/src/plugin-manifest.ts +206 -0
  489. package/src/plugin.test.ts +246 -2
  490. package/src/plugin.ts +338 -36
  491. package/src/registry.ts +6 -1
  492. package/src/runtime-detect.test.ts +30 -0
  493. package/src/runtime-detect.ts +36 -0
  494. package/src/runtime.ts +1 -1
  495. package/src/serialization.ts +4 -0
  496. package/src/types.ts +1 -0
  497. package/src/update-check.ts +114 -0
  498. package/src/weixin-download.test.ts +64 -0
  499. package/src/weread-private-api-regression.test.ts +150 -0
  500. package/src/weread-search-regression.test.ts +44 -0
  501. package/src/yaml-schema.ts +20 -0
  502. package/tests/e2e/browser-auth.test.ts +13 -9
  503. package/tests/e2e/browser-public-extended.test.ts +162 -0
  504. package/tests/e2e/browser-public.test.ts +55 -136
  505. package/tests/e2e/helpers.ts +2 -1
  506. package/tests/e2e/public-commands.test.ts +37 -3
  507. package/tests/smoke/api-health.test.ts +1 -1
  508. package/vitest.config.ts +34 -17
  509. package/dist/clis/linux-do/category.yaml +0 -51
  510. package/dist/clis/linux-do/hot.yaml +0 -50
  511. package/dist/clis/linux-do/latest.yaml +0 -40
  512. package/src/clis/linux-do/category.yaml +0 -51
  513. package/src/clis/linux-do/hot.yaml +0 -50
  514. package/src/clis/linux-do/latest.yaml +0 -40
@@ -0,0 +1,67 @@
1
+ site: linux-do
2
+ name: user-posts
3
+ description: linux.do 用户的帖子
4
+ domain: linux.do
5
+ strategy: cookie
6
+ browser: true
7
+
8
+ args:
9
+ username:
10
+ positional: true
11
+ type: str
12
+ required: true
13
+ description: Username
14
+ limit:
15
+ type: int
16
+ default: 20
17
+ description: Number of posts
18
+
19
+ pipeline:
20
+ - navigate: https://linux.do
21
+
22
+ - evaluate: |
23
+ (async () => {
24
+ const username = ${{ args.username | json }};
25
+ const toLocalTime = (utcStr) => {
26
+ if (!utcStr) return '';
27
+ const date = new Date(utcStr);
28
+ return Number.isNaN(date.getTime()) ? utcStr : date.toLocaleString();
29
+ };
30
+ const strip = (html) => (html || '')
31
+ .replace(/<br\s*\/?>/gi, ' ')
32
+ .replace(/<\/(p|div|li|blockquote|h[1-6])>/gi, ' ')
33
+ .replace(/<[^>]+>/g, '')
34
+ .replace(/&nbsp;/g, ' ')
35
+ .replace(/&amp;/g, '&')
36
+ .replace(/&lt;/g, '<')
37
+ .replace(/&gt;/g, '>')
38
+ .replace(/&quot;/g, '"')
39
+ .replace(/&#(?:(\d+)|x([0-9a-fA-F]+));/g, (_, dec, hex) => {
40
+ try { return String.fromCodePoint(dec !== undefined ? Number(dec) : parseInt(hex, 16)); } catch { return ''; }
41
+ })
42
+ .replace(/\s+/g, ' ')
43
+ .trim();
44
+ const limit = ${{ args.limit | default(20) }};
45
+ const res = await fetch('/user_actions.json?username=' + encodeURIComponent(username) + '&filter=5&offset=0&limit=' + limit, { credentials: 'include' });
46
+ if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
47
+ let data;
48
+ try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
49
+ const actions = data?.user_actions || [];
50
+ return actions.slice(0, limit).map(a => ({
51
+ author: a.acting_username || a.username || '',
52
+ title: a.title || '',
53
+ content: strip(a.excerpt).slice(0, 200),
54
+ created_at: toLocalTime(a.created_at),
55
+ url: 'https://linux.do/t/topic/' + a.topic_id + '/' + a.post_number,
56
+ }));
57
+ })()
58
+
59
+ - map:
60
+ index: ${{ index + 1 }}
61
+ topic_user: ${{ item.author }}
62
+ topic: ${{ item.title }}
63
+ reply: ${{ item.content }}
64
+ time: ${{ item.created_at }}
65
+ url: ${{ item.url }}
66
+
67
+ columns: [index, topic_user, topic, reply, time, url]
@@ -0,0 +1,54 @@
1
+ site: linux-do
2
+ name: user-topics
3
+ description: linux.do 用户创建的话题
4
+ domain: linux.do
5
+ strategy: cookie
6
+ browser: true
7
+
8
+ args:
9
+ username:
10
+ positional: true
11
+ type: str
12
+ required: true
13
+ description: Username
14
+ limit:
15
+ type: int
16
+ default: 20
17
+ description: Number of topics
18
+
19
+ pipeline:
20
+ - navigate: https://linux.do
21
+
22
+ - evaluate: |
23
+ (async () => {
24
+ const username = ${{ args.username | json }};
25
+ const toLocalTime = (utcStr) => {
26
+ if (!utcStr) return '';
27
+ const date = new Date(utcStr);
28
+ return Number.isNaN(date.getTime()) ? utcStr : date.toLocaleString();
29
+ };
30
+ const res = await fetch('/topics/created-by/' + encodeURIComponent(username) + '.json', { credentials: 'include' });
31
+ if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
32
+ let data;
33
+ try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
34
+ const topics = data?.topic_list?.topics || [];
35
+ return topics.slice(0, ${{ args.limit }}).map(t => ({
36
+ title: t.fancy_title || t.title || '',
37
+ replies: t.posts_count || 0,
38
+ created_at: toLocalTime(t.created_at),
39
+ likes: t.like_count || 0,
40
+ views: t.views || 0,
41
+ url: 'https://linux.do/t/topic/' + t.id,
42
+ }));
43
+ })()
44
+
45
+ - map:
46
+ rank: ${{ index + 1 }}
47
+ title: ${{ item.title }}
48
+ replies: ${{ item.replies }}
49
+ created_at: ${{ item.created_at }}
50
+ likes: ${{ item.likes }}
51
+ views: ${{ item.views }}
52
+ url: ${{ item.url }}
53
+
54
+ columns: [rank, title, replies, created_at, likes, views, url]
@@ -11,6 +11,6 @@ cli({
11
11
  { name: 'keyword', required: true, positional: true, help: '搜索关键词' },
12
12
  { name: 'limit', type: 'int', default: 20, help: '返回的文章数量' },
13
13
  ],
14
- columns: ['rank', 'title', 'author', 'date', 'readTime', 'claps'],
14
+ columns: ['rank', 'title', 'author', 'date', 'readTime', 'claps', 'url'],
15
15
  func: async (page, args) => loadMediumPosts(page, buildMediumSearchUrl(args.keyword), Number(args.limit) || 20),
16
16
  });
@@ -0,0 +1,283 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+
3
+ const {
4
+ mockReadPdfFile,
5
+ mockRequestJson,
6
+ mockUploadPresignedPdf,
7
+ mockValidateHelpfulness,
8
+ mockParseYesNo,
9
+ } = vi.hoisted(() => ({
10
+ mockReadPdfFile: vi.fn(),
11
+ mockRequestJson: vi.fn(),
12
+ mockUploadPresignedPdf: vi.fn(),
13
+ mockValidateHelpfulness: vi.fn(),
14
+ mockParseYesNo: vi.fn(),
15
+ }));
16
+
17
+ vi.mock('./utils.js', async () => {
18
+ const actual = await vi.importActual<typeof import('./utils.js')>('./utils.js');
19
+ return {
20
+ ...actual,
21
+ readPdfFile: mockReadPdfFile,
22
+ requestJson: mockRequestJson,
23
+ uploadPresignedPdf: mockUploadPresignedPdf,
24
+ validateHelpfulness: mockValidateHelpfulness,
25
+ parseYesNo: mockParseYesNo,
26
+ };
27
+ });
28
+
29
+ import { getRegistry } from '../../registry.js';
30
+ import './submit.js';
31
+ import './review.js';
32
+ import './feedback.js';
33
+
34
+ describe('paperreview submit command', () => {
35
+ beforeEach(() => {
36
+ mockReadPdfFile.mockReset();
37
+ mockRequestJson.mockReset();
38
+ mockUploadPresignedPdf.mockReset();
39
+ mockValidateHelpfulness.mockReset();
40
+ mockParseYesNo.mockReset();
41
+ });
42
+
43
+ it('supports dry run without any remote request', async () => {
44
+ const cmd = getRegistry().get('paperreview/submit');
45
+ expect(cmd?.func).toBeTypeOf('function');
46
+
47
+ mockReadPdfFile.mockResolvedValue({
48
+ buffer: Buffer.from('%PDF'),
49
+ fileName: 'paper.pdf',
50
+ resolvedPath: '/tmp/paper.pdf',
51
+ sizeBytes: 4096,
52
+ });
53
+
54
+ const result = await cmd!.func!(null as any, {
55
+ pdf: './paper.pdf',
56
+ email: 'wang2629651228@gmail.com',
57
+ venue: 'RAL',
58
+ 'dry-run': true,
59
+ 'prepare-only': false,
60
+ });
61
+
62
+ expect(mockRequestJson).not.toHaveBeenCalled();
63
+ expect(result).toMatchObject({
64
+ status: 'dry-run',
65
+ file: 'paper.pdf',
66
+ email: 'wang2629651228@gmail.com',
67
+ venue: 'RAL',
68
+ });
69
+ });
70
+
71
+ it('treats explicit false flags as false and performs the real submission path', async () => {
72
+ const cmd = getRegistry().get('paperreview/submit');
73
+ expect(cmd?.func).toBeTypeOf('function');
74
+
75
+ mockReadPdfFile.mockResolvedValue({
76
+ buffer: Buffer.from('%PDF'),
77
+ fileName: 'paper.pdf',
78
+ resolvedPath: '/tmp/paper.pdf',
79
+ sizeBytes: 4096,
80
+ });
81
+ mockRequestJson
82
+ .mockResolvedValueOnce({
83
+ response: { ok: true, status: 200 } as Response,
84
+ payload: {
85
+ success: true,
86
+ presigned_url: 'https://upload.example.com',
87
+ presigned_fields: { key: 'uploads/paper.pdf' },
88
+ s3_key: 'uploads/paper.pdf',
89
+ },
90
+ })
91
+ .mockResolvedValueOnce({
92
+ response: { ok: true, status: 200 } as Response,
93
+ payload: {
94
+ success: true,
95
+ token: 'tok_false',
96
+ message: 'Submission accepted',
97
+ },
98
+ });
99
+
100
+ const result = await cmd!.func!(null as any, {
101
+ pdf: './paper.pdf',
102
+ email: 'wang2629651228@gmail.com',
103
+ venue: 'RAL',
104
+ 'dry-run': false,
105
+ 'prepare-only': false,
106
+ });
107
+
108
+ expect(mockUploadPresignedPdf).toHaveBeenCalledTimes(1);
109
+ expect(result).toMatchObject({
110
+ status: 'submitted',
111
+ token: 'tok_false',
112
+ review_url: 'https://paperreview.ai/review?token=tok_false',
113
+ });
114
+ });
115
+
116
+ it('supports prepare-only without uploading the PDF', async () => {
117
+ const cmd = getRegistry().get('paperreview/submit');
118
+ expect(cmd?.func).toBeTypeOf('function');
119
+
120
+ mockReadPdfFile.mockResolvedValue({
121
+ buffer: Buffer.from('%PDF'),
122
+ fileName: 'paper.pdf',
123
+ resolvedPath: '/tmp/paper.pdf',
124
+ sizeBytes: 4096,
125
+ });
126
+ mockRequestJson.mockResolvedValueOnce({
127
+ response: { ok: true, status: 200 } as Response,
128
+ payload: {
129
+ success: true,
130
+ presigned_url: 'https://upload.example.com',
131
+ presigned_fields: { key: 'uploads/paper.pdf' },
132
+ s3_key: 'uploads/paper.pdf',
133
+ },
134
+ });
135
+
136
+ const result = await cmd!.func!(null as any, {
137
+ pdf: './paper.pdf',
138
+ email: 'wang2629651228@gmail.com',
139
+ venue: 'RAL',
140
+ 'dry-run': false,
141
+ 'prepare-only': true,
142
+ });
143
+
144
+ expect(mockUploadPresignedPdf).not.toHaveBeenCalled();
145
+ expect(mockRequestJson).toHaveBeenCalledTimes(1);
146
+ expect(result).toMatchObject({
147
+ status: 'prepared',
148
+ s3_key: 'uploads/paper.pdf',
149
+ });
150
+ });
151
+
152
+ it('requests an upload URL, uploads the PDF, and confirms the submission', async () => {
153
+ const cmd = getRegistry().get('paperreview/submit');
154
+ expect(cmd?.func).toBeTypeOf('function');
155
+
156
+ mockReadPdfFile.mockResolvedValue({
157
+ buffer: Buffer.from('%PDF'),
158
+ fileName: 'paper.pdf',
159
+ resolvedPath: '/tmp/paper.pdf',
160
+ sizeBytes: 4096,
161
+ });
162
+ mockRequestJson
163
+ .mockResolvedValueOnce({
164
+ response: { ok: true, status: 200 } as Response,
165
+ payload: {
166
+ success: true,
167
+ presigned_url: 'https://upload.example.com',
168
+ presigned_fields: { key: 'uploads/paper.pdf' },
169
+ s3_key: 'uploads/paper.pdf',
170
+ },
171
+ })
172
+ .mockResolvedValueOnce({
173
+ response: { ok: true, status: 200 } as Response,
174
+ payload: {
175
+ success: true,
176
+ token: 'tok_123',
177
+ message: 'Submission accepted',
178
+ },
179
+ });
180
+
181
+ const result = await cmd!.func!(null as any, {
182
+ pdf: './paper.pdf',
183
+ email: 'wang2629651228@gmail.com',
184
+ venue: 'RAL',
185
+ 'dry-run': false,
186
+ 'prepare-only': false,
187
+ });
188
+
189
+ expect(mockRequestJson).toHaveBeenNthCalledWith(1, '/api/get-upload-url', expect.objectContaining({
190
+ method: 'POST',
191
+ body: JSON.stringify({
192
+ filename: 'paper.pdf',
193
+ venue: 'RAL',
194
+ }),
195
+ }));
196
+ expect(mockUploadPresignedPdf).toHaveBeenCalledWith(
197
+ 'https://upload.example.com',
198
+ expect.objectContaining({ fileName: 'paper.pdf' }),
199
+ expect.objectContaining({ s3_key: 'uploads/paper.pdf' }),
200
+ );
201
+ expect(mockRequestJson).toHaveBeenNthCalledWith(2, '/api/confirm-upload', expect.objectContaining({
202
+ method: 'POST',
203
+ body: expect.any(FormData),
204
+ }));
205
+ expect(result).toMatchObject({
206
+ status: 'submitted',
207
+ token: 'tok_123',
208
+ review_url: 'https://paperreview.ai/review?token=tok_123',
209
+ });
210
+ });
211
+ });
212
+
213
+ describe('paperreview review command', () => {
214
+ beforeEach(() => {
215
+ mockRequestJson.mockReset();
216
+ });
217
+
218
+ it('returns processing status when the review is not ready yet', async () => {
219
+ const cmd = getRegistry().get('paperreview/review');
220
+ expect(cmd?.func).toBeTypeOf('function');
221
+
222
+ mockRequestJson.mockResolvedValue({
223
+ response: { status: 202 } as Response,
224
+ payload: { detail: 'Review is still processing.' },
225
+ });
226
+
227
+ const result = await cmd!.func!(null as any, { token: 'tok_123' });
228
+
229
+ expect(result).toMatchObject({
230
+ status: 'processing',
231
+ token: 'tok_123',
232
+ review_url: 'https://paperreview.ai/review?token=tok_123',
233
+ message: 'Review is still processing.',
234
+ });
235
+ });
236
+ });
237
+
238
+ describe('paperreview feedback command', () => {
239
+ beforeEach(() => {
240
+ mockRequestJson.mockReset();
241
+ mockValidateHelpfulness.mockReset();
242
+ mockParseYesNo.mockReset();
243
+ });
244
+
245
+ it('normalizes feedback inputs and posts them to the API', async () => {
246
+ const cmd = getRegistry().get('paperreview/feedback');
247
+ expect(cmd?.func).toBeTypeOf('function');
248
+
249
+ mockValidateHelpfulness.mockReturnValue(4);
250
+ mockParseYesNo.mockReturnValueOnce(true).mockReturnValueOnce(false);
251
+ mockRequestJson.mockResolvedValue({
252
+ response: { ok: true, status: 200 } as Response,
253
+ payload: { message: 'Thanks for the feedback.' },
254
+ });
255
+
256
+ const result = await cmd!.func!(null as any, {
257
+ token: 'tok_123',
258
+ helpfulness: 4,
259
+ 'critical-error': 'yes',
260
+ 'actionable-suggestions': 'no',
261
+ 'additional-comments': 'Helpful summary, but the contribution section needs more detail.',
262
+ });
263
+
264
+ expect(mockRequestJson).toHaveBeenCalledWith('/api/feedback/tok_123', {
265
+ method: 'POST',
266
+ headers: { 'Content-Type': 'application/json' },
267
+ body: JSON.stringify({
268
+ helpfulness: 4,
269
+ has_critical_error: true,
270
+ has_actionable_suggestions: false,
271
+ additional_comments: 'Helpful summary, but the contribution section needs more detail.',
272
+ }),
273
+ });
274
+ expect(result).toMatchObject({
275
+ status: 'submitted',
276
+ token: 'tok_123',
277
+ helpfulness: 4,
278
+ critical_error: true,
279
+ actionable_suggestions: false,
280
+ message: 'Thanks for the feedback.',
281
+ });
282
+ });
283
+ });
@@ -0,0 +1,64 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import { CliError } from '../../errors.js';
3
+ import {
4
+ PAPERREVIEW_DOMAIN,
5
+ ensureSuccess,
6
+ parseYesNo,
7
+ requestJson,
8
+ summarizeFeedback,
9
+ validateHelpfulness,
10
+ } from './utils.js';
11
+
12
+ cli({
13
+ site: 'paperreview',
14
+ name: 'feedback',
15
+ description: 'Submit feedback for a paperreview.ai review token',
16
+ domain: PAPERREVIEW_DOMAIN,
17
+ strategy: Strategy.PUBLIC,
18
+ browser: false,
19
+ timeoutSeconds: 30,
20
+ args: [
21
+ { name: 'token', positional: true, required: true, help: 'Review token returned by paperreview.ai' },
22
+ { name: 'helpfulness', required: true, type: 'int', help: 'Helpfulness score from 1 to 5' },
23
+ { name: 'critical-error', required: true, choices: ['yes', 'no'], help: 'Whether the review contains a critical error' },
24
+ { name: 'actionable-suggestions', required: true, choices: ['yes', 'no'], help: 'Whether the review contains actionable suggestions' },
25
+ { name: 'additional-comments', help: 'Optional free-text feedback' },
26
+ ],
27
+ columns: ['status', 'token', 'helpfulness', 'critical_error', 'actionable_suggestions', 'message'],
28
+ func: async (_page, kwargs) => {
29
+ const token = String(kwargs.token ?? '').trim();
30
+ if (!token) {
31
+ throw new CliError('ARGUMENT', 'A review token is required.');
32
+ }
33
+
34
+ const helpfulness = validateHelpfulness(kwargs.helpfulness);
35
+ const criticalError = parseYesNo(kwargs['critical-error'], 'critical-error');
36
+ const actionableSuggestions = parseYesNo(kwargs['actionable-suggestions'], 'actionable-suggestions');
37
+ const comments = String(kwargs['additional-comments'] ?? '').trim();
38
+
39
+ const payload: Record<string, unknown> = {
40
+ helpfulness,
41
+ has_critical_error: criticalError,
42
+ has_actionable_suggestions: actionableSuggestions,
43
+ };
44
+ if (comments) {
45
+ payload.additional_comments = comments;
46
+ }
47
+
48
+ const { response, payload: responsePayload } = await requestJson(`/api/feedback/${encodeURIComponent(token)}`, {
49
+ method: 'POST',
50
+ headers: { 'Content-Type': 'application/json' },
51
+ body: JSON.stringify(payload),
52
+ });
53
+ ensureSuccess(response, responsePayload, 'Failed to submit feedback.', 'Check the token and try again');
54
+
55
+ return summarizeFeedback({
56
+ token,
57
+ helpfulness,
58
+ criticalError,
59
+ actionableSuggestions,
60
+ comments,
61
+ payload: responsePayload,
62
+ });
63
+ },
64
+ });
@@ -0,0 +1,47 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import { CliError } from '../../errors.js';
3
+ import {
4
+ PAPERREVIEW_DOMAIN,
5
+ buildReviewUrl,
6
+ ensureSuccess,
7
+ requestJson,
8
+ summarizeReview,
9
+ } from './utils.js';
10
+
11
+ cli({
12
+ site: 'paperreview',
13
+ name: 'review',
14
+ description: 'Fetch a paperreview.ai review by token',
15
+ domain: PAPERREVIEW_DOMAIN,
16
+ strategy: Strategy.PUBLIC,
17
+ browser: false,
18
+ timeoutSeconds: 30,
19
+ args: [
20
+ { name: 'token', positional: true, required: true, help: 'Review token returned by paperreview.ai' },
21
+ ],
22
+ columns: ['status', 'title', 'venue', 'numerical_score', 'has_feedback', 'review_url'],
23
+ func: async (_page, kwargs) => {
24
+ const token = String(kwargs.token ?? '').trim();
25
+ if (!token) {
26
+ throw new CliError('ARGUMENT', 'A review token is required.');
27
+ }
28
+
29
+ const { response, payload } = await requestJson(`/api/review/${encodeURIComponent(token)}`);
30
+
31
+ if (response.status === 202) {
32
+ return {
33
+ status: 'processing',
34
+ token,
35
+ review_url: buildReviewUrl(token),
36
+ title: '',
37
+ venue: '',
38
+ numerical_score: '',
39
+ has_feedback: '',
40
+ message: typeof payload === 'object' && payload ? payload.detail ?? 'Review is still processing.' : 'Review is still processing.',
41
+ };
42
+ }
43
+
44
+ ensureSuccess(response, payload, 'Failed to fetch the review.', 'Check the token and try again');
45
+ return summarizeReview(token, payload);
46
+ },
47
+ });
@@ -0,0 +1,119 @@
1
+ import { cli, Strategy } from '../../registry.js';
2
+ import { CliError } from '../../errors.js';
3
+ import {
4
+ PAPERREVIEW_DOMAIN,
5
+ ensureApiSuccess,
6
+ ensureSuccess,
7
+ normalizeVenue,
8
+ readPdfFile,
9
+ requestJson,
10
+ summarizeSubmission,
11
+ uploadPresignedPdf,
12
+ } from './utils.js';
13
+
14
+ cli({
15
+ site: 'paperreview',
16
+ name: 'submit',
17
+ description: 'Submit a PDF to paperreview.ai for review',
18
+ domain: PAPERREVIEW_DOMAIN,
19
+ strategy: Strategy.PUBLIC,
20
+ browser: false,
21
+ timeoutSeconds: 120,
22
+ args: [
23
+ { name: 'pdf', positional: true, required: true, help: 'Path to the paper PDF' },
24
+ { name: 'email', required: true, help: 'Email address for the submission' },
25
+ { name: 'venue', help: 'Optional target venue such as ICLR or NeurIPS' },
26
+ { name: 'dry-run', type: 'bool', default: false, help: 'Validate the input and stop before remote submission' },
27
+ { name: 'prepare-only', type: 'bool', default: false, help: 'Request an upload slot but stop before uploading the PDF' },
28
+ ],
29
+ columns: ['status', 'file', 'email', 'venue', 'token', 'review_url', 'message'],
30
+ footerExtra: (kwargs) => {
31
+ if (kwargs['dry-run'] === true) return 'dry run only';
32
+ if (kwargs['prepare-only'] === true) return 'prepared only';
33
+ return undefined;
34
+ },
35
+ func: async (_page, kwargs) => {
36
+ const pdfFile = await readPdfFile(kwargs.pdf);
37
+ const email = String(kwargs.email ?? '').trim();
38
+ const venue = normalizeVenue(kwargs.venue);
39
+ const dryRun = kwargs['dry-run'] === true;
40
+ const prepareOnly = kwargs['prepare-only'] === true;
41
+
42
+ if (!email) {
43
+ throw new CliError('ARGUMENT', 'An email address is required.', 'Pass --email <address>');
44
+ }
45
+
46
+ if (dryRun) {
47
+ return summarizeSubmission({
48
+ pdfFile,
49
+ email,
50
+ venue,
51
+ message: 'Input validation passed. No remote request was sent.',
52
+ dryRun: true,
53
+ });
54
+ }
55
+
56
+ const { response: uploadUrlResponse, payload: uploadUrlPayload } = await requestJson('/api/get-upload-url', {
57
+ method: 'POST',
58
+ headers: { 'Content-Type': 'application/json' },
59
+ body: JSON.stringify({
60
+ filename: pdfFile.fileName,
61
+ venue,
62
+ }),
63
+ });
64
+ ensureSuccess(
65
+ uploadUrlResponse,
66
+ uploadUrlPayload,
67
+ 'Failed to request an upload URL.',
68
+ 'Try again in a moment',
69
+ );
70
+ ensureApiSuccess(
71
+ uploadUrlPayload,
72
+ 'paperreview.ai did not return a usable upload URL.',
73
+ 'Try again in a moment',
74
+ );
75
+
76
+ if (prepareOnly) {
77
+ return summarizeSubmission({
78
+ pdfFile,
79
+ email,
80
+ venue,
81
+ message: 'Upload slot prepared. The PDF was not uploaded and no submission was confirmed.',
82
+ s3Key: uploadUrlPayload.s3_key,
83
+ status: 'prepared',
84
+ });
85
+ }
86
+
87
+ await uploadPresignedPdf(uploadUrlPayload.presigned_url, pdfFile, uploadUrlPayload);
88
+
89
+ const confirmForm = new FormData();
90
+ confirmForm.append('s3_key', uploadUrlPayload.s3_key);
91
+ confirmForm.append('venue', venue);
92
+ confirmForm.append('email', email);
93
+
94
+ const { response: confirmResponse, payload: confirmPayload } = await requestJson('/api/confirm-upload', {
95
+ method: 'POST',
96
+ body: confirmForm,
97
+ });
98
+ ensureSuccess(
99
+ confirmResponse,
100
+ confirmPayload,
101
+ 'Failed to confirm the upload with paperreview.ai.',
102
+ 'Try again in a moment',
103
+ );
104
+ ensureApiSuccess(
105
+ confirmPayload,
106
+ 'paperreview.ai did not confirm the submission.',
107
+ 'Try again in a moment',
108
+ );
109
+
110
+ return summarizeSubmission({
111
+ pdfFile,
112
+ email,
113
+ venue,
114
+ token: confirmPayload.token,
115
+ message: confirmPayload.message,
116
+ s3Key: uploadUrlPayload.s3_key,
117
+ });
118
+ },
119
+ });