@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/extension/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "OpenCLI",
|
|
4
|
-
"version": "1.4.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "1.4.1",
|
|
5
|
+
"description": "Browser automation bridge for the OpenCLI CLI tool. Executes commands in isolated Chrome windows via a local daemon.",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"debugger",
|
|
8
8
|
"tabs",
|
|
@@ -22,10 +22,14 @@
|
|
|
22
22
|
},
|
|
23
23
|
"action": {
|
|
24
24
|
"default_title": "OpenCLI",
|
|
25
|
+
"default_popup": "popup.html",
|
|
25
26
|
"default_icon": {
|
|
26
27
|
"16": "icons/icon-16.png",
|
|
27
28
|
"32": "icons/icon-32.png"
|
|
28
29
|
}
|
|
29
30
|
},
|
|
31
|
+
"content_security_policy": {
|
|
32
|
+
"extension_pages": "script-src 'self'; object-src 'self'"
|
|
33
|
+
},
|
|
30
34
|
"homepage_url": "https://github.com/jackwener/opencli"
|
|
31
35
|
}
|
package/extension/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencli-extension",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vite build --watch",
|
|
8
8
|
"build": "vite build",
|
|
9
|
+
"package:release": "node scripts/package-release.mjs",
|
|
9
10
|
"typecheck": "tsc --noEmit"
|
|
10
11
|
},
|
|
11
12
|
"devDependencies": {
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<style>
|
|
6
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
7
|
+
body {
|
|
8
|
+
width: 280px;
|
|
9
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
10
|
+
font-size: 13px;
|
|
11
|
+
color: #333;
|
|
12
|
+
background: #fff;
|
|
13
|
+
padding: 16px;
|
|
14
|
+
}
|
|
15
|
+
.header {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: 8px;
|
|
19
|
+
margin-bottom: 14px;
|
|
20
|
+
}
|
|
21
|
+
.header img { width: 24px; height: 24px; }
|
|
22
|
+
.header h1 { font-size: 15px; font-weight: 600; }
|
|
23
|
+
.status-row {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: 8px;
|
|
27
|
+
padding: 10px 12px;
|
|
28
|
+
border-radius: 8px;
|
|
29
|
+
background: #f5f5f5;
|
|
30
|
+
}
|
|
31
|
+
.dot {
|
|
32
|
+
width: 8px; height: 8px;
|
|
33
|
+
border-radius: 50%;
|
|
34
|
+
flex-shrink: 0;
|
|
35
|
+
}
|
|
36
|
+
.dot.connected { background: #34c759; }
|
|
37
|
+
.dot.disconnected { background: #ff3b30; }
|
|
38
|
+
.dot.connecting { background: #ff9500; }
|
|
39
|
+
.status-text { font-size: 13px; color: #555; }
|
|
40
|
+
.status-text strong { color: #333; }
|
|
41
|
+
.hint {
|
|
42
|
+
margin-top: 10px;
|
|
43
|
+
padding: 8px 10px;
|
|
44
|
+
border-radius: 6px;
|
|
45
|
+
background: #f0f4ff;
|
|
46
|
+
font-size: 11px;
|
|
47
|
+
color: #666;
|
|
48
|
+
line-height: 1.5;
|
|
49
|
+
display: none;
|
|
50
|
+
}
|
|
51
|
+
.hint code {
|
|
52
|
+
background: #e8ecf1;
|
|
53
|
+
padding: 1px 4px;
|
|
54
|
+
border-radius: 3px;
|
|
55
|
+
font-size: 11px;
|
|
56
|
+
}
|
|
57
|
+
.footer {
|
|
58
|
+
margin-top: 14px;
|
|
59
|
+
text-align: center;
|
|
60
|
+
font-size: 11px;
|
|
61
|
+
color: #999;
|
|
62
|
+
}
|
|
63
|
+
.footer a { color: #007aff; text-decoration: none; }
|
|
64
|
+
.footer a:hover { text-decoration: underline; }
|
|
65
|
+
</style>
|
|
66
|
+
</head>
|
|
67
|
+
<body>
|
|
68
|
+
<div class="header">
|
|
69
|
+
<img src="icons/icon-48.png" alt="OpenCLI">
|
|
70
|
+
<h1>OpenCLI</h1>
|
|
71
|
+
</div>
|
|
72
|
+
<div class="status-row">
|
|
73
|
+
<span class="dot disconnected" id="dot"></span>
|
|
74
|
+
<span class="status-text" id="status">Checking...</span>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="hint" id="hint">
|
|
77
|
+
This is normal. The extension connects automatically when you run any <code>opencli</code> command.
|
|
78
|
+
</div>
|
|
79
|
+
<div class="footer">
|
|
80
|
+
<a href="https://github.com/jackwener/opencli" target="_blank">Documentation</a>
|
|
81
|
+
</div>
|
|
82
|
+
<script src="popup.js"></script>
|
|
83
|
+
</body>
|
|
84
|
+
</html>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Query connection status from background service worker
|
|
2
|
+
chrome.runtime.sendMessage({ type: 'getStatus' }, (resp) => {
|
|
3
|
+
const dot = document.getElementById('dot');
|
|
4
|
+
const status = document.getElementById('status');
|
|
5
|
+
const hint = document.getElementById('hint');
|
|
6
|
+
if (chrome.runtime.lastError || !resp) {
|
|
7
|
+
dot.className = 'dot disconnected';
|
|
8
|
+
status.innerHTML = '<strong>No daemon connected</strong>';
|
|
9
|
+
hint.style.display = 'block';
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (resp.connected) {
|
|
13
|
+
dot.className = 'dot connected';
|
|
14
|
+
status.innerHTML = '<strong>Connected to daemon</strong>';
|
|
15
|
+
hint.style.display = 'none';
|
|
16
|
+
} else if (resp.reconnecting) {
|
|
17
|
+
dot.className = 'dot connecting';
|
|
18
|
+
status.innerHTML = '<strong>Reconnecting...</strong>';
|
|
19
|
+
hint.style.display = 'none';
|
|
20
|
+
} else {
|
|
21
|
+
dot.className = 'dot disconnected';
|
|
22
|
+
status.innerHTML = '<strong>No daemon connected</strong>';
|
|
23
|
+
hint.style.display = 'block';
|
|
24
|
+
}
|
|
25
|
+
});
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const extensionDir = path.resolve(__dirname, '..');
|
|
7
|
+
const repoRoot = path.resolve(extensionDir, '..');
|
|
8
|
+
|
|
9
|
+
function parseArgs(argv) {
|
|
10
|
+
const args = { outDir: path.join(repoRoot, 'extension-package') };
|
|
11
|
+
for (let i = 0; i < argv.length; i++) {
|
|
12
|
+
const arg = argv[i];
|
|
13
|
+
if (arg === '--out' && argv[i + 1]) {
|
|
14
|
+
const outDir = argv[++i];
|
|
15
|
+
args.outDir = path.isAbsolute(outDir)
|
|
16
|
+
? outDir
|
|
17
|
+
: path.resolve(process.cwd(), outDir);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return args;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function exists(targetPath) {
|
|
24
|
+
try {
|
|
25
|
+
await fs.access(targetPath);
|
|
26
|
+
return true;
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isLocalAsset(ref) {
|
|
33
|
+
return typeof ref === 'string'
|
|
34
|
+
&& ref.length > 0
|
|
35
|
+
&& !ref.startsWith('http://')
|
|
36
|
+
&& !ref.startsWith('https://')
|
|
37
|
+
&& !ref.startsWith('//')
|
|
38
|
+
&& !ref.startsWith('chrome://')
|
|
39
|
+
&& !ref.startsWith('chrome-extension://')
|
|
40
|
+
&& !ref.startsWith('data:')
|
|
41
|
+
&& !ref.startsWith('#');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function addLocalAsset(files, ref) {
|
|
45
|
+
if (isLocalAsset(ref)) files.add(ref);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function collectManifestEntrypoints(manifest) {
|
|
49
|
+
const files = new Set(['manifest.json']);
|
|
50
|
+
|
|
51
|
+
addLocalAsset(files, manifest.background?.service_worker);
|
|
52
|
+
addLocalAsset(files, manifest.action?.default_popup);
|
|
53
|
+
addLocalAsset(files, manifest.options_page);
|
|
54
|
+
addLocalAsset(files, manifest.devtools_page);
|
|
55
|
+
addLocalAsset(files, manifest.side_panel?.default_path);
|
|
56
|
+
|
|
57
|
+
for (const ref of Object.values(manifest.icons ?? {})) addLocalAsset(files, ref);
|
|
58
|
+
for (const ref of Object.values(manifest.action?.default_icon ?? {})) addLocalAsset(files, ref);
|
|
59
|
+
for (const contentScript of manifest.content_scripts ?? []) {
|
|
60
|
+
for (const jsFile of contentScript.js ?? []) addLocalAsset(files, jsFile);
|
|
61
|
+
for (const cssFile of contentScript.css ?? []) addLocalAsset(files, cssFile);
|
|
62
|
+
}
|
|
63
|
+
for (const page of manifest.sandbox?.pages ?? []) addLocalAsset(files, page);
|
|
64
|
+
for (const overridePage of Object.values(manifest.chrome_url_overrides ?? {})) addLocalAsset(files, overridePage);
|
|
65
|
+
for (const entry of manifest.web_accessible_resources ?? []) {
|
|
66
|
+
for (const resource of entry.resources ?? []) addLocalAsset(files, resource);
|
|
67
|
+
}
|
|
68
|
+
if (manifest.default_locale) files.add('_locales');
|
|
69
|
+
|
|
70
|
+
return [...files];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function collectHtmlDependencies(relativeHtmlPath, files, visited) {
|
|
74
|
+
if (visited.has(relativeHtmlPath)) return;
|
|
75
|
+
visited.add(relativeHtmlPath);
|
|
76
|
+
|
|
77
|
+
const htmlPath = path.join(extensionDir, relativeHtmlPath);
|
|
78
|
+
const html = await fs.readFile(htmlPath, 'utf8');
|
|
79
|
+
const attrRe = /\b(?:src|href)=["']([^"'#?]+(?:\?[^"']*)?)["']/gi;
|
|
80
|
+
|
|
81
|
+
for (const match of html.matchAll(attrRe)) {
|
|
82
|
+
const rawRef = match[1];
|
|
83
|
+
const cleanRef = rawRef.split('?')[0];
|
|
84
|
+
if (!isLocalAsset(cleanRef)) continue;
|
|
85
|
+
|
|
86
|
+
const resolvedRelativePath = cleanRef.startsWith('/')
|
|
87
|
+
? cleanRef.slice(1)
|
|
88
|
+
: path.posix.normalize(path.posix.join(path.posix.dirname(relativeHtmlPath), cleanRef));
|
|
89
|
+
|
|
90
|
+
addLocalAsset(files, resolvedRelativePath);
|
|
91
|
+
if (resolvedRelativePath.endsWith('.html')) {
|
|
92
|
+
await collectHtmlDependencies(resolvedRelativePath, files, visited);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function collectManifestAssets(manifest) {
|
|
98
|
+
const files = new Set(collectManifestEntrypoints(manifest));
|
|
99
|
+
const htmlPages = [];
|
|
100
|
+
|
|
101
|
+
if (manifest.action?.default_popup) {
|
|
102
|
+
htmlPages.push(manifest.action.default_popup);
|
|
103
|
+
}
|
|
104
|
+
if (manifest.options_page) htmlPages.push(manifest.options_page);
|
|
105
|
+
if (manifest.devtools_page) htmlPages.push(manifest.devtools_page);
|
|
106
|
+
if (manifest.side_panel?.default_path) htmlPages.push(manifest.side_panel.default_path);
|
|
107
|
+
for (const page of manifest.sandbox?.pages ?? []) htmlPages.push(page);
|
|
108
|
+
for (const overridePage of Object.values(manifest.chrome_url_overrides ?? {})) htmlPages.push(overridePage);
|
|
109
|
+
|
|
110
|
+
const visited = new Set();
|
|
111
|
+
for (const htmlPage of htmlPages) {
|
|
112
|
+
if (isLocalAsset(htmlPage)) {
|
|
113
|
+
await collectHtmlDependencies(htmlPage, files, visited);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return [...files];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function copyEntry(relativePath, outDir) {
|
|
121
|
+
const fromPath = path.join(extensionDir, relativePath);
|
|
122
|
+
const toPath = path.join(outDir, relativePath);
|
|
123
|
+
const stats = await fs.stat(fromPath);
|
|
124
|
+
|
|
125
|
+
if (stats.isDirectory()) {
|
|
126
|
+
await fs.cp(fromPath, toPath, { recursive: true });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
await fs.mkdir(path.dirname(toPath), { recursive: true });
|
|
131
|
+
await fs.copyFile(fromPath, toPath);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function findMissingEntries(baseDir, entries) {
|
|
135
|
+
const missingEntries = [];
|
|
136
|
+
for (const relativePath of entries) {
|
|
137
|
+
const absolutePath = path.join(baseDir, relativePath);
|
|
138
|
+
if (!(await exists(absolutePath))) {
|
|
139
|
+
missingEntries.push(relativePath);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return missingEntries;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function main() {
|
|
146
|
+
const { outDir } = parseArgs(process.argv.slice(2));
|
|
147
|
+
const manifestPath = path.join(extensionDir, 'manifest.json');
|
|
148
|
+
const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
|
|
149
|
+
|
|
150
|
+
const requiredEntries = await collectManifestAssets(manifest);
|
|
151
|
+
const missingEntries = await findMissingEntries(extensionDir, requiredEntries);
|
|
152
|
+
|
|
153
|
+
if (missingEntries.length > 0) {
|
|
154
|
+
console.error('Missing files referenced by the extension package:');
|
|
155
|
+
for (const missingEntry of missingEntries) console.error(`- ${missingEntry}`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
await fs.rm(outDir, { recursive: true, force: true });
|
|
160
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
161
|
+
|
|
162
|
+
for (const relativePath of requiredEntries) {
|
|
163
|
+
await copyEntry(relativePath, outDir);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Guard against regressions where manifest entry files (e.g. action.default_popup)
|
|
167
|
+
// are accidentally omitted from the packaged directory.
|
|
168
|
+
const packagedEntrypoints = collectManifestEntrypoints(manifest);
|
|
169
|
+
const missingPackagedEntrypoints = await findMissingEntries(outDir, packagedEntrypoints);
|
|
170
|
+
if (missingPackagedEntrypoints.length > 0) {
|
|
171
|
+
console.error('Packaged extension is missing files referenced by manifest.json:');
|
|
172
|
+
for (const missingEntry of missingPackagedEntrypoints) console.error(`- ${missingEntry}`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.log(`Extension package prepared at ${path.relative(repoRoot, outDir) || outDir}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await main();
|
|
@@ -51,6 +51,8 @@ function connect(): void {
|
|
|
51
51
|
clearTimeout(reconnectTimer);
|
|
52
52
|
reconnectTimer = null;
|
|
53
53
|
}
|
|
54
|
+
// Send version so the daemon can report mismatches to the CLI
|
|
55
|
+
ws?.send(JSON.stringify({ type: 'hello', version: chrome.runtime.getManifest().version }));
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
ws.onmessage = async (event) => {
|
|
@@ -74,10 +76,17 @@ function connect(): void {
|
|
|
74
76
|
};
|
|
75
77
|
}
|
|
76
78
|
|
|
79
|
+
/**
|
|
80
|
+
* After MAX_EAGER_ATTEMPTS (reaching 60s backoff), stop scheduling reconnects.
|
|
81
|
+
* The keepalive alarm (~24s) will still call connect() periodically, but at a
|
|
82
|
+
* much lower frequency — reducing console noise when the daemon is not running.
|
|
83
|
+
*/
|
|
84
|
+
const MAX_EAGER_ATTEMPTS = 6; // 2s, 4s, 8s, 16s, 32s, 60s — then stop
|
|
85
|
+
|
|
77
86
|
function scheduleReconnect(): void {
|
|
78
87
|
if (reconnectTimer) return;
|
|
79
88
|
reconnectAttempts++;
|
|
80
|
-
|
|
89
|
+
if (reconnectAttempts > MAX_EAGER_ATTEMPTS) return; // let keepalive alarm handle it
|
|
81
90
|
const delay = Math.min(WS_RECONNECT_BASE_DELAY * Math.pow(2, reconnectAttempts - 1), WS_RECONNECT_MAX_DELAY);
|
|
82
91
|
reconnectTimer = setTimeout(() => {
|
|
83
92
|
reconnectTimer = null;
|
|
@@ -193,6 +202,18 @@ chrome.alarms.onAlarm.addListener((alarm) => {
|
|
|
193
202
|
if (alarm.name === 'keepalive') connect();
|
|
194
203
|
});
|
|
195
204
|
|
|
205
|
+
// ─── Popup status API ───────────────────────────────────────────────
|
|
206
|
+
|
|
207
|
+
chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
|
|
208
|
+
if (msg?.type === 'getStatus') {
|
|
209
|
+
sendResponse({
|
|
210
|
+
connected: ws?.readyState === WebSocket.OPEN,
|
|
211
|
+
reconnecting: reconnectTimer !== null,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return false;
|
|
215
|
+
});
|
|
216
|
+
|
|
196
217
|
// ─── Command dispatcher ─────────────────────────────────────────────
|
|
197
218
|
|
|
198
219
|
async function handleCommand(cmd: Command): Promise<Result> {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jackwener/opencli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -19,17 +19,20 @@
|
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"dev": "tsx src/main.ts",
|
|
22
|
+
"dev:bun": "bun src/main.ts",
|
|
22
23
|
"build": "npm run clean-dist && tsc && npm run clean-yaml && npm run copy-yaml && npm run build-manifest",
|
|
23
24
|
"build-manifest": "node dist/build-manifest.js",
|
|
24
25
|
"clean-dist": "node scripts/clean-dist.cjs",
|
|
25
26
|
"clean-yaml": "node scripts/clean-yaml.cjs",
|
|
26
27
|
"copy-yaml": "node scripts/copy-yaml.cjs",
|
|
27
28
|
"start": "node dist/main.js",
|
|
29
|
+
"start:bun": "bun dist/main.js",
|
|
28
30
|
"postinstall": "node scripts/postinstall.js || true",
|
|
29
31
|
"typecheck": "tsc --noEmit",
|
|
30
32
|
"lint": "tsc --noEmit",
|
|
31
33
|
"prepublishOnly": "npm run build",
|
|
32
34
|
"test": "vitest run --project unit",
|
|
35
|
+
"test:bun": "bun vitest run --project unit",
|
|
33
36
|
"test:adapter": "vitest run --project adapter",
|
|
34
37
|
"test:all": "vitest run",
|
|
35
38
|
"test:e2e": "vitest run --project e2e",
|
package/scripts/postinstall.js
CHANGED
|
@@ -195,6 +195,16 @@ function main() {
|
|
|
195
195
|
console.error(`Warning: Could not install shell completion: ${err.message}`);
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
|
+
|
|
199
|
+
// ── Browser Bridge setup hint ───────────────────────────────────────
|
|
200
|
+
console.log('');
|
|
201
|
+
console.log(' \x1b[1mNext step — Browser Bridge setup\x1b[0m');
|
|
202
|
+
console.log(' Browser commands (bilibili, zhihu, twitter...) require the extension:');
|
|
203
|
+
console.log(' 1. Download: https://github.com/jackwener/opencli/releases');
|
|
204
|
+
console.log(' 2. Open chrome://extensions → enable Developer Mode → Load unpacked');
|
|
205
|
+
console.log('');
|
|
206
|
+
console.log(' Then run \x1b[36mopencli doctor\x1b[0m to verify.');
|
|
207
|
+
console.log('');
|
|
198
208
|
}
|
|
199
209
|
|
|
200
210
|
main();
|
package/src/browser/cdp.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { WebSocket, type RawData } from 'ws';
|
|
|
12
12
|
import { request as httpRequest } from 'node:http';
|
|
13
13
|
import { request as httpsRequest } from 'node:https';
|
|
14
14
|
import type { BrowserCookie, IPage, ScreenshotOptions, SnapshotOptions, WaitOptions } from '../types.js';
|
|
15
|
+
import type { IBrowserFactory } from '../runtime.js';
|
|
15
16
|
import { wrapForEval } from './utils.js';
|
|
16
17
|
import { generateSnapshotJs, scrollToRefJs, getFormStateJs } from './dom-snapshot.js';
|
|
17
18
|
import { generateStealthJs } from './stealth.js';
|
|
@@ -47,7 +48,7 @@ interface RuntimeEvaluateResult {
|
|
|
47
48
|
|
|
48
49
|
const CDP_SEND_TIMEOUT = 30_000;
|
|
49
50
|
|
|
50
|
-
export class CDPBridge {
|
|
51
|
+
export class CDPBridge implements IBrowserFactory {
|
|
51
52
|
private _ws: WebSocket | null = null;
|
|
52
53
|
private _idCounter = 0;
|
|
53
54
|
private _pending = new Map<number, { resolve: (val: unknown) => void; reject: (err: Error) => void; timer: ReturnType<typeof setTimeout> }>();
|
package/src/browser/discover.ts
CHANGED
|
@@ -13,17 +13,22 @@ export { isDaemonRunning };
|
|
|
13
13
|
/**
|
|
14
14
|
* Check daemon status and return connection info.
|
|
15
15
|
*/
|
|
16
|
-
export async function checkDaemonStatus(): Promise<{
|
|
16
|
+
export async function checkDaemonStatus(opts?: { timeout?: number }): Promise<{
|
|
17
17
|
running: boolean;
|
|
18
18
|
extensionConnected: boolean;
|
|
19
|
+
extensionVersion?: string;
|
|
19
20
|
}> {
|
|
20
21
|
try {
|
|
21
22
|
const port = parseInt(process.env.OPENCLI_DAEMON_PORT ?? String(DEFAULT_DAEMON_PORT), 10);
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const timer = setTimeout(() => controller.abort(), opts?.timeout ?? 2000);
|
|
22
25
|
const res = await fetch(`http://127.0.0.1:${port}/status`, {
|
|
23
26
|
headers: { 'X-OpenCLI': '1' },
|
|
27
|
+
signal: controller.signal,
|
|
24
28
|
});
|
|
25
|
-
const data = await res.json() as { ok: boolean; extensionConnected: boolean };
|
|
26
|
-
|
|
29
|
+
const data = await res.json() as { ok: boolean; extensionConnected: boolean; extensionVersion?: string };
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
return { running: true, extensionConnected: data.extensionConnected, extensionVersion: data.extensionVersion };
|
|
27
32
|
} catch {
|
|
28
33
|
return { running: false, extensionConnected: false };
|
|
29
34
|
}
|
package/src/browser/errors.ts
CHANGED
|
@@ -5,38 +5,37 @@
|
|
|
5
5
|
* The daemon architecture has a single failure mode: daemon not reachable or extension not connected.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { BrowserConnectError } from '../errors.js';
|
|
8
|
+
import { BrowserConnectError, type BrowserConnectKind } from '../errors.js';
|
|
9
9
|
import { DEFAULT_DAEMON_PORT } from '../constants.js';
|
|
10
10
|
|
|
11
|
-
export
|
|
11
|
+
// Re-export so callers don't need to import from two places
|
|
12
|
+
export type ConnectFailureKind = BrowserConnectKind;
|
|
12
13
|
|
|
13
14
|
export function formatBrowserConnectError(kind: ConnectFailureKind, detail?: string): BrowserConnectError {
|
|
14
15
|
switch (kind) {
|
|
15
16
|
case 'daemon-not-running':
|
|
16
17
|
return new BrowserConnectError(
|
|
17
|
-
'Cannot connect to opencli daemon.' +
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
' node dist/daemon.js\n' +
|
|
21
|
-
`Make sure port ${DEFAULT_DAEMON_PORT} is available.`,
|
|
18
|
+
'Cannot connect to opencli daemon.' + (detail ? `\n\n${detail}` : ''),
|
|
19
|
+
`The daemon should auto-start. If it keeps failing, make sure port ${DEFAULT_DAEMON_PORT} is available.`,
|
|
20
|
+
kind,
|
|
22
21
|
);
|
|
23
22
|
case 'extension-not-connected':
|
|
24
23
|
return new BrowserConnectError(
|
|
25
|
-
'
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
' 1. Download from GitHub Releases\n' +
|
|
29
|
-
' 2. Open chrome://extensions/ → Enable Developer Mode\n' +
|
|
30
|
-
' 3. Click "Load unpacked" → select the extension folder\n' +
|
|
31
|
-
' 4. Make sure Chrome is running',
|
|
24
|
+
'Browser Bridge extension is not connected.' + (detail ? `\n\n${detail}` : ''),
|
|
25
|
+
'Install the extension from GitHub Releases, then reload.',
|
|
26
|
+
kind,
|
|
32
27
|
);
|
|
33
28
|
case 'command-failed':
|
|
34
29
|
return new BrowserConnectError(
|
|
35
30
|
`Browser command failed: ${detail ?? 'unknown error'}`,
|
|
31
|
+
undefined,
|
|
32
|
+
kind,
|
|
36
33
|
);
|
|
37
34
|
default:
|
|
38
35
|
return new BrowserConnectError(
|
|
39
36
|
detail ?? 'Failed to connect to browser',
|
|
37
|
+
undefined,
|
|
38
|
+
kind,
|
|
40
39
|
);
|
|
41
40
|
}
|
|
42
41
|
}
|
package/src/browser/mcp.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
import * as path from 'node:path';
|
|
8
8
|
import * as fs from 'node:fs';
|
|
9
9
|
import type { IPage } from '../types.js';
|
|
10
|
+
import type { IBrowserFactory } from '../runtime.js';
|
|
10
11
|
import { Page } from './page.js';
|
|
11
12
|
import { isDaemonRunning, isExtensionConnected } from './daemon-client.js';
|
|
12
13
|
import { DEFAULT_DAEMON_PORT } from '../constants.js';
|
|
@@ -18,7 +19,7 @@ export type BrowserBridgeState = 'idle' | 'connecting' | 'connected' | 'closing'
|
|
|
18
19
|
/**
|
|
19
20
|
* Browser factory: manages daemon lifecycle and provides IPage instances.
|
|
20
21
|
*/
|
|
21
|
-
export class BrowserBridge {
|
|
22
|
+
export class BrowserBridge implements IBrowserFactory {
|
|
22
23
|
private _state: BrowserBridgeState = 'idle';
|
|
23
24
|
private _page: Page | null = null;
|
|
24
25
|
private _daemonProc: ChildProcess | null = null;
|
|
@@ -143,4 +143,27 @@ describe('manifest helper rules', () => {
|
|
|
143
143
|
modulePath: 'xueqiu/fund-holdings.js',
|
|
144
144
|
});
|
|
145
145
|
});
|
|
146
|
+
|
|
147
|
+
it('captures deprecated metadata for TS adapters', () => {
|
|
148
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
|
|
149
|
+
tempDirs.push(dir);
|
|
150
|
+
const file = path.join(dir, 'legacy.ts');
|
|
151
|
+
fs.writeFileSync(file, `
|
|
152
|
+
import { cli } from '../../registry.js';
|
|
153
|
+
cli({
|
|
154
|
+
site: 'demo',
|
|
155
|
+
name: 'legacy',
|
|
156
|
+
description: 'legacy command',
|
|
157
|
+
deprecated: 'legacy is deprecated',
|
|
158
|
+
replacedBy: 'opencli demo new',
|
|
159
|
+
});
|
|
160
|
+
`);
|
|
161
|
+
|
|
162
|
+
expect(scanTs(file, 'demo')).toMatchObject({
|
|
163
|
+
site: 'demo',
|
|
164
|
+
name: 'legacy',
|
|
165
|
+
deprecated: 'legacy is deprecated',
|
|
166
|
+
replacedBy: 'opencli demo new',
|
|
167
|
+
});
|
|
168
|
+
});
|
|
146
169
|
});
|
package/src/build-manifest.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface ManifestEntry {
|
|
|
38
38
|
columns?: string[];
|
|
39
39
|
pipeline?: Record<string, unknown>[];
|
|
40
40
|
timeout?: number;
|
|
41
|
+
deprecated?: boolean | string;
|
|
42
|
+
replacedBy?: string;
|
|
41
43
|
/** 'yaml' or 'ts' — determines how executeCommand loads the handler */
|
|
42
44
|
type: 'yaml' | 'ts';
|
|
43
45
|
/** Relative path from clis/ dir, e.g. 'bilibili/hot.yaml' or 'bilibili/search.js' */
|
|
@@ -46,7 +48,7 @@ export interface ManifestEntry {
|
|
|
46
48
|
navigateBefore?: boolean | string;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
import type
|
|
51
|
+
import { type YamlCliDefinition, parseYamlArgs } from './yaml-schema.js';
|
|
50
52
|
|
|
51
53
|
import { isRecord } from './utils.js';
|
|
52
54
|
|
|
@@ -173,20 +175,7 @@ function scanYaml(filePath: string, site: string): ManifestEntry | null {
|
|
|
173
175
|
const strategy = strategyStr.toUpperCase();
|
|
174
176
|
const browser = cliDef.browser ?? (strategy !== 'PUBLIC');
|
|
175
177
|
|
|
176
|
-
const args
|
|
177
|
-
if (cliDef.args && typeof cliDef.args === 'object') {
|
|
178
|
-
for (const [argName, argDef] of Object.entries(cliDef.args)) {
|
|
179
|
-
args.push({
|
|
180
|
-
name: argName,
|
|
181
|
-
type: argDef?.type ?? 'str',
|
|
182
|
-
default: argDef?.default,
|
|
183
|
-
required: argDef?.required ?? false,
|
|
184
|
-
positional: argDef?.positional === true || undefined,
|
|
185
|
-
help: argDef?.description ?? argDef?.help ?? '',
|
|
186
|
-
choices: argDef?.choices,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
}
|
|
178
|
+
const args = parseYamlArgs(cliDef.args);
|
|
190
179
|
|
|
191
180
|
return {
|
|
192
181
|
site: cliDef.site ?? site,
|
|
@@ -199,6 +188,8 @@ function scanYaml(filePath: string, site: string): ManifestEntry | null {
|
|
|
199
188
|
columns: cliDef.columns,
|
|
200
189
|
pipeline: cliDef.pipeline,
|
|
201
190
|
timeout: cliDef.timeout,
|
|
191
|
+
deprecated: (cliDef as Record<string, unknown>).deprecated as boolean | string | undefined,
|
|
192
|
+
replacedBy: (cliDef as Record<string, unknown>).replacedBy as string | undefined,
|
|
202
193
|
type: 'yaml',
|
|
203
194
|
navigateBefore: cliDef.navigateBefore,
|
|
204
195
|
};
|
|
@@ -269,6 +260,17 @@ export function scanTs(filePath: string, site: string): ManifestEntry | null {
|
|
|
269
260
|
if (navStringMatch) entry.navigateBefore = navStringMatch[1];
|
|
270
261
|
}
|
|
271
262
|
|
|
263
|
+
const deprecatedBoolMatch = src.match(/deprecated\s*:\s*(true|false)/);
|
|
264
|
+
if (deprecatedBoolMatch) {
|
|
265
|
+
entry.deprecated = deprecatedBoolMatch[1] === 'true';
|
|
266
|
+
} else {
|
|
267
|
+
const deprecatedStringMatch = src.match(/deprecated\s*:\s*['"`]([^'"`]+)['"`]/);
|
|
268
|
+
if (deprecatedStringMatch) entry.deprecated = deprecatedStringMatch[1];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const replacedByMatch = src.match(/replacedBy\s*:\s*['"`]([^'"`]+)['"`]/);
|
|
272
|
+
if (replacedByMatch) entry.replacedBy = replacedByMatch[1];
|
|
273
|
+
|
|
272
274
|
return entry;
|
|
273
275
|
} catch (err) {
|
|
274
276
|
// If parsing fails, log a warning (matching scanYaml behaviour) and skip the entry.
|
|
@@ -338,6 +340,29 @@ function main(): void {
|
|
|
338
340
|
const yamlCount = manifest.filter(e => e.type === 'yaml').length;
|
|
339
341
|
const tsCount = manifest.filter(e => e.type === 'ts').length;
|
|
340
342
|
console.log(`✅ Manifest compiled: ${manifest.length} entries (${yamlCount} YAML, ${tsCount} TS) → ${OUTPUT}`);
|
|
343
|
+
|
|
344
|
+
// Restore executable permissions on bin entries.
|
|
345
|
+
// tsc does not preserve the +x bit, so after a clean rebuild the CLI
|
|
346
|
+
// entry-point loses its executable permission, causing "Permission denied".
|
|
347
|
+
// See: https://github.com/jackwener/opencli/issues/446
|
|
348
|
+
if (process.platform !== 'win32') {
|
|
349
|
+
const pkgPath = path.resolve(__dirname, '..', 'package.json');
|
|
350
|
+
try {
|
|
351
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
352
|
+
const bins: Record<string, string> = typeof pkg.bin === 'string'
|
|
353
|
+
? { [pkg.name ?? 'cli']: pkg.bin }
|
|
354
|
+
: pkg.bin ?? {};
|
|
355
|
+
for (const binPath of Object.values(bins)) {
|
|
356
|
+
const abs = path.resolve(__dirname, '..', binPath);
|
|
357
|
+
if (fs.existsSync(abs)) {
|
|
358
|
+
fs.chmodSync(abs, 0o755);
|
|
359
|
+
console.log(`✅ Restored executable permission: ${binPath}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
} catch {
|
|
363
|
+
// Best-effort; never break the build for a permission fix.
|
|
364
|
+
}
|
|
365
|
+
}
|
|
341
366
|
}
|
|
342
367
|
|
|
343
368
|
const entrypoint = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : null;
|
package/src/capabilityRouting.ts
CHANGED