@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
|
@@ -0,0 +1,206 @@
|
|
|
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
|
+
|
|
9
|
+
import * as fs from 'node:fs';
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
import { PKG_VERSION } from './version.js';
|
|
12
|
+
|
|
13
|
+
// ── Types ───────────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
export interface SubPluginEntry {
|
|
16
|
+
/** Relative path from repo root to the sub-plugin directory. */
|
|
17
|
+
path: string;
|
|
18
|
+
version?: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
/** Semver range for opencli compatibility (overrides top-level). */
|
|
21
|
+
opencli?: string;
|
|
22
|
+
/** When true, this sub-plugin is skipped during install. */
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface PluginManifest {
|
|
27
|
+
/** Plugin name (single-plugin mode). */
|
|
28
|
+
name?: string;
|
|
29
|
+
/** Semantic version of the plugin (single-plugin mode). */
|
|
30
|
+
version?: string;
|
|
31
|
+
/** Semver range for opencli compatibility, e.g. ">=1.0.0". */
|
|
32
|
+
opencli?: string;
|
|
33
|
+
/** Human-readable description. */
|
|
34
|
+
description?: string;
|
|
35
|
+
/** Monorepo sub-plugins. Key = logical plugin name. */
|
|
36
|
+
plugins?: Record<string, SubPluginEntry>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const MANIFEST_FILENAME = 'opencli-plugin.json';
|
|
40
|
+
|
|
41
|
+
// ── Read / Validate ─────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Read and parse opencli-plugin.json from a directory.
|
|
45
|
+
* Returns null if the file does not exist or is unparseable.
|
|
46
|
+
*/
|
|
47
|
+
export function readPluginManifest(dir: string): PluginManifest | null {
|
|
48
|
+
const manifestPath = path.join(dir, MANIFEST_FILENAME);
|
|
49
|
+
try {
|
|
50
|
+
const raw = fs.readFileSync(manifestPath, 'utf-8');
|
|
51
|
+
const parsed = JSON.parse(raw);
|
|
52
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return parsed as PluginManifest;
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Returns true when the manifest declares a monorepo (has `plugins` field). */
|
|
62
|
+
export function isMonorepo(manifest: PluginManifest): boolean {
|
|
63
|
+
return (
|
|
64
|
+
manifest.plugins !== undefined &&
|
|
65
|
+
manifest.plugins !== null &&
|
|
66
|
+
typeof manifest.plugins === 'object' &&
|
|
67
|
+
Object.keys(manifest.plugins).length > 0
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get the list of enabled sub-plugins from a monorepo manifest.
|
|
73
|
+
* Returns entries sorted by key name.
|
|
74
|
+
*/
|
|
75
|
+
export function getEnabledPlugins(
|
|
76
|
+
manifest: PluginManifest,
|
|
77
|
+
): Array<{ name: string; entry: SubPluginEntry }> {
|
|
78
|
+
if (!manifest.plugins) return [];
|
|
79
|
+
return Object.entries(manifest.plugins)
|
|
80
|
+
.filter(([, entry]) => !entry.disabled)
|
|
81
|
+
.map(([name, entry]) => ({ name, entry }))
|
|
82
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── Version compatibility ───────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check if the current opencli version satisfies a semver range string.
|
|
89
|
+
*
|
|
90
|
+
* Supports a simplified subset of semver ranges:
|
|
91
|
+
* ">=1.0.0" – greater than or equal
|
|
92
|
+
* "<=1.5.0" – less than or equal
|
|
93
|
+
* ">1.0.0" – strictly greater
|
|
94
|
+
* "<2.0.0" – strictly less
|
|
95
|
+
* "^1.2.0" – compatible (>=1.2.0 and <2.0.0)
|
|
96
|
+
* "~1.2.0" – patch-level (>=1.2.0 and <1.3.0)
|
|
97
|
+
* "1.2.0" – exact match
|
|
98
|
+
* ">=1.0.0 <2.0.0" – multiple constraints (space-separated, all must match)
|
|
99
|
+
*
|
|
100
|
+
* Returns true if compatible, false if not, and true for empty/undefined
|
|
101
|
+
* ranges (no constraint = always compatible).
|
|
102
|
+
*/
|
|
103
|
+
export function checkCompatibility(range: string | undefined): boolean {
|
|
104
|
+
if (!range || range.trim() === '') return true;
|
|
105
|
+
return satisfiesRange(PKG_VERSION, range);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Parse a version string ("1.2.3") into [major, minor, patch]. */
|
|
109
|
+
export function parseVersion(version: string): [number, number, number] | null {
|
|
110
|
+
const match = version.trim().match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
111
|
+
if (!match) return null;
|
|
112
|
+
return [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10)];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Compare two version tuples: -1 if a<b, 0 if equal, 1 if a>b. */
|
|
116
|
+
function compareVersions(
|
|
117
|
+
a: [number, number, number],
|
|
118
|
+
b: [number, number, number],
|
|
119
|
+
): -1 | 0 | 1 {
|
|
120
|
+
for (let i = 0; i < 3; i++) {
|
|
121
|
+
if (a[i] < b[i]) return -1;
|
|
122
|
+
if (a[i] > b[i]) return 1;
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Check if a version satisfies a single constraint like ">=1.2.0". */
|
|
128
|
+
function satisfiesSingleConstraint(
|
|
129
|
+
version: [number, number, number],
|
|
130
|
+
constraint: string,
|
|
131
|
+
): boolean {
|
|
132
|
+
const trimmed = constraint.trim();
|
|
133
|
+
if (!trimmed) return true;
|
|
134
|
+
|
|
135
|
+
// ^1.2.0 → >=1.2.0 <2.0.0
|
|
136
|
+
if (trimmed.startsWith('^')) {
|
|
137
|
+
const target = parseVersion(trimmed.slice(1));
|
|
138
|
+
if (!target) return true;
|
|
139
|
+
const upper: [number, number, number] = [target[0] + 1, 0, 0];
|
|
140
|
+
return compareVersions(version, target) >= 0 && compareVersions(version, upper) < 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ~1.2.0 → >=1.2.0 <1.3.0
|
|
144
|
+
if (trimmed.startsWith('~')) {
|
|
145
|
+
const target = parseVersion(trimmed.slice(1));
|
|
146
|
+
if (!target) return true;
|
|
147
|
+
const upper: [number, number, number] = [target[0], target[1] + 1, 0];
|
|
148
|
+
return compareVersions(version, target) >= 0 && compareVersions(version, upper) < 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// >=, <=, >, <, =
|
|
152
|
+
if (trimmed.startsWith('>=')) {
|
|
153
|
+
const target = parseVersion(trimmed.slice(2));
|
|
154
|
+
if (!target) return true;
|
|
155
|
+
return compareVersions(version, target) >= 0;
|
|
156
|
+
}
|
|
157
|
+
if (trimmed.startsWith('<=')) {
|
|
158
|
+
const target = parseVersion(trimmed.slice(2));
|
|
159
|
+
if (!target) return true;
|
|
160
|
+
return compareVersions(version, target) <= 0;
|
|
161
|
+
}
|
|
162
|
+
if (trimmed.startsWith('>')) {
|
|
163
|
+
const target = parseVersion(trimmed.slice(1));
|
|
164
|
+
if (!target) return true;
|
|
165
|
+
return compareVersions(version, target) > 0;
|
|
166
|
+
}
|
|
167
|
+
if (trimmed.startsWith('<')) {
|
|
168
|
+
const target = parseVersion(trimmed.slice(1));
|
|
169
|
+
if (!target) return true;
|
|
170
|
+
return compareVersions(version, target) < 0;
|
|
171
|
+
}
|
|
172
|
+
if (trimmed.startsWith('=')) {
|
|
173
|
+
const target = parseVersion(trimmed.slice(1));
|
|
174
|
+
if (!target) return true;
|
|
175
|
+
return compareVersions(version, target) === 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Exact match
|
|
179
|
+
const target = parseVersion(trimmed);
|
|
180
|
+
if (!target) return true;
|
|
181
|
+
return compareVersions(version, target) === 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Check if a version string satisfies a range expression.
|
|
186
|
+
* Space-separated constraints are ANDed together.
|
|
187
|
+
*/
|
|
188
|
+
export function satisfiesRange(versionStr: string, range: string): boolean {
|
|
189
|
+
const version = parseVersion(versionStr);
|
|
190
|
+
if (!version) return true; // Can't parse our own version → assume ok
|
|
191
|
+
|
|
192
|
+
// Split on whitespace for multi-constraint ranges (e.g. ">=1.0.0 <2.0.0")
|
|
193
|
+
const constraints = range.trim().split(/\s+/);
|
|
194
|
+
return constraints.every((c) => satisfiesSingleConstraint(version, c));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── Exports for testing ─────────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
export {
|
|
200
|
+
readPluginManifest as _readPluginManifest,
|
|
201
|
+
isMonorepo as _isMonorepo,
|
|
202
|
+
getEnabledPlugins as _getEnabledPlugins,
|
|
203
|
+
checkCompatibility as _checkCompatibility,
|
|
204
|
+
parseVersion as _parseVersion,
|
|
205
|
+
satisfiesRange as _satisfiesRange,
|
|
206
|
+
};
|
package/src/plugin.test.ts
CHANGED
|
@@ -10,9 +10,16 @@ import { PLUGINS_DIR } from './discovery.js';
|
|
|
10
10
|
import type { LockEntry } from './plugin.js';
|
|
11
11
|
import * as pluginModule from './plugin.js';
|
|
12
12
|
|
|
13
|
+
const { mockExecFileSync, mockExecSync } = vi.hoisted(() => ({
|
|
14
|
+
mockExecFileSync: vi.fn(),
|
|
15
|
+
mockExecSync: vi.fn(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
13
18
|
const {
|
|
14
19
|
LOCK_FILE,
|
|
15
20
|
_getCommitHash,
|
|
21
|
+
_installDependencies,
|
|
22
|
+
_postInstallMonorepoLifecycle,
|
|
16
23
|
listPlugins,
|
|
17
24
|
_readLockFile,
|
|
18
25
|
_resolveEsbuildBin,
|
|
@@ -22,6 +29,8 @@ const {
|
|
|
22
29
|
_updateAllPlugins,
|
|
23
30
|
_validatePluginStructure,
|
|
24
31
|
_writeLockFile,
|
|
32
|
+
_isSymlinkSync,
|
|
33
|
+
_getMonoreposDir,
|
|
25
34
|
} = pluginModule;
|
|
26
35
|
|
|
27
36
|
describe('parseSource', () => {
|
|
@@ -283,7 +292,7 @@ describe('updatePlugin', () => {
|
|
|
283
292
|
|
|
284
293
|
vi.mock('node:child_process', () => {
|
|
285
294
|
return {
|
|
286
|
-
execFileSync:
|
|
295
|
+
execFileSync: mockExecFileSync.mockImplementation((_cmd, args, opts) => {
|
|
287
296
|
if (Array.isArray(args) && args[0] === 'rev-parse' && args[1] === 'HEAD') {
|
|
288
297
|
if (opts?.cwd === os.tmpdir()) {
|
|
289
298
|
throw new Error('not a git repository');
|
|
@@ -295,10 +304,63 @@ vi.mock('node:child_process', () => {
|
|
|
295
304
|
}
|
|
296
305
|
return '';
|
|
297
306
|
}),
|
|
298
|
-
execSync:
|
|
307
|
+
execSync: mockExecSync.mockImplementation(() => ''),
|
|
299
308
|
};
|
|
300
309
|
});
|
|
301
310
|
|
|
311
|
+
describe('installDependencies', () => {
|
|
312
|
+
beforeEach(() => {
|
|
313
|
+
mockExecFileSync.mockClear();
|
|
314
|
+
mockExecSync.mockClear();
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('throws when npm install fails', () => {
|
|
318
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-plugin-b-'));
|
|
319
|
+
const failingDir = path.join(tmpDir, 'plugin-b');
|
|
320
|
+
fs.mkdirSync(failingDir, { recursive: true });
|
|
321
|
+
fs.writeFileSync(path.join(failingDir, 'package.json'), JSON.stringify({ name: 'plugin-b' }));
|
|
322
|
+
|
|
323
|
+
expect(() => _installDependencies(failingDir)).toThrow('npm install failed');
|
|
324
|
+
|
|
325
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
describe('postInstallMonorepoLifecycle', () => {
|
|
330
|
+
let repoDir: string;
|
|
331
|
+
let subDir: string;
|
|
332
|
+
|
|
333
|
+
beforeEach(() => {
|
|
334
|
+
mockExecFileSync.mockClear();
|
|
335
|
+
mockExecSync.mockClear();
|
|
336
|
+
repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-monorepo-'));
|
|
337
|
+
subDir = path.join(repoDir, 'packages', 'alpha');
|
|
338
|
+
fs.mkdirSync(subDir, { recursive: true });
|
|
339
|
+
fs.writeFileSync(path.join(repoDir, 'package.json'), JSON.stringify({
|
|
340
|
+
name: 'opencli-plugins',
|
|
341
|
+
private: true,
|
|
342
|
+
workspaces: ['packages/*'],
|
|
343
|
+
}));
|
|
344
|
+
fs.writeFileSync(path.join(subDir, 'hello.yaml'), 'site: test\nname: hello\n');
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
afterEach(() => {
|
|
348
|
+
fs.rmSync(repoDir, { recursive: true, force: true });
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('installs dependencies once at the monorepo root, not in each sub-plugin', () => {
|
|
352
|
+
_postInstallMonorepoLifecycle(repoDir, [subDir]);
|
|
353
|
+
|
|
354
|
+
const npmCalls = mockExecFileSync.mock.calls.filter(
|
|
355
|
+
([cmd, args]) => cmd === 'npm' && Array.isArray(args) && args[0] === 'install',
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
expect(npmCalls).toHaveLength(1);
|
|
359
|
+
expect(npmCalls[0][2]).toMatchObject({ cwd: repoDir });
|
|
360
|
+
expect(npmCalls.some(([, , opts]) => opts?.cwd === subDir)).toBe(false);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
302
364
|
describe('updateAllPlugins', () => {
|
|
303
365
|
const testDirA = path.join(PLUGINS_DIR, 'plugin-a');
|
|
304
366
|
const testDirB = path.join(PLUGINS_DIR, 'plugin-b');
|
|
@@ -338,3 +400,185 @@ describe('updateAllPlugins', () => {
|
|
|
338
400
|
expect(resC!.success).toBe(true);
|
|
339
401
|
});
|
|
340
402
|
});
|
|
403
|
+
|
|
404
|
+
// ── Monorepo-specific tests ─────────────────────────────────────────────────
|
|
405
|
+
|
|
406
|
+
describe('parseSource with monorepo subplugin', () => {
|
|
407
|
+
it('parses github:user/repo/subplugin format', () => {
|
|
408
|
+
const result = _parseSource('github:ByteYue/opencli-plugins/polymarket');
|
|
409
|
+
expect(result).toEqual({
|
|
410
|
+
cloneUrl: 'https://github.com/ByteYue/opencli-plugins.git',
|
|
411
|
+
name: 'opencli-plugins',
|
|
412
|
+
subPlugin: 'polymarket',
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('strips opencli-plugin- prefix from repo name in subplugin format', () => {
|
|
417
|
+
const result = _parseSource('github:user/opencli-plugin-collection/defi');
|
|
418
|
+
expect(result!.name).toBe('collection');
|
|
419
|
+
expect(result!.subPlugin).toBe('defi');
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('still parses github:user/repo without subplugin', () => {
|
|
423
|
+
const result = _parseSource('github:user/my-repo');
|
|
424
|
+
expect(result).toEqual({
|
|
425
|
+
cloneUrl: 'https://github.com/user/my-repo.git',
|
|
426
|
+
name: 'my-repo',
|
|
427
|
+
});
|
|
428
|
+
expect(result!.subPlugin).toBeUndefined();
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
describe('isSymlinkSync', () => {
|
|
433
|
+
let tmpDir: string;
|
|
434
|
+
|
|
435
|
+
beforeEach(() => {
|
|
436
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-symlink-test-'));
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
afterEach(() => {
|
|
440
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('returns false for a regular directory', () => {
|
|
444
|
+
const dir = path.join(tmpDir, 'regular');
|
|
445
|
+
fs.mkdirSync(dir);
|
|
446
|
+
expect(_isSymlinkSync(dir)).toBe(false);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('returns true for a symlink', () => {
|
|
450
|
+
const target = path.join(tmpDir, 'target');
|
|
451
|
+
const link = path.join(tmpDir, 'link');
|
|
452
|
+
fs.mkdirSync(target);
|
|
453
|
+
fs.symlinkSync(target, link, 'dir');
|
|
454
|
+
expect(_isSymlinkSync(link)).toBe(true);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it('returns false for non-existent path', () => {
|
|
458
|
+
expect(_isSymlinkSync(path.join(tmpDir, 'nope'))).toBe(false);
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
describe('monorepo uninstall with symlink', () => {
|
|
463
|
+
let tmpDir: string;
|
|
464
|
+
let pluginDir: string;
|
|
465
|
+
let monoDir: string;
|
|
466
|
+
|
|
467
|
+
beforeEach(() => {
|
|
468
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-mono-uninstall-'));
|
|
469
|
+
// We need to use the real PLUGINS_DIR for uninstallPlugin() to work
|
|
470
|
+
pluginDir = path.join(PLUGINS_DIR, '__test-mono-sub__');
|
|
471
|
+
monoDir = path.join(_getMonoreposDir(), '__test-mono__');
|
|
472
|
+
|
|
473
|
+
// Set up monorepo structure
|
|
474
|
+
const subDir = path.join(monoDir, 'packages', 'sub');
|
|
475
|
+
fs.mkdirSync(subDir, { recursive: true });
|
|
476
|
+
fs.writeFileSync(path.join(subDir, 'cmd.yaml'), 'site: test');
|
|
477
|
+
|
|
478
|
+
// Create symlink in plugins dir
|
|
479
|
+
fs.mkdirSync(PLUGINS_DIR, { recursive: true });
|
|
480
|
+
fs.symlinkSync(subDir, pluginDir, 'dir');
|
|
481
|
+
|
|
482
|
+
// Set up lock file with monorepo entry
|
|
483
|
+
const lock = _readLockFile();
|
|
484
|
+
lock['__test-mono-sub__'] = {
|
|
485
|
+
source: 'https://github.com/user/test.git',
|
|
486
|
+
commitHash: 'abc123',
|
|
487
|
+
installedAt: '2025-01-01T00:00:00.000Z',
|
|
488
|
+
monorepo: { name: '__test-mono__', subPath: 'packages/sub' },
|
|
489
|
+
};
|
|
490
|
+
_writeLockFile(lock);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
afterEach(() => {
|
|
494
|
+
try { fs.unlinkSync(pluginDir); } catch {}
|
|
495
|
+
try { fs.rmSync(pluginDir, { recursive: true, force: true }); } catch {}
|
|
496
|
+
try { fs.rmSync(monoDir, { recursive: true, force: true }); } catch {}
|
|
497
|
+
// Clean up lock entry
|
|
498
|
+
const lock = _readLockFile();
|
|
499
|
+
delete lock['__test-mono-sub__'];
|
|
500
|
+
_writeLockFile(lock);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it('removes symlink but keeps monorepo if other sub-plugins reference it', () => {
|
|
504
|
+
// Add another sub-plugin referencing the same monorepo
|
|
505
|
+
const lock = _readLockFile();
|
|
506
|
+
lock['__test-mono-other__'] = {
|
|
507
|
+
source: 'https://github.com/user/test.git',
|
|
508
|
+
commitHash: 'abc123',
|
|
509
|
+
installedAt: '2025-01-01T00:00:00.000Z',
|
|
510
|
+
monorepo: { name: '__test-mono__', subPath: 'packages/other' },
|
|
511
|
+
};
|
|
512
|
+
_writeLockFile(lock);
|
|
513
|
+
|
|
514
|
+
uninstallPlugin('__test-mono-sub__');
|
|
515
|
+
|
|
516
|
+
// Symlink removed
|
|
517
|
+
expect(fs.existsSync(pluginDir)).toBe(false);
|
|
518
|
+
// Monorepo dir still exists (other sub-plugin references it)
|
|
519
|
+
expect(fs.existsSync(monoDir)).toBe(true);
|
|
520
|
+
// Lock entry removed
|
|
521
|
+
expect(_readLockFile()['__test-mono-sub__']).toBeUndefined();
|
|
522
|
+
// Other lock entry still present
|
|
523
|
+
expect(_readLockFile()['__test-mono-other__']).toBeDefined();
|
|
524
|
+
|
|
525
|
+
// Clean up the other entry
|
|
526
|
+
const finalLock = _readLockFile();
|
|
527
|
+
delete finalLock['__test-mono-other__'];
|
|
528
|
+
_writeLockFile(finalLock);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('removes symlink AND monorepo dir when last sub-plugin is uninstalled', () => {
|
|
532
|
+
uninstallPlugin('__test-mono-sub__');
|
|
533
|
+
|
|
534
|
+
// Symlink removed
|
|
535
|
+
expect(fs.existsSync(pluginDir)).toBe(false);
|
|
536
|
+
// Monorepo dir also removed (no more references)
|
|
537
|
+
expect(fs.existsSync(monoDir)).toBe(false);
|
|
538
|
+
// Lock entry removed
|
|
539
|
+
expect(_readLockFile()['__test-mono-sub__']).toBeUndefined();
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
describe('listPlugins with monorepo metadata', () => {
|
|
544
|
+
const testSymlinkTarget = path.join(os.tmpdir(), 'opencli-list-mono-target');
|
|
545
|
+
const testLink = path.join(PLUGINS_DIR, '__test-mono-list__');
|
|
546
|
+
|
|
547
|
+
beforeEach(() => {
|
|
548
|
+
// Create a target dir with a command file
|
|
549
|
+
fs.mkdirSync(testSymlinkTarget, { recursive: true });
|
|
550
|
+
fs.writeFileSync(path.join(testSymlinkTarget, 'hello.yaml'), 'site: test\nname: hello\n');
|
|
551
|
+
|
|
552
|
+
// Create symlink
|
|
553
|
+
fs.mkdirSync(PLUGINS_DIR, { recursive: true });
|
|
554
|
+
try { fs.unlinkSync(testLink); } catch {}
|
|
555
|
+
fs.symlinkSync(testSymlinkTarget, testLink, 'dir');
|
|
556
|
+
|
|
557
|
+
// Set up lock file with monorepo entry
|
|
558
|
+
const lock = _readLockFile();
|
|
559
|
+
lock['__test-mono-list__'] = {
|
|
560
|
+
source: 'https://github.com/user/test-mono.git',
|
|
561
|
+
commitHash: 'def456def456def456def456def456def456def4',
|
|
562
|
+
installedAt: '2025-01-01T00:00:00.000Z',
|
|
563
|
+
monorepo: { name: 'test-mono', subPath: 'packages/list' },
|
|
564
|
+
};
|
|
565
|
+
_writeLockFile(lock);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
afterEach(() => {
|
|
569
|
+
try { fs.unlinkSync(testLink); } catch {}
|
|
570
|
+
try { fs.rmSync(testSymlinkTarget, { recursive: true, force: true }); } catch {}
|
|
571
|
+
const lock = _readLockFile();
|
|
572
|
+
delete lock['__test-mono-list__'];
|
|
573
|
+
_writeLockFile(lock);
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
it('lists symlinked plugins with monorepoName', () => {
|
|
577
|
+
const plugins = listPlugins();
|
|
578
|
+
const found = plugins.find(p => p.name === '__test-mono-list__');
|
|
579
|
+
expect(found).toBeDefined();
|
|
580
|
+
expect(found!.monorepoName).toBe('test-mono');
|
|
581
|
+
expect(found!.commands).toContain('hello');
|
|
582
|
+
expect(found!.source).toBe('https://github.com/user/test-mono.git');
|
|
583
|
+
});
|
|
584
|
+
});
|