@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
package/dist/registry.d.ts
CHANGED
|
@@ -39,6 +39,10 @@ export interface CliCommand {
|
|
|
39
39
|
source?: string;
|
|
40
40
|
footerExtra?: (kwargs: CommandArgs) => string | undefined;
|
|
41
41
|
requiredEnv?: RequiredEnv[];
|
|
42
|
+
/** Deprecation note shown in help / execution warnings. */
|
|
43
|
+
deprecated?: boolean | string;
|
|
44
|
+
/** Preferred replacement command, if any. */
|
|
45
|
+
replacedBy?: string;
|
|
42
46
|
/**
|
|
43
47
|
* Control pre-navigation for cookie/header context before command execution.
|
|
44
48
|
*
|
package/dist/registry.js
CHANGED
|
@@ -27,6 +27,8 @@ export function cli(opts) {
|
|
|
27
27
|
timeoutSeconds: opts.timeoutSeconds,
|
|
28
28
|
footerExtra: opts.footerExtra,
|
|
29
29
|
requiredEnv: opts.requiredEnv,
|
|
30
|
+
deprecated: opts.deprecated,
|
|
31
|
+
replacedBy: opts.replacedBy,
|
|
30
32
|
navigateBefore: opts.navigateBefore,
|
|
31
33
|
};
|
|
32
34
|
const key = fullName(cmd);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime detection — identify whether opencli is running under Node.js or Bun.
|
|
3
|
+
*
|
|
4
|
+
* Bun injects `globalThis.Bun` at startup, making detection trivial.
|
|
5
|
+
* This module centralises the check so other code can adapt behaviour
|
|
6
|
+
* (e.g. logging, diagnostics) without littering runtime sniffing everywhere.
|
|
7
|
+
*/
|
|
8
|
+
export type Runtime = 'bun' | 'node';
|
|
9
|
+
/**
|
|
10
|
+
* Detect the current JavaScript runtime.
|
|
11
|
+
*/
|
|
12
|
+
export declare function detectRuntime(): Runtime;
|
|
13
|
+
/**
|
|
14
|
+
* Return a human-readable version string for the current runtime.
|
|
15
|
+
* Examples: "v22.13.0" (Node), "1.1.42" (Bun)
|
|
16
|
+
*/
|
|
17
|
+
export declare function getRuntimeVersion(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Return a combined label like "node v22.13.0" or "bun 1.1.42".
|
|
20
|
+
*/
|
|
21
|
+
export declare function getRuntimeLabel(): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime detection — identify whether opencli is running under Node.js or Bun.
|
|
3
|
+
*
|
|
4
|
+
* Bun injects `globalThis.Bun` at startup, making detection trivial.
|
|
5
|
+
* This module centralises the check so other code can adapt behaviour
|
|
6
|
+
* (e.g. logging, diagnostics) without littering runtime sniffing everywhere.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Detect the current JavaScript runtime.
|
|
10
|
+
*/
|
|
11
|
+
export function detectRuntime() {
|
|
12
|
+
// Bun always exposes globalThis.Bun (including Bun.version)
|
|
13
|
+
if (typeof globalThis.Bun !== 'undefined')
|
|
14
|
+
return 'bun';
|
|
15
|
+
return 'node';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Return a human-readable version string for the current runtime.
|
|
19
|
+
* Examples: "v22.13.0" (Node), "1.1.42" (Bun)
|
|
20
|
+
*/
|
|
21
|
+
export function getRuntimeVersion() {
|
|
22
|
+
if (detectRuntime() === 'bun') {
|
|
23
|
+
return globalThis.Bun.version;
|
|
24
|
+
}
|
|
25
|
+
return process.version; // e.g. "v22.13.0"
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Return a combined label like "node v22.13.0" or "bun 1.1.42".
|
|
29
|
+
*/
|
|
30
|
+
export function getRuntimeLabel() {
|
|
31
|
+
return `${detectRuntime()} ${getRuntimeVersion()}`;
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { detectRuntime, getRuntimeVersion, getRuntimeLabel } from './runtime-detect.js';
|
|
3
|
+
describe('runtime-detect', () => {
|
|
4
|
+
it('detectRuntime returns a valid runtime string', () => {
|
|
5
|
+
const rt = detectRuntime();
|
|
6
|
+
expect(['bun', 'node']).toContain(rt);
|
|
7
|
+
});
|
|
8
|
+
it('getRuntimeVersion returns a non-empty version string', () => {
|
|
9
|
+
const ver = getRuntimeVersion();
|
|
10
|
+
expect(typeof ver).toBe('string');
|
|
11
|
+
expect(ver.length).toBeGreaterThan(0);
|
|
12
|
+
});
|
|
13
|
+
it('getRuntimeLabel returns "<runtime> <version>" format', () => {
|
|
14
|
+
const label = getRuntimeLabel();
|
|
15
|
+
expect(label).toMatch(/^(bun|node) .+$/);
|
|
16
|
+
});
|
|
17
|
+
it('detects the current environment correctly', () => {
|
|
18
|
+
const isBun = typeof globalThis.Bun !== 'undefined';
|
|
19
|
+
const rt = detectRuntime();
|
|
20
|
+
if (isBun) {
|
|
21
|
+
expect(rt).toBe('bun');
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
expect(rt).toBe('node');
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
package/dist/runtime.js
CHANGED
|
@@ -5,7 +5,7 @@ import { TimeoutError } from './errors.js';
|
|
|
5
5
|
* Uses CDPBridge when OPENCLI_CDP_ENDPOINT is set, otherwise BrowserBridge.
|
|
6
6
|
*/
|
|
7
7
|
export function getBrowserFactory() {
|
|
8
|
-
return
|
|
8
|
+
return process.env.OPENCLI_CDP_ENDPOINT ? CDPBridge : BrowserBridge;
|
|
9
9
|
}
|
|
10
10
|
function parseEnvTimeout(envVar, fallback) {
|
|
11
11
|
const raw = process.env[envVar];
|
package/dist/serialization.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export declare function serializeCommand(cmd: CliCommand): {
|
|
|
27
27
|
args: SerializedArg[];
|
|
28
28
|
columns: string[];
|
|
29
29
|
domain: string | null;
|
|
30
|
+
deprecated: string | boolean | null;
|
|
31
|
+
replacedBy: string | null;
|
|
30
32
|
};
|
|
31
33
|
/** Human-readable arg summary: `<required> [optional]` style. */
|
|
32
34
|
export declare function formatArgSummary(args: Arg[]): string;
|
package/dist/serialization.js
CHANGED
|
@@ -29,6 +29,8 @@ export function serializeCommand(cmd) {
|
|
|
29
29
|
args: cmd.args.map(serializeArg),
|
|
30
30
|
columns: cmd.columns ?? [],
|
|
31
31
|
domain: cmd.domain ?? null,
|
|
32
|
+
deprecated: cmd.deprecated ?? null,
|
|
33
|
+
replacedBy: cmd.replacedBy ?? null,
|
|
32
34
|
};
|
|
33
35
|
}
|
|
34
36
|
// ── Formatting ──────────────────────────────────────────────────────────────
|
|
@@ -56,6 +58,10 @@ export function formatRegistryHelpText(cmd) {
|
|
|
56
58
|
meta.push(`Browser: ${cmd.browser ? 'yes' : 'no'}`);
|
|
57
59
|
if (cmd.domain)
|
|
58
60
|
meta.push(`Domain: ${cmd.domain}`);
|
|
61
|
+
if (cmd.deprecated)
|
|
62
|
+
meta.push(`Deprecated: ${typeof cmd.deprecated === 'string' ? cmd.deprecated : 'yes'}`);
|
|
63
|
+
if (cmd.replacedBy)
|
|
64
|
+
meta.push(`Use instead: ${cmd.replacedBy}`);
|
|
59
65
|
lines.push(meta.join(' | '));
|
|
60
66
|
if (cmd.columns?.length)
|
|
61
67
|
lines.push(`Output columns: ${cmd.columns.join(', ')}`);
|
package/dist/types.d.ts
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-blocking update checker.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: register exit-hook + kick-off-background-fetch
|
|
5
|
+
* - On startup: kick off background fetch (non-blocking)
|
|
6
|
+
* - On process exit: read cache, print notice if newer version exists
|
|
7
|
+
* - Check interval: 24 hours
|
|
8
|
+
* - Notice appears AFTER command output, not before (same as npm/gh/yarn)
|
|
9
|
+
* - Never delays or blocks the CLI command
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Register a process exit hook that prints an update notice if a newer
|
|
13
|
+
* version was found on the last background check.
|
|
14
|
+
* Notice appears after command output — same pattern as npm/gh/yarn.
|
|
15
|
+
* Skipped during --get-completions to avoid polluting shell completion output.
|
|
16
|
+
*/
|
|
17
|
+
export declare function registerUpdateNoticeOnExit(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Kick off a background fetch to npm registry. Writes to cache for next run.
|
|
20
|
+
* Fully non-blocking — never awaited.
|
|
21
|
+
*/
|
|
22
|
+
export declare function checkForUpdateBackground(): void;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-blocking update checker.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: register exit-hook + kick-off-background-fetch
|
|
5
|
+
* - On startup: kick off background fetch (non-blocking)
|
|
6
|
+
* - On process exit: read cache, print notice if newer version exists
|
|
7
|
+
* - Check interval: 24 hours
|
|
8
|
+
* - Notice appears AFTER command output, not before (same as npm/gh/yarn)
|
|
9
|
+
* - Never delays or blocks the CLI command
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from 'node:fs';
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import * as os from 'node:os';
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import { PKG_VERSION } from './version.js';
|
|
16
|
+
const CACHE_DIR = path.join(os.homedir(), '.opencli');
|
|
17
|
+
const CACHE_FILE = path.join(CACHE_DIR, 'update-check.json');
|
|
18
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24h
|
|
19
|
+
const NPM_REGISTRY_URL = 'https://registry.npmjs.org/@jackwener/opencli/latest';
|
|
20
|
+
// Read cache once at module load — shared by both exported functions
|
|
21
|
+
const _cache = (() => {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
})();
|
|
29
|
+
function writeCache(latestVersion) {
|
|
30
|
+
try {
|
|
31
|
+
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
32
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify({ lastCheck: Date.now(), latestVersion }), 'utf-8');
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Best-effort; never fail
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Compare semver strings. Returns true if `a` is strictly newer than `b`. */
|
|
39
|
+
function isNewer(a, b) {
|
|
40
|
+
const parse = (v) => v.replace(/^v/, '').split('-')[0].split('.').map(Number);
|
|
41
|
+
const pa = parse(a);
|
|
42
|
+
const pb = parse(b);
|
|
43
|
+
if (pa.some(isNaN) || pb.some(isNaN))
|
|
44
|
+
return false;
|
|
45
|
+
const [aMaj, aMin, aPat] = pa;
|
|
46
|
+
const [bMaj, bMin, bPat] = pb;
|
|
47
|
+
if (aMaj !== bMaj)
|
|
48
|
+
return aMaj > bMaj;
|
|
49
|
+
if (aMin !== bMin)
|
|
50
|
+
return aMin > bMin;
|
|
51
|
+
return aPat > bPat;
|
|
52
|
+
}
|
|
53
|
+
function isCI() {
|
|
54
|
+
return !!(process.env.CI || process.env.CONTINUOUS_INTEGRATION);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Register a process exit hook that prints an update notice if a newer
|
|
58
|
+
* version was found on the last background check.
|
|
59
|
+
* Notice appears after command output — same pattern as npm/gh/yarn.
|
|
60
|
+
* Skipped during --get-completions to avoid polluting shell completion output.
|
|
61
|
+
*/
|
|
62
|
+
export function registerUpdateNoticeOnExit() {
|
|
63
|
+
if (isCI())
|
|
64
|
+
return;
|
|
65
|
+
if (process.argv.includes('--get-completions'))
|
|
66
|
+
return;
|
|
67
|
+
process.on('exit', (code) => {
|
|
68
|
+
if (code !== 0)
|
|
69
|
+
return; // Don't show update notice on error exit
|
|
70
|
+
if (!_cache)
|
|
71
|
+
return;
|
|
72
|
+
if (!isNewer(_cache.latestVersion, PKG_VERSION))
|
|
73
|
+
return;
|
|
74
|
+
try {
|
|
75
|
+
process.stderr.write(chalk.yellow(`\n Update available: v${PKG_VERSION} → v${_cache.latestVersion}\n`) +
|
|
76
|
+
chalk.dim(` Run: npm install -g @jackwener/opencli\n\n`));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Ignore broken pipe (stderr closed before process exits)
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Kick off a background fetch to npm registry. Writes to cache for next run.
|
|
85
|
+
* Fully non-blocking — never awaited.
|
|
86
|
+
*/
|
|
87
|
+
export function checkForUpdateBackground() {
|
|
88
|
+
if (isCI())
|
|
89
|
+
return;
|
|
90
|
+
if (_cache && Date.now() - _cache.lastCheck < CHECK_INTERVAL_MS)
|
|
91
|
+
return;
|
|
92
|
+
void (async () => {
|
|
93
|
+
try {
|
|
94
|
+
const controller = new AbortController();
|
|
95
|
+
const timer = setTimeout(() => controller.abort(), 3000);
|
|
96
|
+
const res = await fetch(NPM_REGISTRY_URL, {
|
|
97
|
+
signal: controller.signal,
|
|
98
|
+
headers: { 'User-Agent': `opencli/${PKG_VERSION}` },
|
|
99
|
+
});
|
|
100
|
+
clearTimeout(timer);
|
|
101
|
+
if (!res.ok)
|
|
102
|
+
return;
|
|
103
|
+
const data = await res.json();
|
|
104
|
+
if (typeof data.version === 'string') {
|
|
105
|
+
writeCache(data.version);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Network error: silently skip, try again next run
|
|
110
|
+
}
|
|
111
|
+
})();
|
|
112
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
async function loadModule() {
|
|
3
|
+
return import('./clis/weixin/download.js');
|
|
4
|
+
}
|
|
5
|
+
describe('weixin publish time extraction', () => {
|
|
6
|
+
it('prefers publish_time text over create_time-like date strings', async () => {
|
|
7
|
+
const mod = await loadModule();
|
|
8
|
+
expect(mod.extractWechatPublishTime('2026年3月24日 22:38', 'var create_time = "2026年3月24日 22:38";')).toBe('2026年3月24日 22:38');
|
|
9
|
+
});
|
|
10
|
+
it('falls back to unix timestamp create_time values', async () => {
|
|
11
|
+
const mod = await loadModule();
|
|
12
|
+
expect(mod.extractWechatPublishTime('', 'var create_time = "1711291080";')).toBe('2024-03-24 22:38:00');
|
|
13
|
+
});
|
|
14
|
+
it('rejects malformed create_time values', async () => {
|
|
15
|
+
const mod = await loadModule();
|
|
16
|
+
expect(mod.extractWechatPublishTime('', 'var create_time = "2026年3月24日 22:38";')).toBe('');
|
|
17
|
+
expect(mod.extractWechatPublishTime('', 'var create_time = "1711291080abc";')).toBe('');
|
|
18
|
+
expect(mod.extractWechatPublishTime('', 'var create_time = "17112910800";')).toBe('');
|
|
19
|
+
});
|
|
20
|
+
it('builds a self-contained browser helper that matches fallback behavior', async () => {
|
|
21
|
+
const mod = await loadModule();
|
|
22
|
+
const extractInPage = eval(mod.buildExtractWechatPublishTimeJs());
|
|
23
|
+
expect(extractInPage('', 'var create_time = "1711291080";')).toBe('2024-03-24 22:38:00');
|
|
24
|
+
});
|
|
25
|
+
it('browser helper still prefers DOM publish_time text', async () => {
|
|
26
|
+
const mod = await loadModule();
|
|
27
|
+
const extractInPage = eval(mod.buildExtractWechatPublishTimeJs());
|
|
28
|
+
expect(extractInPage('2026年3月24日 22:38', 'var create_time = "1711291080";')).toBe('2026年3月24日 22:38');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './clis/weread/shelf.js';
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { getRegistry } from './registry.js';
|
|
3
|
+
import { fetchPrivateApi } from './clis/weread/utils.js';
|
|
4
|
+
import './clis/weread/shelf.js';
|
|
5
|
+
describe('weread private API regression', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
vi.restoreAllMocks();
|
|
8
|
+
});
|
|
9
|
+
it('uses browser cookies and Node fetch for private API requests', async () => {
|
|
10
|
+
const mockPage = {
|
|
11
|
+
getCookies: vi.fn()
|
|
12
|
+
.mockResolvedValueOnce([
|
|
13
|
+
{ name: 'wr_name', value: 'alice', domain: 'weread.qq.com' },
|
|
14
|
+
{ name: 'wr_vid', value: 'vid123', domain: 'i.weread.qq.com' },
|
|
15
|
+
]),
|
|
16
|
+
evaluate: vi.fn(),
|
|
17
|
+
};
|
|
18
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
19
|
+
ok: true,
|
|
20
|
+
status: 200,
|
|
21
|
+
json: () => Promise.resolve({ title: 'Test Book', errcode: 0 }),
|
|
22
|
+
});
|
|
23
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
24
|
+
const result = await fetchPrivateApi(mockPage, '/book/info', { bookId: '123' });
|
|
25
|
+
expect(result.title).toBe('Test Book');
|
|
26
|
+
expect(mockPage.getCookies).toHaveBeenCalledTimes(1);
|
|
27
|
+
expect(mockPage.getCookies).toHaveBeenCalledWith({ url: 'https://i.weread.qq.com/book/info?bookId=123' });
|
|
28
|
+
expect(mockPage.evaluate).not.toHaveBeenCalled();
|
|
29
|
+
expect(fetchMock).toHaveBeenCalledWith('https://i.weread.qq.com/book/info?bookId=123', expect.objectContaining({
|
|
30
|
+
headers: expect.objectContaining({
|
|
31
|
+
Cookie: 'wr_name=alice; wr_vid=vid123',
|
|
32
|
+
}),
|
|
33
|
+
}));
|
|
34
|
+
});
|
|
35
|
+
it('maps unauthenticated private API responses to AUTH_REQUIRED', async () => {
|
|
36
|
+
const mockPage = {
|
|
37
|
+
getCookies: vi.fn().mockResolvedValue([]),
|
|
38
|
+
evaluate: vi.fn(),
|
|
39
|
+
};
|
|
40
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
41
|
+
ok: false,
|
|
42
|
+
status: 401,
|
|
43
|
+
json: () => Promise.resolve({ errcode: -2010, errmsg: '用户不存在' }),
|
|
44
|
+
}));
|
|
45
|
+
await expect(fetchPrivateApi(mockPage, '/book/info')).rejects.toThrow('Not logged in');
|
|
46
|
+
});
|
|
47
|
+
it('maps non-auth API errors to API_ERROR', async () => {
|
|
48
|
+
const mockPage = {
|
|
49
|
+
getCookies: vi.fn().mockResolvedValue([]),
|
|
50
|
+
evaluate: vi.fn(),
|
|
51
|
+
};
|
|
52
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
53
|
+
ok: true,
|
|
54
|
+
status: 200,
|
|
55
|
+
json: () => Promise.resolve({ errcode: -1, errmsg: 'unknown error' }),
|
|
56
|
+
}));
|
|
57
|
+
await expect(fetchPrivateApi(mockPage, '/book/info')).rejects.toThrow('unknown error');
|
|
58
|
+
});
|
|
59
|
+
it('maps non-401 HTTP failures to FETCH_ERROR', async () => {
|
|
60
|
+
const mockPage = {
|
|
61
|
+
getCookies: vi.fn().mockResolvedValue([]),
|
|
62
|
+
evaluate: vi.fn(),
|
|
63
|
+
};
|
|
64
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
65
|
+
ok: false,
|
|
66
|
+
status: 403,
|
|
67
|
+
json: () => Promise.resolve({ errmsg: 'forbidden' }),
|
|
68
|
+
}));
|
|
69
|
+
await expect(fetchPrivateApi(mockPage, '/book/info')).rejects.toThrow('HTTP 403');
|
|
70
|
+
});
|
|
71
|
+
it('maps invalid JSON to PARSE_ERROR', async () => {
|
|
72
|
+
const mockPage = {
|
|
73
|
+
getCookies: vi.fn().mockResolvedValue([]),
|
|
74
|
+
evaluate: vi.fn(),
|
|
75
|
+
};
|
|
76
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
77
|
+
ok: true,
|
|
78
|
+
status: 200,
|
|
79
|
+
json: () => Promise.reject(new SyntaxError('Unexpected token <')),
|
|
80
|
+
}));
|
|
81
|
+
await expect(fetchPrivateApi(mockPage, '/book/info')).rejects.toThrow('Invalid JSON');
|
|
82
|
+
});
|
|
83
|
+
it('routes weread shelf through the private API helper path', async () => {
|
|
84
|
+
const command = getRegistry().get('weread/shelf');
|
|
85
|
+
expect(command?.func).toBeTypeOf('function');
|
|
86
|
+
const mockPage = {
|
|
87
|
+
getCookies: vi.fn()
|
|
88
|
+
.mockResolvedValueOnce([
|
|
89
|
+
{ name: 'wr_name', value: 'alice', domain: 'weread.qq.com' },
|
|
90
|
+
{ name: 'wr_vid', value: 'vid123', domain: 'i.weread.qq.com' },
|
|
91
|
+
]),
|
|
92
|
+
evaluate: vi.fn(),
|
|
93
|
+
};
|
|
94
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
95
|
+
ok: true,
|
|
96
|
+
status: 200,
|
|
97
|
+
json: () => Promise.resolve({
|
|
98
|
+
books: [{
|
|
99
|
+
title: 'Deep Work',
|
|
100
|
+
author: 'Cal Newport',
|
|
101
|
+
readingProgress: 42,
|
|
102
|
+
bookId: 'abc123',
|
|
103
|
+
}],
|
|
104
|
+
}),
|
|
105
|
+
});
|
|
106
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
107
|
+
const result = await command.func(mockPage, { limit: 1 });
|
|
108
|
+
expect(mockPage.evaluate).not.toHaveBeenCalled();
|
|
109
|
+
expect(fetchMock).toHaveBeenCalledWith('https://i.weread.qq.com/shelf/sync?synckey=0&lectureSynckey=0', expect.any(Object));
|
|
110
|
+
expect(mockPage.getCookies).toHaveBeenCalledWith({
|
|
111
|
+
url: 'https://i.weread.qq.com/shelf/sync?synckey=0&lectureSynckey=0',
|
|
112
|
+
});
|
|
113
|
+
expect(result).toEqual([
|
|
114
|
+
{
|
|
115
|
+
title: 'Deep Work',
|
|
116
|
+
author: 'Cal Newport',
|
|
117
|
+
progress: '42%',
|
|
118
|
+
bookId: 'abc123',
|
|
119
|
+
},
|
|
120
|
+
]);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './clis/weread/search.js';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { getRegistry } from './registry.js';
|
|
3
|
+
import './clis/weread/search.js';
|
|
4
|
+
describe('weread/search regression', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.restoreAllMocks();
|
|
7
|
+
});
|
|
8
|
+
it('uses the query argument for the search API and returns urls', async () => {
|
|
9
|
+
const command = getRegistry().get('weread/search');
|
|
10
|
+
expect(command?.func).toBeTypeOf('function');
|
|
11
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
12
|
+
ok: true,
|
|
13
|
+
json: () => Promise.resolve({
|
|
14
|
+
books: [
|
|
15
|
+
{
|
|
16
|
+
bookInfo: {
|
|
17
|
+
title: 'Deep Work',
|
|
18
|
+
author: 'Cal Newport',
|
|
19
|
+
bookId: 'abc123',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
25
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
26
|
+
const result = await command.func(null, { query: 'deep work', limit: 5 });
|
|
27
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
28
|
+
expect(String(fetchMock.mock.calls[0][0])).toContain('keyword=deep+work');
|
|
29
|
+
expect(result).toEqual([
|
|
30
|
+
{
|
|
31
|
+
rank: 1,
|
|
32
|
+
title: 'Deep Work',
|
|
33
|
+
author: 'Cal Newport',
|
|
34
|
+
bookId: 'abc123',
|
|
35
|
+
url: 'https://weread.qq.com/web/bookDetail/abc123',
|
|
36
|
+
},
|
|
37
|
+
]);
|
|
38
|
+
});
|
|
39
|
+
});
|
package/dist/yaml-schema.d.ts
CHANGED
|
@@ -24,3 +24,6 @@ export interface YamlCliDefinition {
|
|
|
24
24
|
timeout?: number;
|
|
25
25
|
navigateBefore?: boolean | string;
|
|
26
26
|
}
|
|
27
|
+
import type { Arg } from './registry.js';
|
|
28
|
+
/** Convert YAML args definition to the internal Arg[] format. */
|
|
29
|
+
export declare function parseYamlArgs(args: Record<string, YamlArgDefinition> | undefined): Arg[];
|
package/dist/yaml-schema.js
CHANGED
|
@@ -2,4 +2,21 @@
|
|
|
2
2
|
* Shared YAML CLI definition types.
|
|
3
3
|
* Used by both discovery.ts (runtime) and build-manifest.ts (build-time).
|
|
4
4
|
*/
|
|
5
|
-
|
|
5
|
+
/** Convert YAML args definition to the internal Arg[] format. */
|
|
6
|
+
export function parseYamlArgs(args) {
|
|
7
|
+
if (!args || typeof args !== 'object')
|
|
8
|
+
return [];
|
|
9
|
+
const result = [];
|
|
10
|
+
for (const [argName, argDef] of Object.entries(args)) {
|
|
11
|
+
result.push({
|
|
12
|
+
name: argName,
|
|
13
|
+
type: argDef?.type ?? 'str',
|
|
14
|
+
default: argDef?.default,
|
|
15
|
+
required: argDef?.required ?? false,
|
|
16
|
+
positional: argDef?.positional ?? false,
|
|
17
|
+
help: argDef?.description ?? argDef?.help ?? '',
|
|
18
|
+
choices: argDef?.choices,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
@@ -32,6 +32,7 @@ export default defineConfig({
|
|
|
32
32
|
{ text: 'Comparison', link: '/comparison' },
|
|
33
33
|
{ text: 'Browser Bridge', link: '/guide/browser-bridge' },
|
|
34
34
|
{ text: 'Troubleshooting', link: '/guide/troubleshooting' },
|
|
35
|
+
{ text: 'Add an Electron App CLI', link: '/guide/electron-app-cli' },
|
|
35
36
|
{ text: 'Plugins', link: '/guide/plugins' },
|
|
36
37
|
],
|
|
37
38
|
},
|
|
@@ -74,6 +75,17 @@ export default defineConfig({
|
|
|
74
75
|
{ text: 'Sina Blog', link: '/adapters/browser/sinablog' },
|
|
75
76
|
{ text: 'Substack', link: '/adapters/browser/substack' },
|
|
76
77
|
{ text: 'Pixiv', link: '/adapters/browser/pixiv' },
|
|
78
|
+
{ text: 'Douban', link: '/adapters/browser/douban' },
|
|
79
|
+
{ text: 'Doubao', link: '/adapters/browser/doubao' },
|
|
80
|
+
{ text: 'Facebook', link: '/adapters/browser/facebook' },
|
|
81
|
+
{ text: 'Google', link: '/adapters/browser/google' },
|
|
82
|
+
{ text: 'IMDb', link: '/adapters/browser/imdb' },
|
|
83
|
+
{ text: 'Instagram', link: '/adapters/browser/instagram' },
|
|
84
|
+
{ text: 'JD.com', link: '/adapters/browser/jd' },
|
|
85
|
+
{ text: 'Medium', link: '/adapters/browser/medium' },
|
|
86
|
+
{ text: 'TikTok', link: '/adapters/browser/tiktok' },
|
|
87
|
+
{ text: 'Web (Generic)', link: '/adapters/browser/web' },
|
|
88
|
+
{ text: 'Weixin', link: '/adapters/browser/weixin' },
|
|
77
89
|
],
|
|
78
90
|
},
|
|
79
91
|
{
|
|
@@ -88,11 +100,14 @@ export default defineConfig({
|
|
|
88
100
|
{ text: 'Xiaoyuzhou', link: '/adapters/browser/xiaoyuzhou' },
|
|
89
101
|
{ text: 'Yahoo Finance', link: '/adapters/browser/yahoo-finance' },
|
|
90
102
|
{ text: 'arXiv', link: '/adapters/browser/arxiv' },
|
|
103
|
+
{ text: 'paperreview.ai', link: '/adapters/browser/paperreview' },
|
|
91
104
|
{ text: 'Barchart', link: '/adapters/browser/barchart' },
|
|
92
105
|
{ text: 'Hugging Face', link: '/adapters/browser/hf' },
|
|
93
106
|
{ text: 'Sina Finance', link: '/adapters/browser/sinafinance' },
|
|
94
107
|
{ text: 'Stack Overflow', link: '/adapters/browser/stackoverflow' },
|
|
95
108
|
{ text: 'Wikipedia', link: '/adapters/browser/wikipedia' },
|
|
109
|
+
{ text: 'Lobsters', link: '/adapters/browser/lobsters' },
|
|
110
|
+
{ text: 'Steam', link: '/adapters/browser/steam' },
|
|
96
111
|
],
|
|
97
112
|
},
|
|
98
113
|
{
|
|
@@ -106,6 +121,7 @@ export default defineConfig({
|
|
|
106
121
|
{ text: 'ChatWise', link: '/adapters/desktop/chatwise' },
|
|
107
122
|
{ text: 'Notion', link: '/adapters/desktop/notion' },
|
|
108
123
|
{ text: 'Discord', link: '/adapters/desktop/discord' },
|
|
124
|
+
{ text: 'Doubao App', link: '/adapters/desktop/doubao-app' },
|
|
109
125
|
],
|
|
110
126
|
},
|
|
111
127
|
],
|
|
@@ -155,6 +171,7 @@ export default defineConfig({
|
|
|
155
171
|
{ text: '快速开始', link: '/zh/guide/getting-started' },
|
|
156
172
|
{ text: '安装', link: '/zh/guide/installation' },
|
|
157
173
|
{ text: 'Browser Bridge', link: '/zh/guide/browser-bridge' },
|
|
174
|
+
{ text: '给新 Electron 应用生成 CLI', link: '/zh/guide/electron-app-cli' },
|
|
158
175
|
{ text: '插件', link: '/zh/guide/plugins' },
|
|
159
176
|
],
|
|
160
177
|
},
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 36kr (36氪)
|
|
2
|
+
|
|
3
|
+
**Mode**: 🌐 Public / 🔐 Browser · **Domain**: `36kr.com`
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
| Command | Description |
|
|
8
|
+
|---------|-------------|
|
|
9
|
+
| `opencli 36kr hot` | 36氪热榜 — trending articles |
|
|
10
|
+
| `opencli 36kr news` | Latest tech/startup news from 36kr |
|
|
11
|
+
| `opencli 36kr search <query>` | Search 36kr articles |
|
|
12
|
+
| `opencli 36kr article <id-or-url>` | Read full article content |
|
|
13
|
+
|
|
14
|
+
## Usage Examples
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Trending articles
|
|
18
|
+
opencli 36kr hot --limit 10
|
|
19
|
+
|
|
20
|
+
# Hot by type
|
|
21
|
+
opencli 36kr hot --type renqi --limit 10
|
|
22
|
+
opencli 36kr hot --type zonghe --limit 10
|
|
23
|
+
|
|
24
|
+
# Latest news
|
|
25
|
+
opencli 36kr news --limit 20
|
|
26
|
+
|
|
27
|
+
# Search articles
|
|
28
|
+
opencli 36kr search "AI" --limit 10
|
|
29
|
+
opencli 36kr search "OpenAI" --limit 5
|
|
30
|
+
|
|
31
|
+
# Read full article (by ID or URL)
|
|
32
|
+
opencli 36kr article 3000000123456
|
|
33
|
+
opencli 36kr article https://36kr.com/p/3000000123456
|
|
34
|
+
|
|
35
|
+
# JSON output
|
|
36
|
+
opencli 36kr hot -f json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Notes
|
|
40
|
+
|
|
41
|
+
- `news` uses the public RSS feed and works without Browser Bridge.
|
|
42
|
+
- `hot`, `search`, and `article` use Browser Bridge and are best run with Chrome open.
|
|
43
|
+
- `hot --type` accepts `catalog`, `renqi`, `zonghe`, and `shoucang`.
|
|
44
|
+
|
|
45
|
+
## Prerequisites
|
|
46
|
+
|
|
47
|
+
- No browser required — uses public API
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
| `opencli douban search` | 搜索豆瓣电影、图书或音乐 |
|
|
10
10
|
| `opencli douban top250` | 豆瓣电影 Top 250 |
|
|
11
11
|
| `opencli douban subject` | 条目详情 |
|
|
12
|
+
| `opencli douban photos` | 获取电影海报/剧照图片列表 |
|
|
13
|
+
| `opencli douban download` | 下载电影海报/剧照图片 |
|
|
12
14
|
| `opencli douban marks` | 我的标记 |
|
|
13
15
|
| `opencli douban reviews` | 我的短评 |
|
|
14
16
|
| `opencli douban movie-hot` | 豆瓣电影热门榜单 |
|
|
@@ -32,6 +34,18 @@ opencli douban top250 --limit 10
|
|
|
32
34
|
# 条目详情
|
|
33
35
|
opencli douban subject 1292052
|
|
34
36
|
|
|
37
|
+
# 获取海报直链(默认 type=Rb)
|
|
38
|
+
opencli douban photos 30382501 --limit 20
|
|
39
|
+
|
|
40
|
+
# 下载海报到本地目录
|
|
41
|
+
opencli douban download 30382501 --output ./douban
|
|
42
|
+
|
|
43
|
+
# 只下载指定 photo_id 的一张图
|
|
44
|
+
opencli douban download 30382501 --photo-id 2913621075 --output ./douban
|
|
45
|
+
|
|
46
|
+
# 返回 JSON,便于上层界面直接渲染图片并右键取图
|
|
47
|
+
opencli douban photos 30382501 -f json
|
|
48
|
+
|
|
35
49
|
# 电影热门
|
|
36
50
|
opencli douban movie-hot --limit 10
|
|
37
51
|
|