@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.
- package/.github/actions/setup-chrome/action.yml +5 -4
- package/.github/workflows/build-extension.yml +2 -6
- package/.github/workflows/ci.yml +37 -3
- package/.github/workflows/e2e-headed.yml +16 -3
- package/CHANGELOG.md +23 -0
- package/PRIVACY.md +57 -0
- package/README.md +36 -7
- package/README.zh-CN.md +13 -6
- package/SKILL.md +103 -2
- package/dist/browser/cdp.d.ts +2 -1
- package/dist/browser/discover.d.ts +4 -1
- package/dist/browser/discover.js +6 -2
- package/dist/browser/errors.d.ts +2 -2
- package/dist/browser/errors.js +4 -12
- package/dist/browser/mcp.d.ts +2 -1
- package/dist/build-manifest.d.ts +2 -0
- package/dist/build-manifest.js +39 -14
- package/dist/build-manifest.test.js +21 -0
- package/dist/capabilityRouting.d.ts +2 -0
- package/dist/capabilityRouting.js +2 -1
- package/dist/cli-manifest.json +1838 -151
- package/dist/cli.js +34 -3
- package/dist/clis/36kr/article.d.ts +1 -0
- package/dist/clis/36kr/article.js +62 -0
- package/dist/clis/36kr/hot.d.ts +3 -0
- package/dist/clis/36kr/hot.js +80 -0
- package/dist/clis/36kr/hot.test.d.ts +1 -0
- package/dist/clis/36kr/hot.test.js +15 -0
- package/dist/clis/36kr/news.d.ts +1 -0
- package/dist/clis/36kr/news.js +51 -0
- package/dist/clis/36kr/news.test.d.ts +1 -0
- package/dist/clis/36kr/news.test.js +85 -0
- package/dist/clis/36kr/search.d.ts +1 -0
- package/dist/clis/36kr/search.js +72 -0
- package/dist/clis/apple-podcasts/search.js +2 -1
- package/dist/clis/arxiv/search.js +2 -2
- package/dist/clis/bbc/news.js +0 -1
- package/dist/clis/bilibili/comments.d.ts +5 -0
- package/dist/clis/bilibili/comments.js +40 -0
- package/dist/clis/bilibili/comments.test.d.ts +1 -0
- package/dist/clis/bilibili/comments.test.js +82 -0
- package/dist/clis/chatgpt/ask.js +29 -14
- package/dist/clis/chatgpt/ax.d.ts +6 -0
- package/dist/clis/chatgpt/ax.js +172 -1
- package/dist/clis/chatgpt/model.d.ts +1 -0
- package/dist/clis/chatgpt/model.js +24 -0
- package/dist/clis/chatgpt/send.js +12 -3
- package/dist/clis/ctrip/search.js +0 -1
- package/dist/clis/douban/download.d.ts +1 -0
- package/dist/clis/douban/download.js +67 -0
- package/dist/clis/douban/download.test.d.ts +1 -0
- package/dist/clis/douban/download.test.js +170 -0
- package/dist/clis/douban/photos.d.ts +1 -0
- package/dist/clis/douban/photos.js +34 -0
- package/dist/clis/douban/utils.d.ts +25 -0
- package/dist/clis/douban/utils.js +190 -1
- package/dist/clis/douban/utils.test.d.ts +1 -0
- package/dist/clis/douban/utils.test.js +64 -0
- package/dist/clis/douyin/_shared/browser-fetch.d.ts +10 -0
- package/dist/clis/douyin/_shared/browser-fetch.js +30 -0
- package/dist/clis/douyin/_shared/browser-fetch.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/browser-fetch.test.js +31 -0
- package/dist/clis/douyin/_shared/creation-id.d.ts +1 -0
- package/dist/clis/douyin/_shared/creation-id.js +5 -0
- package/dist/clis/douyin/_shared/creation-id.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/creation-id.test.js +22 -0
- package/dist/clis/douyin/_shared/imagex-upload.d.ts +20 -0
- package/dist/clis/douyin/_shared/imagex-upload.js +53 -0
- package/dist/clis/douyin/_shared/imagex-upload.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/imagex-upload.test.js +87 -0
- package/dist/clis/douyin/_shared/sts2.d.ts +8 -0
- package/dist/clis/douyin/_shared/sts2.js +15 -0
- package/dist/clis/douyin/_shared/text-extra.d.ts +18 -0
- package/dist/clis/douyin/_shared/text-extra.js +15 -0
- package/dist/clis/douyin/_shared/text-extra.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/text-extra.test.js +37 -0
- package/dist/clis/douyin/_shared/timing.d.ts +2 -0
- package/dist/clis/douyin/_shared/timing.js +22 -0
- package/dist/clis/douyin/_shared/timing.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/timing.test.js +28 -0
- package/dist/clis/douyin/_shared/tos-upload-short-read.test.d.ts +11 -0
- package/dist/clis/douyin/_shared/tos-upload-short-read.test.js +83 -0
- package/dist/clis/douyin/_shared/tos-upload.d.ts +53 -0
- package/dist/clis/douyin/_shared/tos-upload.js +295 -0
- package/dist/clis/douyin/_shared/tos-upload.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/tos-upload.test.js +229 -0
- package/dist/clis/douyin/_shared/transcode.d.ts +27 -0
- package/dist/clis/douyin/_shared/transcode.js +45 -0
- package/dist/clis/douyin/_shared/transcode.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/transcode.test.js +93 -0
- package/dist/clis/douyin/_shared/types.d.ts +26 -0
- package/dist/clis/douyin/_shared/types.js +1 -0
- package/dist/clis/douyin/activities.d.ts +1 -0
- package/dist/clis/douyin/activities.js +20 -0
- package/dist/clis/douyin/activities.test.d.ts +1 -0
- package/dist/clis/douyin/activities.test.js +22 -0
- package/dist/clis/douyin/collections.d.ts +1 -0
- package/dist/clis/douyin/collections.js +22 -0
- package/dist/clis/douyin/collections.test.d.ts +1 -0
- package/dist/clis/douyin/collections.test.js +23 -0
- package/dist/clis/douyin/delete.d.ts +1 -0
- package/dist/clis/douyin/delete.js +18 -0
- package/dist/clis/douyin/delete.test.d.ts +1 -0
- package/dist/clis/douyin/delete.test.js +11 -0
- package/dist/clis/douyin/draft.d.ts +14 -0
- package/dist/clis/douyin/draft.js +237 -0
- package/dist/clis/douyin/draft.test.d.ts +1 -0
- package/dist/clis/douyin/draft.test.js +11 -0
- package/dist/clis/douyin/drafts.d.ts +1 -0
- package/dist/clis/douyin/drafts.js +23 -0
- package/dist/clis/douyin/drafts.test.d.ts +1 -0
- package/dist/clis/douyin/drafts.test.js +11 -0
- package/dist/clis/douyin/hashtag.d.ts +1 -0
- package/dist/clis/douyin/hashtag.js +45 -0
- package/dist/clis/douyin/hashtag.test.d.ts +1 -0
- package/dist/clis/douyin/hashtag.test.js +25 -0
- package/dist/clis/douyin/location.d.ts +1 -0
- package/dist/clis/douyin/location.js +24 -0
- package/dist/clis/douyin/location.test.d.ts +1 -0
- package/dist/clis/douyin/location.test.js +23 -0
- package/dist/clis/douyin/profile.d.ts +1 -0
- package/dist/clis/douyin/profile.js +28 -0
- package/dist/clis/douyin/profile.test.d.ts +1 -0
- package/dist/clis/douyin/profile.test.js +11 -0
- package/dist/clis/douyin/publish.d.ts +14 -0
- package/dist/clis/douyin/publish.js +288 -0
- package/dist/clis/douyin/publish.test.d.ts +1 -0
- package/dist/clis/douyin/publish.test.js +38 -0
- package/dist/clis/douyin/stats.d.ts +1 -0
- package/dist/clis/douyin/stats.js +27 -0
- package/dist/clis/douyin/stats.test.d.ts +1 -0
- package/dist/clis/douyin/stats.test.js +22 -0
- package/dist/clis/douyin/update.d.ts +1 -0
- package/dist/clis/douyin/update.js +31 -0
- package/dist/clis/douyin/update.test.d.ts +1 -0
- package/dist/clis/douyin/update.test.js +11 -0
- package/dist/clis/douyin/videos.d.ts +1 -0
- package/dist/clis/douyin/videos.js +34 -0
- package/dist/clis/douyin/videos.test.d.ts +1 -0
- package/dist/clis/douyin/videos.test.js +11 -0
- package/dist/clis/hackernews/search.yaml +1 -1
- package/dist/clis/imdb/person.d.ts +1 -0
- package/dist/clis/imdb/person.js +203 -0
- package/dist/clis/imdb/reviews.d.ts +1 -0
- package/dist/clis/imdb/reviews.js +88 -0
- package/dist/clis/imdb/search.d.ts +1 -0
- package/dist/clis/imdb/search.js +161 -0
- package/dist/clis/imdb/title.d.ts +1 -0
- package/dist/clis/imdb/title.js +93 -0
- package/dist/clis/imdb/top.d.ts +1 -0
- package/dist/clis/imdb/top.js +53 -0
- package/dist/clis/imdb/trending.d.ts +1 -0
- package/dist/clis/imdb/trending.js +52 -0
- package/dist/clis/imdb/utils.d.ts +46 -0
- package/dist/clis/imdb/utils.js +285 -0
- package/dist/clis/imdb/utils.test.d.ts +1 -0
- package/dist/clis/imdb/utils.test.js +88 -0
- package/dist/clis/instagram/search.yaml +2 -1
- package/dist/clis/jd/item.d.ts +4 -0
- package/dist/clis/jd/item.js +16 -15
- package/dist/clis/jd/item.test.js +16 -1
- package/dist/clis/linux-do/categories.yaml +38 -9
- package/dist/clis/linux-do/category.d.ts +1 -0
- package/dist/clis/linux-do/category.js +36 -0
- package/dist/clis/linux-do/feed.d.ts +45 -0
- package/dist/clis/linux-do/feed.js +397 -0
- package/dist/clis/linux-do/feed.test.d.ts +1 -0
- package/dist/clis/linux-do/feed.test.js +118 -0
- package/dist/clis/linux-do/hot.d.ts +1 -0
- package/dist/clis/linux-do/hot.js +25 -0
- package/dist/clis/linux-do/latest.d.ts +1 -0
- package/dist/clis/linux-do/latest.js +18 -0
- package/dist/clis/linux-do/search.yaml +3 -1
- package/dist/clis/linux-do/tags.yaml +41 -0
- package/dist/clis/linux-do/topic.yaml +41 -3
- package/dist/clis/linux-do/user-posts.yaml +67 -0
- package/dist/clis/linux-do/user-topics.yaml +54 -0
- package/dist/clis/medium/search.js +1 -1
- package/dist/clis/paperreview/commands.test.d.ts +3 -0
- package/dist/clis/paperreview/commands.test.js +243 -0
- package/dist/clis/paperreview/feedback.d.ts +1 -0
- package/dist/clis/paperreview/feedback.js +52 -0
- package/dist/clis/paperreview/review.d.ts +1 -0
- package/dist/clis/paperreview/review.js +37 -0
- package/dist/clis/paperreview/submit.d.ts +1 -0
- package/dist/clis/paperreview/submit.js +85 -0
- package/dist/clis/paperreview/utils.d.ts +46 -0
- package/dist/clis/paperreview/utils.js +197 -0
- package/dist/clis/paperreview/utils.test.d.ts +1 -0
- package/dist/clis/paperreview/utils.test.js +49 -0
- package/dist/clis/producthunt/browse.d.ts +1 -0
- package/dist/clis/producthunt/browse.js +99 -0
- package/dist/clis/producthunt/hot.d.ts +1 -0
- package/dist/clis/producthunt/hot.js +110 -0
- package/dist/clis/producthunt/posts.d.ts +1 -0
- package/dist/clis/producthunt/posts.js +28 -0
- package/dist/clis/producthunt/today.d.ts +1 -0
- package/dist/clis/producthunt/today.js +35 -0
- package/dist/clis/producthunt/utils.d.ts +29 -0
- package/dist/clis/producthunt/utils.js +99 -0
- package/dist/clis/producthunt/utils.test.d.ts +1 -0
- package/dist/clis/producthunt/utils.test.js +64 -0
- package/dist/clis/reuters/search.js +0 -1
- package/dist/clis/twitter/article.js +4 -28
- package/dist/clis/twitter/likes.d.ts +24 -0
- package/dist/clis/twitter/likes.js +217 -0
- package/dist/clis/twitter/likes.test.d.ts +1 -0
- package/dist/clis/twitter/likes.test.js +85 -0
- package/dist/clis/twitter/profile.js +4 -28
- package/dist/clis/twitter/search.js +7 -4
- package/dist/clis/twitter/search.test.js +56 -2
- package/dist/clis/twitter/shared.d.ts +6 -0
- package/dist/clis/twitter/shared.js +35 -0
- package/dist/clis/twitter/timeline.js +2 -13
- package/dist/clis/weibo/comments.d.ts +1 -0
- package/dist/clis/weibo/comments.js +53 -0
- package/dist/clis/weibo/feed.d.ts +1 -0
- package/dist/clis/weibo/feed.js +56 -0
- package/dist/clis/weibo/hot.js +0 -1
- package/dist/clis/weibo/me.d.ts +1 -0
- package/dist/clis/weibo/me.js +76 -0
- package/dist/clis/weibo/post.d.ts +1 -0
- package/dist/clis/weibo/post.js +75 -0
- package/dist/clis/weibo/user.d.ts +1 -0
- package/dist/clis/weibo/user.js +63 -0
- package/dist/clis/weibo/utils.d.ts +6 -0
- package/dist/clis/weibo/utils.js +30 -0
- package/dist/clis/weixin/download.d.ts +17 -0
- package/dist/clis/weixin/download.js +88 -20
- package/dist/clis/weread/book.js +2 -2
- package/dist/clis/weread/commands.test.d.ts +3 -0
- package/dist/clis/weread/commands.test.js +43 -0
- package/dist/clis/weread/highlights.js +2 -2
- package/dist/clis/weread/notebooks.js +2 -2
- package/dist/clis/weread/notes.js +3 -3
- package/dist/clis/weread/search.js +3 -2
- package/dist/clis/weread/shelf.js +2 -2
- package/dist/clis/weread/utils.d.ts +4 -4
- package/dist/clis/weread/utils.js +32 -14
- package/dist/clis/weread/utils.test.js +1 -28
- package/dist/clis/xiaohongshu/comments.d.ts +5 -0
- package/dist/clis/xiaohongshu/comments.js +74 -0
- package/dist/clis/xiaohongshu/comments.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/comments.test.js +79 -0
- package/dist/clis/xiaohongshu/publish.js +114 -18
- package/dist/clis/xiaohongshu/publish.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/publish.test.js +119 -0
- package/dist/clis/xueqiu/search.yaml +2 -1
- package/dist/clis/yahoo-finance/quote.js +0 -1
- package/dist/clis/youtube/channel.d.ts +1 -0
- package/dist/clis/youtube/channel.js +150 -0
- package/dist/clis/youtube/comments.d.ts +1 -0
- package/dist/clis/youtube/comments.js +95 -0
- package/dist/clis/youtube/search.js +0 -1
- package/dist/clis/zhihu/search.yaml +2 -1
- package/dist/commanderAdapter.d.ts +1 -0
- package/dist/commanderAdapter.js +176 -29
- package/dist/commanderAdapter.test.d.ts +1 -0
- package/dist/commanderAdapter.test.js +62 -0
- package/dist/daemon.js +17 -1
- package/dist/discovery.js +8 -14
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +9 -2
- package/dist/download/index.js +63 -51
- package/dist/download/index.test.js +17 -4
- package/dist/errors.d.ts +3 -1
- package/dist/errors.js +15 -32
- package/dist/execution.d.ts +1 -3
- package/dist/execution.js +21 -1
- package/dist/external-clis.yaml +0 -17
- package/dist/hooks.js +2 -0
- package/dist/main.js +5 -0
- package/dist/output.js +5 -1
- package/dist/pipeline/executor.js +3 -4
- package/dist/plugin-manifest.d.ts +70 -0
- package/dist/plugin-manifest.js +160 -0
- package/dist/plugin-manifest.test.d.ts +4 -0
- package/dist/plugin-manifest.test.js +179 -0
- package/dist/plugin.d.ts +38 -5
- package/dist/plugin.js +267 -33
- package/dist/plugin.test.js +220 -3
- package/dist/registry.d.ts +4 -0
- package/dist/registry.js +2 -0
- package/dist/runtime-detect.d.ts +21 -0
- package/dist/runtime-detect.js +32 -0
- package/dist/runtime-detect.test.d.ts +1 -0
- package/dist/runtime-detect.test.js +27 -0
- package/dist/runtime.js +1 -1
- package/dist/serialization.d.ts +2 -0
- package/dist/serialization.js +6 -0
- package/dist/types.d.ts +1 -0
- package/dist/update-check.d.ts +22 -0
- package/dist/update-check.js +112 -0
- package/dist/weixin-download.test.d.ts +1 -0
- package/dist/weixin-download.test.js +30 -0
- package/dist/weread-private-api-regression.test.d.ts +1 -0
- package/dist/weread-private-api-regression.test.js +122 -0
- package/dist/weread-search-regression.test.d.ts +1 -0
- package/dist/weread-search-regression.test.js +39 -0
- package/dist/yaml-schema.d.ts +3 -0
- package/dist/yaml-schema.js +18 -1
- package/docs/.vitepress/config.mts +17 -0
- package/docs/adapters/browser/36kr.md +47 -0
- package/docs/adapters/browser/douban.md +14 -0
- package/docs/adapters/browser/douyin.md +75 -0
- package/docs/adapters/browser/imdb.md +47 -0
- package/docs/adapters/browser/jd.md +2 -2
- package/docs/adapters/browser/linux-do.md +181 -20
- package/docs/adapters/browser/paperreview.md +43 -0
- package/docs/adapters/browser/producthunt.md +49 -0
- package/docs/adapters/browser/twitter.md +6 -0
- package/docs/adapters/desktop/chatgpt.md +5 -0
- package/docs/adapters/index.md +12 -3
- package/docs/advanced/download.md +4 -0
- package/docs/advanced/rate-limiter-plugin.md +99 -0
- package/docs/guide/electron-app-cli.md +200 -0
- package/docs/guide/getting-started.md +1 -0
- package/docs/guide/plugins.md +87 -0
- package/docs/zh/guide/electron-app-cli.md +188 -0
- package/docs/zh/guide/getting-started.md +1 -0
- package/docs/zh/guide/plugins.md +65 -0
- package/extension/dist/background.js +508 -518
- package/extension/manifest.json +6 -2
- package/extension/package.json +2 -1
- package/extension/popup.html +84 -0
- package/extension/popup.js +25 -0
- package/extension/scripts/package-release.mjs +179 -0
- package/extension/src/background.ts +22 -1
- package/package.json +4 -1
- package/scripts/postinstall.js +10 -0
- package/src/browser/cdp.ts +2 -1
- package/src/browser/discover.ts +8 -3
- package/src/browser/errors.ts +13 -14
- package/src/browser/mcp.ts +2 -1
- package/src/build-manifest.test.ts +23 -0
- package/src/build-manifest.ts +40 -15
- package/src/capabilityRouting.ts +2 -1
- package/src/cli.ts +35 -3
- package/src/clis/36kr/article.ts +69 -0
- package/src/clis/36kr/hot.test.ts +19 -0
- package/src/clis/36kr/hot.ts +100 -0
- package/src/clis/36kr/news.test.ts +90 -0
- package/src/clis/36kr/news.ts +54 -0
- package/src/clis/36kr/search.ts +78 -0
- package/src/clis/apple-podcasts/search.ts +2 -1
- package/src/clis/arxiv/search.ts +2 -2
- package/src/clis/bbc/news.ts +0 -1
- package/src/clis/bilibili/comments.test.ts +102 -0
- package/src/clis/bilibili/comments.ts +44 -0
- package/src/clis/chatgpt/ask.ts +28 -14
- package/src/clis/chatgpt/ax.ts +180 -1
- package/src/clis/chatgpt/model.ts +27 -0
- package/src/clis/chatgpt/send.ts +16 -6
- package/src/clis/ctrip/search.ts +0 -1
- package/src/clis/douban/download.test.ts +196 -0
- package/src/clis/douban/download.ts +78 -0
- package/src/clis/douban/photos.ts +36 -0
- package/src/clis/douban/utils.test.ts +97 -0
- package/src/clis/douban/utils.ts +232 -1
- package/src/clis/douyin/_shared/browser-fetch.test.ts +38 -0
- package/src/clis/douyin/_shared/browser-fetch.ts +45 -0
- package/src/clis/douyin/_shared/creation-id.test.ts +26 -0
- package/src/clis/douyin/_shared/creation-id.ts +8 -0
- package/src/clis/douyin/_shared/imagex-upload.test.ts +113 -0
- package/src/clis/douyin/_shared/imagex-upload.ts +76 -0
- package/src/clis/douyin/_shared/sts2.ts +20 -0
- package/src/clis/douyin/_shared/text-extra.test.ts +42 -0
- package/src/clis/douyin/_shared/text-extra.ts +33 -0
- package/src/clis/douyin/_shared/timing.test.ts +38 -0
- package/src/clis/douyin/_shared/timing.ts +22 -0
- package/src/clis/douyin/_shared/tos-upload-short-read.test.ts +102 -0
- package/src/clis/douyin/_shared/tos-upload.test.ts +281 -0
- package/src/clis/douyin/_shared/tos-upload.ts +444 -0
- package/src/clis/douyin/_shared/transcode.test.ts +117 -0
- package/src/clis/douyin/_shared/transcode.ts +78 -0
- package/src/clis/douyin/_shared/types.ts +29 -0
- package/src/clis/douyin/activities.test.ts +25 -0
- package/src/clis/douyin/activities.ts +23 -0
- package/src/clis/douyin/collections.test.ts +26 -0
- package/src/clis/douyin/collections.ts +25 -0
- package/src/clis/douyin/delete.test.ts +12 -0
- package/src/clis/douyin/delete.ts +20 -0
- package/src/clis/douyin/draft.test.ts +12 -0
- package/src/clis/douyin/draft.ts +282 -0
- package/src/clis/douyin/drafts.test.ts +12 -0
- package/src/clis/douyin/drafts.ts +27 -0
- package/src/clis/douyin/hashtag.test.ts +28 -0
- package/src/clis/douyin/hashtag.ts +56 -0
- package/src/clis/douyin/location.test.ts +26 -0
- package/src/clis/douyin/location.ts +27 -0
- package/src/clis/douyin/profile.test.ts +12 -0
- package/src/clis/douyin/profile.ts +37 -0
- package/src/clis/douyin/publish.test.ts +45 -0
- package/src/clis/douyin/publish.ts +340 -0
- package/src/clis/douyin/stats.test.ts +25 -0
- package/src/clis/douyin/stats.ts +30 -0
- package/src/clis/douyin/update.test.ts +12 -0
- package/src/clis/douyin/update.ts +43 -0
- package/src/clis/douyin/videos.test.ts +12 -0
- package/src/clis/douyin/videos.ts +49 -0
- package/src/clis/hackernews/search.yaml +1 -1
- package/src/clis/imdb/person.ts +232 -0
- package/src/clis/imdb/reviews.ts +111 -0
- package/src/clis/imdb/search.ts +179 -0
- package/src/clis/imdb/title.ts +121 -0
- package/src/clis/imdb/top.ts +67 -0
- package/src/clis/imdb/trending.ts +66 -0
- package/src/clis/imdb/utils.test.ts +117 -0
- package/src/clis/imdb/utils.ts +305 -0
- package/src/clis/instagram/search.yaml +2 -1
- package/src/clis/jd/item.test.ts +18 -1
- package/src/clis/jd/item.ts +18 -15
- package/src/clis/linux-do/categories.yaml +38 -9
- package/src/clis/linux-do/category.ts +37 -0
- package/src/clis/linux-do/feed.test.ts +132 -0
- package/src/clis/linux-do/feed.ts +501 -0
- package/src/clis/linux-do/hot.ts +26 -0
- package/src/clis/linux-do/latest.ts +19 -0
- package/src/clis/linux-do/search.yaml +3 -1
- package/src/clis/linux-do/tags.yaml +41 -0
- package/src/clis/linux-do/topic.yaml +41 -3
- package/src/clis/linux-do/user-posts.yaml +67 -0
- package/src/clis/linux-do/user-topics.yaml +54 -0
- package/src/clis/medium/search.ts +1 -1
- package/src/clis/paperreview/commands.test.ts +283 -0
- package/src/clis/paperreview/feedback.ts +64 -0
- package/src/clis/paperreview/review.ts +47 -0
- package/src/clis/paperreview/submit.ts +119 -0
- package/src/clis/paperreview/utils.test.ts +68 -0
- package/src/clis/paperreview/utils.ts +276 -0
- package/src/clis/producthunt/browse.ts +109 -0
- package/src/clis/producthunt/hot.ts +127 -0
- package/src/clis/producthunt/posts.ts +29 -0
- package/src/clis/producthunt/today.ts +37 -0
- package/src/clis/producthunt/utils.test.ts +72 -0
- package/src/clis/producthunt/utils.ts +122 -0
- package/src/clis/reuters/search.ts +0 -1
- package/src/clis/twitter/article.ts +5 -28
- package/src/clis/twitter/likes.test.ts +91 -0
- package/src/clis/twitter/likes.ts +256 -0
- package/src/clis/twitter/profile.ts +5 -28
- package/src/clis/twitter/search.test.ts +71 -2
- package/src/clis/twitter/search.ts +8 -4
- package/src/clis/twitter/shared.ts +45 -0
- package/src/clis/twitter/timeline.ts +2 -13
- package/src/clis/weibo/comments.ts +54 -0
- package/src/clis/weibo/feed.ts +57 -0
- package/src/clis/weibo/hot.ts +0 -1
- package/src/clis/weibo/me.ts +77 -0
- package/src/clis/weibo/post.ts +77 -0
- package/src/clis/weibo/user.ts +64 -0
- package/src/clis/weibo/utils.ts +32 -0
- package/src/clis/weixin/download.ts +114 -20
- package/src/clis/weread/book.ts +2 -2
- package/src/clis/weread/commands.test.ts +57 -0
- package/src/clis/weread/highlights.ts +2 -2
- package/src/clis/weread/notebooks.ts +2 -2
- package/src/clis/weread/notes.ts +3 -3
- package/src/clis/weread/search.ts +3 -2
- package/src/clis/weread/shelf.ts +2 -2
- package/src/clis/weread/utils.test.ts +1 -32
- package/src/clis/weread/utils.ts +41 -16
- package/src/clis/xiaohongshu/comments.test.ts +96 -0
- package/src/clis/xiaohongshu/comments.ts +81 -0
- package/src/clis/xiaohongshu/publish.test.ts +137 -0
- package/src/clis/xiaohongshu/publish.ts +129 -18
- package/src/clis/xueqiu/search.yaml +2 -1
- package/src/clis/yahoo-finance/quote.ts +0 -1
- package/src/clis/youtube/channel.ts +155 -0
- package/src/clis/youtube/comments.ts +97 -0
- package/src/clis/youtube/search.ts +0 -1
- package/src/clis/zhihu/search.yaml +2 -1
- package/src/commanderAdapter.test.ts +78 -0
- package/src/commanderAdapter.ts +188 -24
- package/src/daemon.ts +19 -1
- package/src/discovery.ts +8 -15
- package/src/doctor.ts +13 -2
- package/src/download/index.test.ts +14 -4
- package/src/download/index.ts +67 -55
- package/src/errors.ts +25 -66
- package/src/execution.ts +28 -3
- package/src/external-clis.yaml +0 -17
- package/src/hooks.ts +1 -0
- package/src/main.ts +6 -0
- package/src/output.ts +3 -1
- package/src/pipeline/executor.ts +4 -6
- package/src/plugin-manifest.test.ts +223 -0
- package/src/plugin-manifest.ts +206 -0
- package/src/plugin.test.ts +246 -2
- package/src/plugin.ts +338 -36
- package/src/registry.ts +6 -1
- package/src/runtime-detect.test.ts +30 -0
- package/src/runtime-detect.ts +36 -0
- package/src/runtime.ts +1 -1
- package/src/serialization.ts +4 -0
- package/src/types.ts +1 -0
- package/src/update-check.ts +114 -0
- package/src/weixin-download.test.ts +64 -0
- package/src/weread-private-api-regression.test.ts +150 -0
- package/src/weread-search-regression.test.ts +44 -0
- package/src/yaml-schema.ts +20 -0
- package/tests/e2e/browser-auth.test.ts +13 -9
- package/tests/e2e/browser-public-extended.test.ts +162 -0
- package/tests/e2e/browser-public.test.ts +55 -136
- package/tests/e2e/helpers.ts +2 -1
- package/tests/e2e/public-commands.test.ts +37 -3
- package/tests/smoke/api-health.test.ts +1 -1
- package/vitest.config.ts +34 -17
- package/dist/clis/linux-do/category.yaml +0 -51
- package/dist/clis/linux-do/hot.yaml +0 -50
- package/dist/clis/linux-do/latest.yaml +0 -40
- package/src/clis/linux-do/category.yaml +0 -51
- package/src/clis/linux-do/hot.yaml +0 -50
- package/src/clis/linux-do/latest.yaml +0 -40
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* E2E tests for browser commands
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
// ──
|
|
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
|
-
// ──
|
|
125
|
-
it('
|
|
126
|
-
const data = await tryBrowserCommand(['
|
|
127
|
-
expectDataOrSkip(data, '
|
|
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
|
-
// ──
|
|
189
|
-
it('
|
|
190
|
-
const data = await
|
|
191
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
});
|
package/tests/e2e/helpers.ts
CHANGED
|
@@ -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
|
|
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', '
|
|
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', '
|
|
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
|
-
|
|
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/
|
|
26
|
-
'src/clis/
|
|
27
|
-
'src/clis/
|
|
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: [
|
|
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
|
-
|
|
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]
|