@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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * E2E tests for browser commands that access PUBLIC data (no login required).
2
+ * E2E tests for core browser commands (bilibili, zhihu, v2ex, IMDb).
3
3
  * These use OPENCLI_HEADLESS=1 to launch a headless Chromium.
4
4
  *
5
5
  * NOTE: Some sites may block headless browsers with bot detection.
@@ -7,11 +7,8 @@
7
7
  */
8
8
 
9
9
  import { describe, it, expect } from 'vitest';
10
- import { runCli, parseJsonOutput } from './helpers.js';
10
+ import { runCli, parseJsonOutput, type CliResult } from './helpers.js';
11
11
 
12
- /**
13
- * Run a browser command — returns parsed data or null on failure.
14
- */
15
12
  async function tryBrowserCommand(args: string[]): Promise<any[] | null> {
16
13
  const { stdout, code } = await runCli(args, { timeout: 60_000 });
17
14
  if (code !== 0) return null;
@@ -23,10 +20,6 @@ async function tryBrowserCommand(args: string[]): Promise<any[] | null> {
23
20
  }
24
21
  }
25
22
 
26
- /**
27
- * Assert browser command returns data OR log a warning if blocked.
28
- * Empty results (bot detection, geo-blocking) are treated as a warning, not a failure.
29
- */
30
23
  function expectDataOrSkip(data: any[] | null, label: string) {
31
24
  if (data === null || data.length === 0) {
32
25
  console.warn(`${label}: skipped — no data returned (likely bot detection or geo-blocking)`);
@@ -35,53 +28,47 @@ function expectDataOrSkip(data: any[] | null, label: string) {
35
28
  expect(data.length).toBeGreaterThanOrEqual(1);
36
29
  }
37
30
 
38
- describe('browser public-data commands E2E', () => {
31
+ function isImdbChallenge(result: CliResult): boolean {
32
+ const text = `${result.stderr}\n${result.stdout}`;
33
+ return /IMDb blocked this request|Robot Check|Are you a robot|verify that you are human|captcha/i.test(text);
34
+ }
39
35
 
40
- // ── bbc (browser: true, strategy: public) ──
41
- it('bbc news returns headlines', async () => {
42
- const data = await tryBrowserCommand(['bbc', 'news', '--limit', '3', '-f', 'json']);
43
- expectDataOrSkip(data, 'bbc news');
44
- if (data) {
45
- expect(data[0]).toHaveProperty('title');
46
- }
47
- }, 60_000);
36
+ function isBrowserBridgeUnavailable(result: CliResult): boolean {
37
+ const text = `${result.stderr}\n${result.stdout}`;
38
+ return /Browser Extension is not connected|Browser Bridge extension.*not connected|Daemon is running but the Browser Extension is not connected/i.test(text);
39
+ }
48
40
 
49
- it('bloomberg news returns article detail when the article page is accessible', async () => {
50
- const feedResult = await runCli(['bloomberg', 'tech', '--limit', '1', '-f', 'json']);
51
- if (feedResult.code !== 0) {
52
- console.warn('bloomberg news: skipped — could not load Bloomberg tech feed');
53
- return;
41
+ async function expectImdbDataOrChallengeSkip(args: string[], label: string): Promise<any[] | null> {
42
+ const result = await runCli(args, { timeout: 60_000 });
43
+ if (result.code !== 0) {
44
+ if (isImdbChallenge(result)) {
45
+ console.warn(`${label}: skipped — IMDb challenge page detected`);
46
+ return null;
54
47
  }
55
-
56
- const feedItems = parseJsonOutput(feedResult.stdout);
57
- const link = Array.isArray(feedItems) ? feedItems[0]?.link : null;
58
- if (!link) {
59
- console.warn('bloomberg news: skipped — tech feed returned no link');
60
- return;
48
+ if (isBrowserBridgeUnavailable(result)) {
49
+ console.warn(`${label}: skipped Browser Bridge extension is unavailable in this environment`);
50
+ return null;
61
51
  }
52
+ throw new Error(`${label} failed:\n${result.stderr || result.stdout}`);
53
+ }
62
54
 
63
- const data = await tryBrowserCommand(['bloomberg', 'news', link, '-f', 'json']);
64
- expectDataOrSkip(data, 'bloomberg news');
65
- if (data) {
66
- expect(data[0]).toHaveProperty('title');
67
- expect(data[0]).toHaveProperty('summary');
68
- expect(data[0]).toHaveProperty('link');
69
- expect(data[0]).toHaveProperty('mediaLinks');
70
- expect(data[0]).toHaveProperty('content');
71
- }
72
- }, 60_000);
55
+ const data = parseJsonOutput(result.stdout);
56
+ if (!Array.isArray(data)) {
57
+ throw new Error(`${label} returned non-array JSON:\n${result.stdout.slice(0, 500)}`);
58
+ }
59
+ if (data.length === 0) {
60
+ throw new Error(`${label} returned an empty result`);
61
+ }
62
+ return data;
63
+ }
73
64
 
74
- // ── v2ex daily (browser: true) ──
75
- it('v2ex daily returns topics', async () => {
76
- const data = await tryBrowserCommand(['v2ex', 'daily', '--limit', '3', '-f', 'json']);
77
- expectDataOrSkip(data, 'v2ex daily');
78
- }, 60_000);
65
+ describe('browser public-data commands E2E', () => {
79
66
 
80
- // ── bilibili (browser: true, cookie strategy) ──
67
+ // ── bilibili ──
81
68
  it('bilibili hot returns trending videos', async () => {
82
69
  const data = await tryBrowserCommand(['bilibili', 'hot', '--limit', '5', '-f', 'json']);
83
70
  expectDataOrSkip(data, 'bilibili hot');
84
- if (data) {
71
+ if (data?.length) {
85
72
  expect(data[0]).toHaveProperty('title');
86
73
  }
87
74
  }, 60_000);
@@ -96,22 +83,11 @@ describe('browser public-data commands E2E', () => {
96
83
  expectDataOrSkip(data, 'bilibili search');
97
84
  }, 60_000);
98
85
 
99
- // ── weibo (browser: true, cookie strategy) ──
100
- it('weibo hot returns trending topics', async () => {
101
- const data = await tryBrowserCommand(['weibo', 'hot', '--limit', '5', '-f', 'json']);
102
- expectDataOrSkip(data, 'weibo hot');
103
- }, 60_000);
104
-
105
- it('weibo search returns results', async () => {
106
- const data = await tryBrowserCommand(['weibo', 'search', 'openai', '--limit', '3', '-f', 'json']);
107
- expectDataOrSkip(data, 'weibo search');
108
- }, 60_000);
109
-
110
- // ── zhihu (browser: true, cookie strategy) ──
86
+ // ── zhihu ──
111
87
  it('zhihu hot returns trending questions', async () => {
112
88
  const data = await tryBrowserCommand(['zhihu', 'hot', '--limit', '5', '-f', 'json']);
113
89
  expectDataOrSkip(data, 'zhihu hot');
114
- if (data) {
90
+ if (data?.length) {
115
91
  expect(data[0]).toHaveProperty('title');
116
92
  }
117
93
  }, 60_000);
@@ -121,90 +97,33 @@ describe('browser public-data commands E2E', () => {
121
97
  expectDataOrSkip(data, 'zhihu search');
122
98
  }, 60_000);
123
99
 
124
- // ── reddit (browser: true, cookie strategy) ──
125
- it('reddit hot returns posts', async () => {
126
- const data = await tryBrowserCommand(['reddit', 'hot', '--limit', '5', '-f', 'json']);
127
- expectDataOrSkip(data, 'reddit hot');
128
- }, 60_000);
129
-
130
- it('reddit frontpage returns posts', async () => {
131
- const data = await tryBrowserCommand(['reddit', 'frontpage', '--limit', '5', '-f', 'json']);
132
- expectDataOrSkip(data, 'reddit frontpage');
133
- }, 60_000);
134
-
135
- // ── twitter (browser: true) ──
136
- it('twitter trending returns trends', async () => {
137
- const data = await tryBrowserCommand(['twitter', 'trending', '--limit', '5', '-f', 'json']);
138
- expectDataOrSkip(data, 'twitter trending');
139
- }, 60_000);
140
-
141
- // ── xueqiu (browser: true, cookie strategy) ──
142
- it('xueqiu hot returns hot posts', async () => {
143
- const data = await tryBrowserCommand(['xueqiu', 'hot', '--limit', '5', '-f', 'json']);
144
- expectDataOrSkip(data, 'xueqiu hot');
145
- }, 60_000);
146
-
147
- it('xueqiu hot-stock returns stocks', async () => {
148
- const data = await tryBrowserCommand(['xueqiu', 'hot-stock', '--limit', '5', '-f', 'json']);
149
- expectDataOrSkip(data, 'xueqiu hot-stock');
150
- }, 60_000);
151
-
152
- // ── reuters (browser: true) ──
153
- it('reuters search returns articles', async () => {
154
- const data = await tryBrowserCommand(['reuters', 'search', 'technology', '--limit', '3', '-f', 'json']);
155
- expectDataOrSkip(data, 'reuters search');
156
- }, 60_000);
157
-
158
- // ── youtube (browser: true) ──
159
- it('youtube search returns videos', async () => {
160
- const data = await tryBrowserCommand(['youtube', 'search', 'typescript tutorial', '--limit', '3', '-f', 'json']);
161
- expectDataOrSkip(data, 'youtube search');
162
- }, 60_000);
163
-
164
- // ── smzdm (browser: true) ──
165
- it('smzdm search returns deals', async () => {
166
- const data = await tryBrowserCommand(['smzdm', 'search', '键盘', '--limit', '3', '-f', 'json']);
167
- expectDataOrSkip(data, 'smzdm search');
168
- }, 60_000);
169
-
170
- // ── boss (browser: true) ──
171
- it('boss search returns jobs', async () => {
172
- const data = await tryBrowserCommand(['boss', 'search', 'golang', '--limit', '3', '-f', 'json']);
173
- expectDataOrSkip(data, 'boss search');
174
- }, 60_000);
175
-
176
- // ── ctrip (browser: true) ──
177
- it('ctrip search returns flights', async () => {
178
- const data = await tryBrowserCommand(['ctrip', 'search', '-f', 'json']);
179
- expectDataOrSkip(data, 'ctrip search');
180
- }, 60_000);
181
-
182
- // ── coupang (browser: true) ──
183
- it('coupang search returns products', async () => {
184
- const data = await tryBrowserCommand(['coupang', 'search', 'laptop', '--limit', '3', '-f', 'json']);
185
- expectDataOrSkip(data, 'coupang search');
100
+ // ── v2ex ──
101
+ it('v2ex daily returns topics', async () => {
102
+ const data = await tryBrowserCommand(['v2ex', 'daily', '--limit', '3', '-f', 'json']);
103
+ expectDataOrSkip(data, 'v2ex daily');
186
104
  }, 60_000);
187
105
 
188
- // ── xiaohongshu (browser: true) ──
189
- it('xiaohongshu search returns notes', async () => {
190
- const data = await tryBrowserCommand(['xiaohongshu', 'search', '美食', '--limit', '3', '-f', 'json']);
191
- expectDataOrSkip(data, 'xiaohongshu search');
106
+ // ── imdb ──
107
+ it('imdb top returns chart data', async () => {
108
+ const data = await expectImdbDataOrChallengeSkip(['imdb', 'top', '--limit', '3', '-f', 'json'], 'imdb top');
109
+ if (data?.length) {
110
+ expect(data[0]).toHaveProperty('title');
111
+ }
192
112
  }, 60_000);
193
113
 
194
- // ── google search (browser: true, public strategy) ──
195
- it('google search returns results', async () => {
196
- const data = await tryBrowserCommand(['google', 'search', 'typescript', '--limit', '5', '-f', 'json']);
197
- expectDataOrSkip(data, 'google search');
198
- if (data) {
199
- expect(data[0]).toHaveProperty('type');
114
+ it('imdb search returns results', async () => {
115
+ const data = await expectImdbDataOrChallengeSkip(['imdb', 'search', 'inception', '--limit', '3', '-f', 'json'], 'imdb search');
116
+ if (data?.length) {
117
+ expect(data[0]).toHaveProperty('id');
200
118
  expect(data[0]).toHaveProperty('title');
201
- expect(data[0]).toHaveProperty('url');
202
119
  }
203
120
  }, 60_000);
204
121
 
205
- // ── yahoo-finance (browser: true) ──
206
- it('yahoo-finance quote returns stock data', async () => {
207
- const data = await tryBrowserCommand(['yahoo-finance', 'quote', '--symbol', 'AAPL', '-f', 'json']);
208
- expectDataOrSkip(data, 'yahoo-finance quote');
122
+ it('imdb title returns movie details', async () => {
123
+ const data = await expectImdbDataOrChallengeSkip(['imdb', 'title', 'tt1375666', '-f', 'json'], 'imdb title');
124
+ if (data?.length) {
125
+ expect(data[0]).toHaveProperty('field');
126
+ expect(data[0]).toHaveProperty('value');
127
+ }
209
128
  }, 60_000);
210
129
  });
@@ -29,7 +29,8 @@ export async function runCli(
29
29
  ): Promise<CliResult> {
30
30
  const timeout = opts.timeout ?? 30_000;
31
31
  try {
32
- const { stdout, stderr } = await exec('node', [MAIN, ...args], {
32
+ const runtime = process.env.OPENCLI_TEST_RUNTIME || 'node';
33
+ const { stdout, stderr } = await exec(runtime, [MAIN, ...args], {
33
34
  cwd: ROOT,
34
35
  timeout,
35
36
  env: {
@@ -4,14 +4,19 @@
4
4
  */
5
5
 
6
6
  import { describe, expect, it } from 'vitest';
7
+ import * as fs from 'node:fs/promises';
8
+ import * as os from 'node:os';
9
+ import * as path from 'node:path';
7
10
  import { parseJsonOutput, runCli } from './helpers.js';
8
11
 
9
12
  function isExpectedChineseSiteRestriction(code: number, stderr: string): boolean {
10
13
  if (code === 0) return false;
11
14
  // Overseas CI runners may get HTTP errors, geo-blocks, DNS failures,
12
- // or receive mangled HTML that fails parsing.
15
+ // or receive mangled HTML that fails parsing. Some runners also fail
16
+ // without surfacing a useful stderr payload.
13
17
  return /Error \[(FETCH_ERROR|PARSE_ERROR|NOT_FOUND)\]/.test(stderr)
14
- || /fetch failed/.test(stderr);
18
+ || /fetch failed/.test(stderr)
19
+ || stderr.trim() === '';
15
20
  }
16
21
 
17
22
  function isExpectedApplePodcastsRestriction(code: number, stderr: string): boolean {
@@ -126,6 +131,35 @@ describe('public commands E2E', () => {
126
131
  expect(data[0]).toHaveProperty('id');
127
132
  }, 30_000);
128
133
 
134
+ it('paperreview submit dry-run validates a local PDF without remote upload', async () => {
135
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'opencli-paperreview-'));
136
+ const pdfPath = path.join(tempDir, 'sample.pdf');
137
+ await fs.writeFile(pdfPath, Buffer.concat([Buffer.from('%PDF-1.4\n'), Buffer.alloc(256, 1)]));
138
+
139
+ const { stdout, code } = await runCli([
140
+ 'paperreview',
141
+ 'submit',
142
+ pdfPath,
143
+ '--email',
144
+ 'wang2629651228@gmail.com',
145
+ '--venue',
146
+ 'RAL',
147
+ '--dry-run',
148
+ 'true',
149
+ '-f',
150
+ 'json',
151
+ ]);
152
+
153
+ expect(code).toBe(0);
154
+ const data = parseJsonOutput(stdout);
155
+ expect(data).toMatchObject({
156
+ status: 'dry-run',
157
+ file: 'sample.pdf',
158
+ email: 'wang2629651228@gmail.com',
159
+ venue: 'RAL',
160
+ });
161
+ }, 30_000);
162
+
129
163
  // ── hackernews ──
130
164
  it('hackernews top returns structured data', async () => {
131
165
  const { stdout, code } = await runCli(['hackernews', 'top', '--limit', '3', '-f', 'json']);
@@ -235,7 +269,7 @@ describe('public commands E2E', () => {
235
269
 
236
270
  it('v2ex topic returns topic detail', async () => {
237
271
  // Topic 1000001 is a well-known V2EX topic
238
- const { stdout, code } = await runCli(['v2ex', 'topic', '--id', '1000001', '-f', 'json']);
272
+ const { stdout, code } = await runCli(['v2ex', 'topic', '1000001', '-f', 'json']);
239
273
  // May fail if V2EX rate-limits, but should return structured data
240
274
  if (code === 0) {
241
275
  const data = parseJsonOutput(stdout);
@@ -38,7 +38,7 @@ describe('API health smoke tests', () => {
38
38
  }, 30_000);
39
39
 
40
40
  it('v2ex topic API is responsive', async () => {
41
- const { stdout, code } = await runCli(['v2ex', 'topic', '--id', '1000001', '-f', 'json']);
41
+ const { stdout, code } = await runCli(['v2ex', 'topic', '1000001', '-f', 'json']);
42
42
  if (code === 0) {
43
43
  const data = parseJsonOutput(stdout);
44
44
  expect(data).toBeDefined();
package/vitest.config.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { defineConfig } from 'vitest/config';
2
2
 
3
+ const includeExtendedE2e = process.env.OPENCLI_E2E === '1';
4
+
3
5
  export default defineConfig({
4
6
  test: {
5
7
  projects: [
@@ -8,37 +10,52 @@ export default defineConfig({
8
10
  name: 'unit',
9
11
  include: ['src/**/*.test.ts'],
10
12
  exclude: ['src/clis/**/*.test.ts'],
11
- // Run unit tests before e2e tests to avoid project-level contention in CI.
12
- sequence: {
13
- groupOrder: 0,
14
- },
13
+ sequence: { groupOrder: 0 },
15
14
  },
16
15
  },
17
16
  {
18
17
  test: {
19
18
  name: 'adapter',
20
19
  include: [
21
- 'src/clis/zhihu/**/*.test.ts',
22
- 'src/clis/twitter/**/*.test.ts',
23
- 'src/clis/reddit/**/*.test.ts',
24
20
  'src/clis/bilibili/**/*.test.ts',
25
- 'src/clis/linkedin/**/*.test.ts',
26
- 'src/clis/grok/**/*.test.ts',
27
- 'src/clis/pixiv/**/*.test.ts',
21
+ 'src/clis/imdb/**/*.test.ts',
22
+ 'src/clis/jd/**/*.test.ts',
23
+ 'src/clis/linux-do/**/*.test.ts',
24
+ 'src/clis/xiaohongshu/**/*.test.ts',
25
+ 'src/clis/twitter/**/*.test.ts',
26
+ 'src/clis/douban/**/*.test.ts',
27
+ 'src/clis/zhihu/**/*.test.ts',
28
+ 'src/clis/v2ex/**/*.test.ts',
29
+ 'src/clis/weread/**/*.test.ts',
30
+ 'src/clis/36kr/**/*.test.ts',
31
+ 'src/clis/producthunt/**/*.test.ts',
32
+ 'src/clis/paperreview/**/*.test.ts',
28
33
  ],
29
- sequence: {
30
- groupOrder: 1,
31
- },
34
+ sequence: { groupOrder: 1 },
32
35
  },
33
36
  },
34
37
  {
35
38
  test: {
36
39
  name: 'e2e',
37
- include: ['tests/**/*.test.ts'],
40
+ include: [
41
+ 'tests/e2e/browser-public.test.ts',
42
+ 'tests/e2e/public-commands.test.ts',
43
+ 'tests/e2e/management.test.ts',
44
+ 'tests/e2e/output-formats.test.ts',
45
+ 'tests/e2e/plugin-management.test.ts',
46
+ // Extended browser tests (20+ sites) — opt-in only:
47
+ // OPENCLI_E2E=1 npx vitest run
48
+ ...(includeExtendedE2e ? ['tests/e2e/browser-public-extended.test.ts', 'tests/e2e/browser-auth.test.ts'] : []),
49
+ ],
38
50
  maxWorkers: 2,
39
- sequence: {
40
- groupOrder: 2,
41
- },
51
+ sequence: { groupOrder: 2 },
52
+ },
53
+ },
54
+ {
55
+ test: {
56
+ name: 'smoke',
57
+ include: ['tests/smoke/**/*.test.ts'],
58
+ sequence: { groupOrder: 3 },
42
59
  },
43
60
  },
44
61
  ],
@@ -1,51 +0,0 @@
1
- site: linux-do
2
- name: category
3
- description: linux.do 分类内话题
4
- domain: linux.do
5
- browser: true
6
-
7
- args:
8
- slug:
9
- positional: true
10
- type: str
11
- required: true
12
- description: Category slug (use 'categories' command to find)
13
- id:
14
- positional: true
15
- type: int
16
- required: true
17
- description: Category ID (use 'categories' command to find)
18
- limit:
19
- type: int
20
- default: 20
21
- description: Number of topics
22
-
23
- pipeline:
24
- - navigate: https://linux.do
25
-
26
- - evaluate: |
27
- (async () => {
28
- const slug = ${{ args.slug | json }};
29
- const res = await fetch('/c/' + encodeURIComponent(slug) + '/${{ args.id }}.json', { credentials: 'include' });
30
- if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
31
- let data;
32
- try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
33
- const topics = data?.topic_list?.topics || [];
34
- return topics.slice(0, ${{ args.limit }}).map(t => ({
35
- title: t.title,
36
- replies: (t.posts_count || 1) - 1,
37
- views: t.views,
38
- likes: t.like_count,
39
- }));
40
- })()
41
-
42
- - map:
43
- rank: ${{ index + 1 }}
44
- title: ${{ item.title }}
45
- replies: ${{ item.replies }}
46
- views: ${{ item.views }}
47
- likes: ${{ item.likes }}
48
-
49
- - limit: ${{ args.limit }}
50
-
51
- columns: [rank, title, replies, views, likes]
@@ -1,50 +0,0 @@
1
- site: linux-do
2
- name: hot
3
- description: linux.do 热门话题
4
- domain: linux.do
5
- browser: true
6
-
7
- args:
8
- limit:
9
- type: int
10
- default: 20
11
- description: Number of topics
12
- period:
13
- type: str
14
- default: weekly
15
- description: Time period
16
- choices: [all, daily, weekly, monthly, yearly]
17
-
18
- pipeline:
19
- - navigate: https://linux.do
20
-
21
- - evaluate: |
22
- (async () => {
23
- const period = ${{ args.period | json }};
24
- const res = await fetch('/top.json?period=' + encodeURIComponent(period), { credentials: 'include' });
25
- if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
26
- let data;
27
- try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
28
- const topics = data?.topic_list?.topics || [];
29
- const cats = data?.topic_list?.categories || data?.categories || [];
30
- const catMap = Object.fromEntries(cats.map(c => [c.id, c.name]));
31
- return topics.slice(0, ${{ args.limit }}).map(t => ({
32
- title: t.title,
33
- replies: (t.posts_count || 1) - 1,
34
- views: t.views,
35
- likes: t.like_count,
36
- category: catMap[t.category_id] || String(t.category_id),
37
- }));
38
- })()
39
-
40
- - map:
41
- rank: ${{ index + 1 }}
42
- title: ${{ item.title }}
43
- replies: ${{ item.replies }}
44
- views: ${{ item.views }}
45
- likes: ${{ item.likes }}
46
- category: ${{ item.category }}
47
-
48
- - limit: ${{ args.limit }}
49
-
50
- columns: [rank, title, replies, views, likes, category]
@@ -1,40 +0,0 @@
1
- site: linux-do
2
- name: latest
3
- description: linux.do 最新话题
4
- domain: linux.do
5
- browser: true
6
-
7
- args:
8
- limit:
9
- type: int
10
- default: 20
11
- description: Number of topics
12
-
13
- pipeline:
14
- - navigate: https://linux.do
15
-
16
- - evaluate: |
17
- (async () => {
18
- const res = await fetch('/latest.json', { credentials: 'include' });
19
- if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
20
- let data;
21
- try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
22
- const topics = data?.topic_list?.topics || [];
23
- return topics.slice(0, ${{ args.limit }}).map(t => ({
24
- title: t.title,
25
- replies: (t.posts_count || 1) - 1,
26
- views: t.views,
27
- likes: t.like_count,
28
- }));
29
- })()
30
-
31
- - map:
32
- rank: ${{ index + 1 }}
33
- title: ${{ item.title }}
34
- replies: ${{ item.replies }}
35
- views: ${{ item.views }}
36
- likes: ${{ item.likes }}
37
-
38
- - limit: ${{ args.limit }}
39
-
40
- columns: [rank, title, replies, views, likes]
@@ -1,51 +0,0 @@
1
- site: linux-do
2
- name: category
3
- description: linux.do 分类内话题
4
- domain: linux.do
5
- browser: true
6
-
7
- args:
8
- slug:
9
- positional: true
10
- type: str
11
- required: true
12
- description: Category slug (use 'categories' command to find)
13
- id:
14
- positional: true
15
- type: int
16
- required: true
17
- description: Category ID (use 'categories' command to find)
18
- limit:
19
- type: int
20
- default: 20
21
- description: Number of topics
22
-
23
- pipeline:
24
- - navigate: https://linux.do
25
-
26
- - evaluate: |
27
- (async () => {
28
- const slug = ${{ args.slug | json }};
29
- const res = await fetch('/c/' + encodeURIComponent(slug) + '/${{ args.id }}.json', { credentials: 'include' });
30
- if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
31
- let data;
32
- try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
33
- const topics = data?.topic_list?.topics || [];
34
- return topics.slice(0, ${{ args.limit }}).map(t => ({
35
- title: t.title,
36
- replies: (t.posts_count || 1) - 1,
37
- views: t.views,
38
- likes: t.like_count,
39
- }));
40
- })()
41
-
42
- - map:
43
- rank: ${{ index + 1 }}
44
- title: ${{ item.title }}
45
- replies: ${{ item.replies }}
46
- views: ${{ item.views }}
47
- likes: ${{ item.likes }}
48
-
49
- - limit: ${{ args.limit }}
50
-
51
- columns: [rank, title, replies, views, likes]
@@ -1,50 +0,0 @@
1
- site: linux-do
2
- name: hot
3
- description: linux.do 热门话题
4
- domain: linux.do
5
- browser: true
6
-
7
- args:
8
- limit:
9
- type: int
10
- default: 20
11
- description: Number of topics
12
- period:
13
- type: str
14
- default: weekly
15
- description: Time period
16
- choices: [all, daily, weekly, monthly, yearly]
17
-
18
- pipeline:
19
- - navigate: https://linux.do
20
-
21
- - evaluate: |
22
- (async () => {
23
- const period = ${{ args.period | json }};
24
- const res = await fetch('/top.json?period=' + encodeURIComponent(period), { credentials: 'include' });
25
- if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
26
- let data;
27
- try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
28
- const topics = data?.topic_list?.topics || [];
29
- const cats = data?.topic_list?.categories || data?.categories || [];
30
- const catMap = Object.fromEntries(cats.map(c => [c.id, c.name]));
31
- return topics.slice(0, ${{ args.limit }}).map(t => ({
32
- title: t.title,
33
- replies: (t.posts_count || 1) - 1,
34
- views: t.views,
35
- likes: t.like_count,
36
- category: catMap[t.category_id] || String(t.category_id),
37
- }));
38
- })()
39
-
40
- - map:
41
- rank: ${{ index + 1 }}
42
- title: ${{ item.title }}
43
- replies: ${{ item.replies }}
44
- views: ${{ item.views }}
45
- likes: ${{ item.likes }}
46
- category: ${{ item.category }}
47
-
48
- - limit: ${{ args.limit }}
49
-
50
- columns: [rank, title, replies, views, likes, category]