@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/errors.d.ts
CHANGED
|
@@ -12,8 +12,10 @@ export declare class CliError extends Error {
|
|
|
12
12
|
readonly hint?: string;
|
|
13
13
|
constructor(code: string, message: string, hint?: string);
|
|
14
14
|
}
|
|
15
|
+
export type BrowserConnectKind = 'daemon-not-running' | 'extension-not-connected' | 'command-failed' | 'unknown';
|
|
15
16
|
export declare class BrowserConnectError extends CliError {
|
|
16
|
-
|
|
17
|
+
readonly kind: BrowserConnectKind;
|
|
18
|
+
constructor(message: string, hint?: string, kind?: BrowserConnectKind);
|
|
17
19
|
}
|
|
18
20
|
export declare class AdapterLoadError extends CliError {
|
|
19
21
|
constructor(message: string, hint?: string);
|
package/dist/errors.js
CHANGED
|
@@ -12,74 +12,50 @@ export class CliError extends Error {
|
|
|
12
12
|
hint;
|
|
13
13
|
constructor(code, message, hint) {
|
|
14
14
|
super(message);
|
|
15
|
-
this.name =
|
|
15
|
+
this.name = new.target.name;
|
|
16
16
|
this.code = code;
|
|
17
17
|
this.hint = hint;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
// ── Browser / Connection ────────────────────────────────────────────────────
|
|
21
20
|
export class BrowserConnectError extends CliError {
|
|
22
|
-
|
|
21
|
+
kind;
|
|
22
|
+
constructor(message, hint, kind = 'unknown') {
|
|
23
23
|
super('BROWSER_CONNECT', message, hint);
|
|
24
|
-
this.
|
|
24
|
+
this.kind = kind;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
// ── Adapter loading ─────────────────────────────────────────────────────────
|
|
28
27
|
export class AdapterLoadError extends CliError {
|
|
29
|
-
constructor(message, hint) {
|
|
30
|
-
super('ADAPTER_LOAD', message, hint);
|
|
31
|
-
this.name = 'AdapterLoadError';
|
|
32
|
-
}
|
|
28
|
+
constructor(message, hint) { super('ADAPTER_LOAD', message, hint); }
|
|
33
29
|
}
|
|
34
|
-
// ── Command execution ───────────────────────────────────────────────────────
|
|
35
30
|
export class CommandExecutionError extends CliError {
|
|
36
|
-
constructor(message, hint) {
|
|
37
|
-
super('COMMAND_EXEC', message, hint);
|
|
38
|
-
this.name = 'CommandExecutionError';
|
|
39
|
-
}
|
|
31
|
+
constructor(message, hint) { super('COMMAND_EXEC', message, hint); }
|
|
40
32
|
}
|
|
41
|
-
// ── Configuration ───────────────────────────────────────────────────────────
|
|
42
33
|
export class ConfigError extends CliError {
|
|
43
|
-
constructor(message, hint) {
|
|
44
|
-
super('CONFIG', message, hint);
|
|
45
|
-
this.name = 'ConfigError';
|
|
46
|
-
}
|
|
34
|
+
constructor(message, hint) { super('CONFIG', message, hint); }
|
|
47
35
|
}
|
|
48
|
-
// ── Authentication / Login ──────────────────────────────────────────────────
|
|
49
36
|
export class AuthRequiredError extends CliError {
|
|
50
37
|
domain;
|
|
51
38
|
constructor(domain, message) {
|
|
52
39
|
super('AUTH_REQUIRED', message ?? `Not logged in to ${domain}`, `Please open Chrome and log in to https://${domain}`);
|
|
53
|
-
this.name = 'AuthRequiredError';
|
|
54
40
|
this.domain = domain;
|
|
55
41
|
}
|
|
56
42
|
}
|
|
57
|
-
// ── Timeout ─────────────────────────────────────────────────────────────────
|
|
58
43
|
export class TimeoutError extends CliError {
|
|
59
44
|
constructor(label, seconds) {
|
|
60
45
|
super('TIMEOUT', `${label} timed out after ${seconds}s`, 'Try again, or increase timeout with OPENCLI_BROWSER_COMMAND_TIMEOUT env var');
|
|
61
|
-
this.name = 'TimeoutError';
|
|
62
46
|
}
|
|
63
47
|
}
|
|
64
|
-
// ── Argument validation ─────────────────────────────────────────────────────
|
|
65
48
|
export class ArgumentError extends CliError {
|
|
66
|
-
constructor(message, hint) {
|
|
67
|
-
super('ARGUMENT', message, hint);
|
|
68
|
-
this.name = 'ArgumentError';
|
|
69
|
-
}
|
|
49
|
+
constructor(message, hint) { super('ARGUMENT', message, hint); }
|
|
70
50
|
}
|
|
71
|
-
// ── Empty result ────────────────────────────────────────────────────────────
|
|
72
51
|
export class EmptyResultError extends CliError {
|
|
73
52
|
constructor(command, hint) {
|
|
74
53
|
super('EMPTY_RESULT', `${command} returned no data`, hint ?? 'The page structure may have changed, or you may need to log in');
|
|
75
|
-
this.name = 'EmptyResultError';
|
|
76
54
|
}
|
|
77
55
|
}
|
|
78
|
-
// ── Selector / DOM ──────────────────────────────────────────────────────────
|
|
79
56
|
export class SelectorError extends CliError {
|
|
80
57
|
constructor(selector, hint) {
|
|
81
58
|
super('SELECTOR', `Could not find element: ${selector}`, hint ?? 'The page UI may have changed. Please report this issue.');
|
|
82
|
-
this.name = 'SelectorError';
|
|
83
59
|
}
|
|
84
60
|
}
|
|
85
61
|
// ── Utilities ───────────────────────────────────────────────────────────
|
|
@@ -95,4 +71,11 @@ export const ERROR_ICONS = {
|
|
|
95
71
|
ARGUMENT: '❌',
|
|
96
72
|
EMPTY_RESULT: '📭',
|
|
97
73
|
SELECTOR: '🔍',
|
|
74
|
+
COMMAND_EXEC: '💥',
|
|
75
|
+
ADAPTER_LOAD: '📦',
|
|
76
|
+
NETWORK: '🌐',
|
|
77
|
+
API_ERROR: '🚫',
|
|
78
|
+
RATE_LIMITED: '⏳',
|
|
79
|
+
PAGE_CHANGED: '🔄',
|
|
80
|
+
CONFIG: '⚙️ ',
|
|
98
81
|
};
|
package/dist/execution.d.ts
CHANGED
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
* 5. Lazy-loading of TS modules from manifest
|
|
10
10
|
* 6. Lifecycle hooks (onBeforeExecute / onAfterExecute)
|
|
11
11
|
*/
|
|
12
|
-
import { type CliCommand, type Arg } from './registry.js';
|
|
13
|
-
type CommandArgs = Record<string, unknown>;
|
|
12
|
+
import { type CliCommand, type Arg, type CommandArgs } from './registry.js';
|
|
14
13
|
export declare function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: CommandArgs): CommandArgs;
|
|
15
14
|
export declare function executeCommand(cmd: CliCommand, rawKwargs: CommandArgs, debug?: boolean): Promise<unknown>;
|
|
16
|
-
export {};
|
package/dist/execution.js
CHANGED
|
@@ -12,10 +12,13 @@
|
|
|
12
12
|
import { Strategy, getRegistry, fullName } from './registry.js';
|
|
13
13
|
import { pathToFileURL } from 'node:url';
|
|
14
14
|
import { executePipeline } from './pipeline/index.js';
|
|
15
|
-
import { AdapterLoadError, ArgumentError, CommandExecutionError, getErrorMessage } from './errors.js';
|
|
15
|
+
import { AdapterLoadError, ArgumentError, BrowserConnectError, CommandExecutionError, getErrorMessage } from './errors.js';
|
|
16
16
|
import { shouldUseBrowserSession } from './capabilityRouting.js';
|
|
17
17
|
import { getBrowserFactory, browserSession, runWithTimeout, DEFAULT_BROWSER_COMMAND_TIMEOUT } from './runtime.js';
|
|
18
18
|
import { emitHook } from './hooks.js';
|
|
19
|
+
import { checkDaemonStatus } from './browser/discover.js';
|
|
20
|
+
import { PKG_VERSION } from './version.js';
|
|
21
|
+
import chalk from 'chalk';
|
|
19
22
|
const _loadedModules = new Set();
|
|
20
23
|
export function coerceAndValidateArgs(cmdArgs, kwargs) {
|
|
21
24
|
const result = { ...kwargs };
|
|
@@ -126,6 +129,23 @@ export async function executeCommand(cmd, rawKwargs, debug = false) {
|
|
|
126
129
|
let result;
|
|
127
130
|
try {
|
|
128
131
|
if (shouldUseBrowserSession(cmd)) {
|
|
132
|
+
// ── Fail-fast: only when daemon is UP but extension is not connected ──
|
|
133
|
+
// If daemon is not running, let browserSession() handle auto-start as usual.
|
|
134
|
+
// We only short-circuit when the daemon confirms the extension is missing —
|
|
135
|
+
// that's a clear setup gap, not a transient startup state.
|
|
136
|
+
// Use a short timeout: localhost responds in <50ms when running.
|
|
137
|
+
// 300ms avoids a full 2s wait on cold-start (daemon not yet running).
|
|
138
|
+
const status = await checkDaemonStatus({ timeout: 300 });
|
|
139
|
+
if (status.running && !status.extensionConnected) {
|
|
140
|
+
throw new BrowserConnectError('Browser Bridge extension not connected', 'Install the Browser Bridge:\n' +
|
|
141
|
+
' 1. Download: https://github.com/jackwener/opencli/releases\n' +
|
|
142
|
+
' 2. chrome://extensions → Developer Mode → Load unpacked\n' +
|
|
143
|
+
' Then run: opencli doctor');
|
|
144
|
+
}
|
|
145
|
+
// ── Version mismatch: warn but don't block ──
|
|
146
|
+
if (status.extensionVersion && status.extensionVersion !== PKG_VERSION) {
|
|
147
|
+
process.stderr.write(chalk.yellow(`⚠ Extension v${status.extensionVersion} ≠ CLI v${PKG_VERSION} — consider updating the extension.\n`));
|
|
148
|
+
}
|
|
129
149
|
ensureRequiredEnv(cmd);
|
|
130
150
|
const BrowserFactory = getBrowserFactory();
|
|
131
151
|
result = await browserSession(BrowserFactory, async (page) => {
|
package/dist/external-clis.yaml
CHANGED
|
@@ -14,14 +14,6 @@
|
|
|
14
14
|
install:
|
|
15
15
|
mac: "brew install --cask obsidian"
|
|
16
16
|
|
|
17
|
-
- name: readwise
|
|
18
|
-
binary: readwise
|
|
19
|
-
description: "Readwise & Reader CLI — highlights, annotations, reading list"
|
|
20
|
-
homepage: "https://github.com/readwiseio/readwise-cli"
|
|
21
|
-
tags: [reading, highlights]
|
|
22
|
-
install:
|
|
23
|
-
default: "npm install -g @readwiseio/readwise-cli"
|
|
24
|
-
|
|
25
17
|
- name: docker
|
|
26
18
|
binary: docker
|
|
27
19
|
description: "Docker command-line interface"
|
|
@@ -29,12 +21,3 @@
|
|
|
29
21
|
tags: [docker, containers, devops]
|
|
30
22
|
install:
|
|
31
23
|
mac: "brew install --cask docker"
|
|
32
|
-
|
|
33
|
-
- name: gws
|
|
34
|
-
binary: gws
|
|
35
|
-
description: "Google Workspace CLI — Docs, Sheets, Drive, Gmail, Calendar"
|
|
36
|
-
homepage: "https://github.com/nicholasgasior/gws"
|
|
37
|
-
tags: [google, docs, sheets, drive, workspace]
|
|
38
|
-
install:
|
|
39
|
-
mac: "brew install gws"
|
|
40
|
-
default: "npm install -g @nicholasgasior/gws"
|
package/dist/hooks.js
CHANGED
|
@@ -15,6 +15,8 @@ const _hooks = globalThis.__opencli_hooks__ ??= new Map();
|
|
|
15
15
|
// ── Registration API (used by plugins) ─────────────────────────────────────
|
|
16
16
|
function addHook(name, fn) {
|
|
17
17
|
const list = _hooks.get(name) ?? [];
|
|
18
|
+
if (list.includes(fn))
|
|
19
|
+
return;
|
|
18
20
|
list.push(fn);
|
|
19
21
|
_hooks.set(name, list);
|
|
20
22
|
}
|
package/dist/main.js
CHANGED
|
@@ -19,12 +19,17 @@ import { discoverClis, discoverPlugins } from './discovery.js';
|
|
|
19
19
|
import { getCompletions } from './completion.js';
|
|
20
20
|
import { runCli } from './cli.js';
|
|
21
21
|
import { emitHook } from './hooks.js';
|
|
22
|
+
import { registerUpdateNoticeOnExit, checkForUpdateBackground } from './update-check.js';
|
|
22
23
|
const __filename = fileURLToPath(import.meta.url);
|
|
23
24
|
const __dirname = path.dirname(__filename);
|
|
24
25
|
const BUILTIN_CLIS = path.resolve(__dirname, 'clis');
|
|
25
26
|
const USER_CLIS = path.join(os.homedir(), '.opencli', 'clis');
|
|
26
27
|
await discoverClis(BUILTIN_CLIS, USER_CLIS);
|
|
27
28
|
await discoverPlugins();
|
|
29
|
+
// Register exit hook: notice appears after command output (same as npm/gh/yarn)
|
|
30
|
+
registerUpdateNoticeOnExit();
|
|
31
|
+
// Kick off background fetch for next run (non-blocking)
|
|
32
|
+
checkForUpdateBackground();
|
|
28
33
|
// ── Fast-path: handle --get-completions before commander parses ─────────
|
|
29
34
|
// Usage: opencli --get-completions --cursor <N> [word1 word2 ...]
|
|
30
35
|
const getCompIdx = process.argv.indexOf('--get-completions');
|
package/dist/output.js
CHANGED
|
@@ -5,7 +5,11 @@ import chalk from 'chalk';
|
|
|
5
5
|
import Table from 'cli-table3';
|
|
6
6
|
import yaml from 'js-yaml';
|
|
7
7
|
function normalizeRows(data) {
|
|
8
|
-
|
|
8
|
+
if (Array.isArray(data))
|
|
9
|
+
return data;
|
|
10
|
+
if (data && typeof data === 'object')
|
|
11
|
+
return [data];
|
|
12
|
+
return [{ value: data }];
|
|
9
13
|
}
|
|
10
14
|
function resolveColumns(rows, opts) {
|
|
11
15
|
return opts.columns ?? Object.keys(rows[0] ?? {});
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
import { getStep } from './registry.js';
|
|
5
5
|
import { log } from '../logger.js';
|
|
6
6
|
import { ConfigError } from '../errors.js';
|
|
7
|
-
|
|
8
|
-
const BROWSER_STEPS = new Set(['navigate', 'evaluate', 'click', 'type', 'press', 'wait', 'snapshot']);
|
|
7
|
+
import { BROWSER_ONLY_STEPS } from '../capabilityRouting.js';
|
|
9
8
|
export async function executePipeline(page, pipeline, ctx = {}) {
|
|
10
9
|
const args = ctx.args ?? {};
|
|
11
10
|
const debug = ctx.debug ?? false;
|
|
@@ -33,7 +32,7 @@ export async function executePipeline(page, pipeline, ctx = {}) {
|
|
|
33
32
|
}
|
|
34
33
|
catch (err) {
|
|
35
34
|
// Attempt cleanup: close automation window on pipeline failure
|
|
36
|
-
if (page
|
|
35
|
+
if (page?.closeWindow) {
|
|
37
36
|
try {
|
|
38
37
|
await page.closeWindow();
|
|
39
38
|
}
|
|
@@ -44,7 +43,7 @@ export async function executePipeline(page, pipeline, ctx = {}) {
|
|
|
44
43
|
return data;
|
|
45
44
|
}
|
|
46
45
|
async function executeStepWithRetry(handler, page, params, data, args, op, configRetries) {
|
|
47
|
-
const maxRetries = configRetries ?? (
|
|
46
|
+
const maxRetries = configRetries ?? (BROWSER_ONLY_STEPS.has(op) ? 2 : 0);
|
|
48
47
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
49
48
|
try {
|
|
50
49
|
return await handler(page, params, data, args);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin manifest: reads and validates opencli-plugin.json files.
|
|
3
|
+
*
|
|
4
|
+
* Supports two modes:
|
|
5
|
+
* 1. Single plugin: repo root IS the plugin directory.
|
|
6
|
+
* 2. Monorepo: repo contains multiple plugins declared in `plugins` field.
|
|
7
|
+
*/
|
|
8
|
+
export interface SubPluginEntry {
|
|
9
|
+
/** Relative path from repo root to the sub-plugin directory. */
|
|
10
|
+
path: string;
|
|
11
|
+
version?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
/** Semver range for opencli compatibility (overrides top-level). */
|
|
14
|
+
opencli?: string;
|
|
15
|
+
/** When true, this sub-plugin is skipped during install. */
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface PluginManifest {
|
|
19
|
+
/** Plugin name (single-plugin mode). */
|
|
20
|
+
name?: string;
|
|
21
|
+
/** Semantic version of the plugin (single-plugin mode). */
|
|
22
|
+
version?: string;
|
|
23
|
+
/** Semver range for opencli compatibility, e.g. ">=1.0.0". */
|
|
24
|
+
opencli?: string;
|
|
25
|
+
/** Human-readable description. */
|
|
26
|
+
description?: string;
|
|
27
|
+
/** Monorepo sub-plugins. Key = logical plugin name. */
|
|
28
|
+
plugins?: Record<string, SubPluginEntry>;
|
|
29
|
+
}
|
|
30
|
+
export declare const MANIFEST_FILENAME = "opencli-plugin.json";
|
|
31
|
+
/**
|
|
32
|
+
* Read and parse opencli-plugin.json from a directory.
|
|
33
|
+
* Returns null if the file does not exist or is unparseable.
|
|
34
|
+
*/
|
|
35
|
+
export declare function readPluginManifest(dir: string): PluginManifest | null;
|
|
36
|
+
/** Returns true when the manifest declares a monorepo (has `plugins` field). */
|
|
37
|
+
export declare function isMonorepo(manifest: PluginManifest): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Get the list of enabled sub-plugins from a monorepo manifest.
|
|
40
|
+
* Returns entries sorted by key name.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getEnabledPlugins(manifest: PluginManifest): Array<{
|
|
43
|
+
name: string;
|
|
44
|
+
entry: SubPluginEntry;
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Check if the current opencli version satisfies a semver range string.
|
|
48
|
+
*
|
|
49
|
+
* Supports a simplified subset of semver ranges:
|
|
50
|
+
* ">=1.0.0" – greater than or equal
|
|
51
|
+
* "<=1.5.0" – less than or equal
|
|
52
|
+
* ">1.0.0" – strictly greater
|
|
53
|
+
* "<2.0.0" – strictly less
|
|
54
|
+
* "^1.2.0" – compatible (>=1.2.0 and <2.0.0)
|
|
55
|
+
* "~1.2.0" – patch-level (>=1.2.0 and <1.3.0)
|
|
56
|
+
* "1.2.0" – exact match
|
|
57
|
+
* ">=1.0.0 <2.0.0" – multiple constraints (space-separated, all must match)
|
|
58
|
+
*
|
|
59
|
+
* Returns true if compatible, false if not, and true for empty/undefined
|
|
60
|
+
* ranges (no constraint = always compatible).
|
|
61
|
+
*/
|
|
62
|
+
export declare function checkCompatibility(range: string | undefined): boolean;
|
|
63
|
+
/** Parse a version string ("1.2.3") into [major, minor, patch]. */
|
|
64
|
+
export declare function parseVersion(version: string): [number, number, number] | null;
|
|
65
|
+
/**
|
|
66
|
+
* Check if a version string satisfies a range expression.
|
|
67
|
+
* Space-separated constraints are ANDed together.
|
|
68
|
+
*/
|
|
69
|
+
export declare function satisfiesRange(versionStr: string, range: string): boolean;
|
|
70
|
+
export { readPluginManifest as _readPluginManifest, isMonorepo as _isMonorepo, getEnabledPlugins as _getEnabledPlugins, checkCompatibility as _checkCompatibility, parseVersion as _parseVersion, satisfiesRange as _satisfiesRange, };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin manifest: reads and validates opencli-plugin.json files.
|
|
3
|
+
*
|
|
4
|
+
* Supports two modes:
|
|
5
|
+
* 1. Single plugin: repo root IS the plugin directory.
|
|
6
|
+
* 2. Monorepo: repo contains multiple plugins declared in `plugins` field.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { PKG_VERSION } from './version.js';
|
|
11
|
+
export const MANIFEST_FILENAME = 'opencli-plugin.json';
|
|
12
|
+
// ── Read / Validate ─────────────────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* Read and parse opencli-plugin.json from a directory.
|
|
15
|
+
* Returns null if the file does not exist or is unparseable.
|
|
16
|
+
*/
|
|
17
|
+
export function readPluginManifest(dir) {
|
|
18
|
+
const manifestPath = path.join(dir, MANIFEST_FILENAME);
|
|
19
|
+
try {
|
|
20
|
+
const raw = fs.readFileSync(manifestPath, 'utf-8');
|
|
21
|
+
const parsed = JSON.parse(raw);
|
|
22
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Returns true when the manifest declares a monorepo (has `plugins` field). */
|
|
32
|
+
export function isMonorepo(manifest) {
|
|
33
|
+
return (manifest.plugins !== undefined &&
|
|
34
|
+
manifest.plugins !== null &&
|
|
35
|
+
typeof manifest.plugins === 'object' &&
|
|
36
|
+
Object.keys(manifest.plugins).length > 0);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get the list of enabled sub-plugins from a monorepo manifest.
|
|
40
|
+
* Returns entries sorted by key name.
|
|
41
|
+
*/
|
|
42
|
+
export function getEnabledPlugins(manifest) {
|
|
43
|
+
if (!manifest.plugins)
|
|
44
|
+
return [];
|
|
45
|
+
return Object.entries(manifest.plugins)
|
|
46
|
+
.filter(([, entry]) => !entry.disabled)
|
|
47
|
+
.map(([name, entry]) => ({ name, entry }))
|
|
48
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
49
|
+
}
|
|
50
|
+
// ── Version compatibility ───────────────────────────────────────────────────
|
|
51
|
+
/**
|
|
52
|
+
* Check if the current opencli version satisfies a semver range string.
|
|
53
|
+
*
|
|
54
|
+
* Supports a simplified subset of semver ranges:
|
|
55
|
+
* ">=1.0.0" – greater than or equal
|
|
56
|
+
* "<=1.5.0" – less than or equal
|
|
57
|
+
* ">1.0.0" – strictly greater
|
|
58
|
+
* "<2.0.0" – strictly less
|
|
59
|
+
* "^1.2.0" – compatible (>=1.2.0 and <2.0.0)
|
|
60
|
+
* "~1.2.0" – patch-level (>=1.2.0 and <1.3.0)
|
|
61
|
+
* "1.2.0" – exact match
|
|
62
|
+
* ">=1.0.0 <2.0.0" – multiple constraints (space-separated, all must match)
|
|
63
|
+
*
|
|
64
|
+
* Returns true if compatible, false if not, and true for empty/undefined
|
|
65
|
+
* ranges (no constraint = always compatible).
|
|
66
|
+
*/
|
|
67
|
+
export function checkCompatibility(range) {
|
|
68
|
+
if (!range || range.trim() === '')
|
|
69
|
+
return true;
|
|
70
|
+
return satisfiesRange(PKG_VERSION, range);
|
|
71
|
+
}
|
|
72
|
+
/** Parse a version string ("1.2.3") into [major, minor, patch]. */
|
|
73
|
+
export function parseVersion(version) {
|
|
74
|
+
const match = version.trim().match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
75
|
+
if (!match)
|
|
76
|
+
return null;
|
|
77
|
+
return [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10)];
|
|
78
|
+
}
|
|
79
|
+
/** Compare two version tuples: -1 if a<b, 0 if equal, 1 if a>b. */
|
|
80
|
+
function compareVersions(a, b) {
|
|
81
|
+
for (let i = 0; i < 3; i++) {
|
|
82
|
+
if (a[i] < b[i])
|
|
83
|
+
return -1;
|
|
84
|
+
if (a[i] > b[i])
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
/** Check if a version satisfies a single constraint like ">=1.2.0". */
|
|
90
|
+
function satisfiesSingleConstraint(version, constraint) {
|
|
91
|
+
const trimmed = constraint.trim();
|
|
92
|
+
if (!trimmed)
|
|
93
|
+
return true;
|
|
94
|
+
// ^1.2.0 → >=1.2.0 <2.0.0
|
|
95
|
+
if (trimmed.startsWith('^')) {
|
|
96
|
+
const target = parseVersion(trimmed.slice(1));
|
|
97
|
+
if (!target)
|
|
98
|
+
return true;
|
|
99
|
+
const upper = [target[0] + 1, 0, 0];
|
|
100
|
+
return compareVersions(version, target) >= 0 && compareVersions(version, upper) < 0;
|
|
101
|
+
}
|
|
102
|
+
// ~1.2.0 → >=1.2.0 <1.3.0
|
|
103
|
+
if (trimmed.startsWith('~')) {
|
|
104
|
+
const target = parseVersion(trimmed.slice(1));
|
|
105
|
+
if (!target)
|
|
106
|
+
return true;
|
|
107
|
+
const upper = [target[0], target[1] + 1, 0];
|
|
108
|
+
return compareVersions(version, target) >= 0 && compareVersions(version, upper) < 0;
|
|
109
|
+
}
|
|
110
|
+
// >=, <=, >, <, =
|
|
111
|
+
if (trimmed.startsWith('>=')) {
|
|
112
|
+
const target = parseVersion(trimmed.slice(2));
|
|
113
|
+
if (!target)
|
|
114
|
+
return true;
|
|
115
|
+
return compareVersions(version, target) >= 0;
|
|
116
|
+
}
|
|
117
|
+
if (trimmed.startsWith('<=')) {
|
|
118
|
+
const target = parseVersion(trimmed.slice(2));
|
|
119
|
+
if (!target)
|
|
120
|
+
return true;
|
|
121
|
+
return compareVersions(version, target) <= 0;
|
|
122
|
+
}
|
|
123
|
+
if (trimmed.startsWith('>')) {
|
|
124
|
+
const target = parseVersion(trimmed.slice(1));
|
|
125
|
+
if (!target)
|
|
126
|
+
return true;
|
|
127
|
+
return compareVersions(version, target) > 0;
|
|
128
|
+
}
|
|
129
|
+
if (trimmed.startsWith('<')) {
|
|
130
|
+
const target = parseVersion(trimmed.slice(1));
|
|
131
|
+
if (!target)
|
|
132
|
+
return true;
|
|
133
|
+
return compareVersions(version, target) < 0;
|
|
134
|
+
}
|
|
135
|
+
if (trimmed.startsWith('=')) {
|
|
136
|
+
const target = parseVersion(trimmed.slice(1));
|
|
137
|
+
if (!target)
|
|
138
|
+
return true;
|
|
139
|
+
return compareVersions(version, target) === 0;
|
|
140
|
+
}
|
|
141
|
+
// Exact match
|
|
142
|
+
const target = parseVersion(trimmed);
|
|
143
|
+
if (!target)
|
|
144
|
+
return true;
|
|
145
|
+
return compareVersions(version, target) === 0;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Check if a version string satisfies a range expression.
|
|
149
|
+
* Space-separated constraints are ANDed together.
|
|
150
|
+
*/
|
|
151
|
+
export function satisfiesRange(versionStr, range) {
|
|
152
|
+
const version = parseVersion(versionStr);
|
|
153
|
+
if (!version)
|
|
154
|
+
return true; // Can't parse our own version → assume ok
|
|
155
|
+
// Split on whitespace for multi-constraint ranges (e.g. ">=1.0.0 <2.0.0")
|
|
156
|
+
const constraints = range.trim().split(/\s+/);
|
|
157
|
+
return constraints.every((c) => satisfiesSingleConstraint(version, c));
|
|
158
|
+
}
|
|
159
|
+
// ── Exports for testing ─────────────────────────────────────────────────────
|
|
160
|
+
export { readPluginManifest as _readPluginManifest, isMonorepo as _isMonorepo, getEnabledPlugins as _getEnabledPlugins, checkCompatibility as _checkCompatibility, parseVersion as _parseVersion, satisfiesRange as _satisfiesRange, };
|