@jackwener/opencli 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/actions/setup-chrome/action.yml +5 -4
- package/.github/workflows/build-extension.yml +2 -6
- package/.github/workflows/ci.yml +37 -3
- package/.github/workflows/e2e-headed.yml +16 -3
- package/CHANGELOG.md +23 -0
- package/PRIVACY.md +57 -0
- package/README.md +36 -7
- package/README.zh-CN.md +13 -6
- package/SKILL.md +103 -2
- package/dist/browser/cdp.d.ts +2 -1
- package/dist/browser/discover.d.ts +4 -1
- package/dist/browser/discover.js +6 -2
- package/dist/browser/errors.d.ts +2 -2
- package/dist/browser/errors.js +4 -12
- package/dist/browser/mcp.d.ts +2 -1
- package/dist/build-manifest.d.ts +2 -0
- package/dist/build-manifest.js +39 -14
- package/dist/build-manifest.test.js +21 -0
- package/dist/capabilityRouting.d.ts +2 -0
- package/dist/capabilityRouting.js +2 -1
- package/dist/cli-manifest.json +1838 -151
- package/dist/cli.js +34 -3
- package/dist/clis/36kr/article.d.ts +1 -0
- package/dist/clis/36kr/article.js +62 -0
- package/dist/clis/36kr/hot.d.ts +3 -0
- package/dist/clis/36kr/hot.js +80 -0
- package/dist/clis/36kr/hot.test.d.ts +1 -0
- package/dist/clis/36kr/hot.test.js +15 -0
- package/dist/clis/36kr/news.d.ts +1 -0
- package/dist/clis/36kr/news.js +51 -0
- package/dist/clis/36kr/news.test.d.ts +1 -0
- package/dist/clis/36kr/news.test.js +85 -0
- package/dist/clis/36kr/search.d.ts +1 -0
- package/dist/clis/36kr/search.js +72 -0
- package/dist/clis/apple-podcasts/search.js +2 -1
- package/dist/clis/arxiv/search.js +2 -2
- package/dist/clis/bbc/news.js +0 -1
- package/dist/clis/bilibili/comments.d.ts +5 -0
- package/dist/clis/bilibili/comments.js +40 -0
- package/dist/clis/bilibili/comments.test.d.ts +1 -0
- package/dist/clis/bilibili/comments.test.js +82 -0
- package/dist/clis/chatgpt/ask.js +29 -14
- package/dist/clis/chatgpt/ax.d.ts +6 -0
- package/dist/clis/chatgpt/ax.js +172 -1
- package/dist/clis/chatgpt/model.d.ts +1 -0
- package/dist/clis/chatgpt/model.js +24 -0
- package/dist/clis/chatgpt/send.js +12 -3
- package/dist/clis/ctrip/search.js +0 -1
- package/dist/clis/douban/download.d.ts +1 -0
- package/dist/clis/douban/download.js +67 -0
- package/dist/clis/douban/download.test.d.ts +1 -0
- package/dist/clis/douban/download.test.js +170 -0
- package/dist/clis/douban/photos.d.ts +1 -0
- package/dist/clis/douban/photos.js +34 -0
- package/dist/clis/douban/utils.d.ts +25 -0
- package/dist/clis/douban/utils.js +190 -1
- package/dist/clis/douban/utils.test.d.ts +1 -0
- package/dist/clis/douban/utils.test.js +64 -0
- package/dist/clis/douyin/_shared/browser-fetch.d.ts +10 -0
- package/dist/clis/douyin/_shared/browser-fetch.js +30 -0
- package/dist/clis/douyin/_shared/browser-fetch.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/browser-fetch.test.js +31 -0
- package/dist/clis/douyin/_shared/creation-id.d.ts +1 -0
- package/dist/clis/douyin/_shared/creation-id.js +5 -0
- package/dist/clis/douyin/_shared/creation-id.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/creation-id.test.js +22 -0
- package/dist/clis/douyin/_shared/imagex-upload.d.ts +20 -0
- package/dist/clis/douyin/_shared/imagex-upload.js +53 -0
- package/dist/clis/douyin/_shared/imagex-upload.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/imagex-upload.test.js +87 -0
- package/dist/clis/douyin/_shared/sts2.d.ts +8 -0
- package/dist/clis/douyin/_shared/sts2.js +15 -0
- package/dist/clis/douyin/_shared/text-extra.d.ts +18 -0
- package/dist/clis/douyin/_shared/text-extra.js +15 -0
- package/dist/clis/douyin/_shared/text-extra.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/text-extra.test.js +37 -0
- package/dist/clis/douyin/_shared/timing.d.ts +2 -0
- package/dist/clis/douyin/_shared/timing.js +22 -0
- package/dist/clis/douyin/_shared/timing.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/timing.test.js +28 -0
- package/dist/clis/douyin/_shared/tos-upload-short-read.test.d.ts +11 -0
- package/dist/clis/douyin/_shared/tos-upload-short-read.test.js +83 -0
- package/dist/clis/douyin/_shared/tos-upload.d.ts +53 -0
- package/dist/clis/douyin/_shared/tos-upload.js +295 -0
- package/dist/clis/douyin/_shared/tos-upload.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/tos-upload.test.js +229 -0
- package/dist/clis/douyin/_shared/transcode.d.ts +27 -0
- package/dist/clis/douyin/_shared/transcode.js +45 -0
- package/dist/clis/douyin/_shared/transcode.test.d.ts +1 -0
- package/dist/clis/douyin/_shared/transcode.test.js +93 -0
- package/dist/clis/douyin/_shared/types.d.ts +26 -0
- package/dist/clis/douyin/_shared/types.js +1 -0
- package/dist/clis/douyin/activities.d.ts +1 -0
- package/dist/clis/douyin/activities.js +20 -0
- package/dist/clis/douyin/activities.test.d.ts +1 -0
- package/dist/clis/douyin/activities.test.js +22 -0
- package/dist/clis/douyin/collections.d.ts +1 -0
- package/dist/clis/douyin/collections.js +22 -0
- package/dist/clis/douyin/collections.test.d.ts +1 -0
- package/dist/clis/douyin/collections.test.js +23 -0
- package/dist/clis/douyin/delete.d.ts +1 -0
- package/dist/clis/douyin/delete.js +18 -0
- package/dist/clis/douyin/delete.test.d.ts +1 -0
- package/dist/clis/douyin/delete.test.js +11 -0
- package/dist/clis/douyin/draft.d.ts +14 -0
- package/dist/clis/douyin/draft.js +237 -0
- package/dist/clis/douyin/draft.test.d.ts +1 -0
- package/dist/clis/douyin/draft.test.js +11 -0
- package/dist/clis/douyin/drafts.d.ts +1 -0
- package/dist/clis/douyin/drafts.js +23 -0
- package/dist/clis/douyin/drafts.test.d.ts +1 -0
- package/dist/clis/douyin/drafts.test.js +11 -0
- package/dist/clis/douyin/hashtag.d.ts +1 -0
- package/dist/clis/douyin/hashtag.js +45 -0
- package/dist/clis/douyin/hashtag.test.d.ts +1 -0
- package/dist/clis/douyin/hashtag.test.js +25 -0
- package/dist/clis/douyin/location.d.ts +1 -0
- package/dist/clis/douyin/location.js +24 -0
- package/dist/clis/douyin/location.test.d.ts +1 -0
- package/dist/clis/douyin/location.test.js +23 -0
- package/dist/clis/douyin/profile.d.ts +1 -0
- package/dist/clis/douyin/profile.js +28 -0
- package/dist/clis/douyin/profile.test.d.ts +1 -0
- package/dist/clis/douyin/profile.test.js +11 -0
- package/dist/clis/douyin/publish.d.ts +14 -0
- package/dist/clis/douyin/publish.js +288 -0
- package/dist/clis/douyin/publish.test.d.ts +1 -0
- package/dist/clis/douyin/publish.test.js +38 -0
- package/dist/clis/douyin/stats.d.ts +1 -0
- package/dist/clis/douyin/stats.js +27 -0
- package/dist/clis/douyin/stats.test.d.ts +1 -0
- package/dist/clis/douyin/stats.test.js +22 -0
- package/dist/clis/douyin/update.d.ts +1 -0
- package/dist/clis/douyin/update.js +31 -0
- package/dist/clis/douyin/update.test.d.ts +1 -0
- package/dist/clis/douyin/update.test.js +11 -0
- package/dist/clis/douyin/videos.d.ts +1 -0
- package/dist/clis/douyin/videos.js +34 -0
- package/dist/clis/douyin/videos.test.d.ts +1 -0
- package/dist/clis/douyin/videos.test.js +11 -0
- package/dist/clis/hackernews/search.yaml +1 -1
- package/dist/clis/imdb/person.d.ts +1 -0
- package/dist/clis/imdb/person.js +203 -0
- package/dist/clis/imdb/reviews.d.ts +1 -0
- package/dist/clis/imdb/reviews.js +88 -0
- package/dist/clis/imdb/search.d.ts +1 -0
- package/dist/clis/imdb/search.js +161 -0
- package/dist/clis/imdb/title.d.ts +1 -0
- package/dist/clis/imdb/title.js +93 -0
- package/dist/clis/imdb/top.d.ts +1 -0
- package/dist/clis/imdb/top.js +53 -0
- package/dist/clis/imdb/trending.d.ts +1 -0
- package/dist/clis/imdb/trending.js +52 -0
- package/dist/clis/imdb/utils.d.ts +46 -0
- package/dist/clis/imdb/utils.js +285 -0
- package/dist/clis/imdb/utils.test.d.ts +1 -0
- package/dist/clis/imdb/utils.test.js +88 -0
- package/dist/clis/instagram/search.yaml +2 -1
- package/dist/clis/jd/item.d.ts +4 -0
- package/dist/clis/jd/item.js +16 -15
- package/dist/clis/jd/item.test.js +16 -1
- package/dist/clis/linux-do/categories.yaml +38 -9
- package/dist/clis/linux-do/category.d.ts +1 -0
- package/dist/clis/linux-do/category.js +36 -0
- package/dist/clis/linux-do/feed.d.ts +45 -0
- package/dist/clis/linux-do/feed.js +397 -0
- package/dist/clis/linux-do/feed.test.d.ts +1 -0
- package/dist/clis/linux-do/feed.test.js +118 -0
- package/dist/clis/linux-do/hot.d.ts +1 -0
- package/dist/clis/linux-do/hot.js +25 -0
- package/dist/clis/linux-do/latest.d.ts +1 -0
- package/dist/clis/linux-do/latest.js +18 -0
- package/dist/clis/linux-do/search.yaml +3 -1
- package/dist/clis/linux-do/tags.yaml +41 -0
- package/dist/clis/linux-do/topic.yaml +41 -3
- package/dist/clis/linux-do/user-posts.yaml +67 -0
- package/dist/clis/linux-do/user-topics.yaml +54 -0
- package/dist/clis/medium/search.js +1 -1
- package/dist/clis/paperreview/commands.test.d.ts +3 -0
- package/dist/clis/paperreview/commands.test.js +243 -0
- package/dist/clis/paperreview/feedback.d.ts +1 -0
- package/dist/clis/paperreview/feedback.js +52 -0
- package/dist/clis/paperreview/review.d.ts +1 -0
- package/dist/clis/paperreview/review.js +37 -0
- package/dist/clis/paperreview/submit.d.ts +1 -0
- package/dist/clis/paperreview/submit.js +85 -0
- package/dist/clis/paperreview/utils.d.ts +46 -0
- package/dist/clis/paperreview/utils.js +197 -0
- package/dist/clis/paperreview/utils.test.d.ts +1 -0
- package/dist/clis/paperreview/utils.test.js +49 -0
- package/dist/clis/producthunt/browse.d.ts +1 -0
- package/dist/clis/producthunt/browse.js +99 -0
- package/dist/clis/producthunt/hot.d.ts +1 -0
- package/dist/clis/producthunt/hot.js +110 -0
- package/dist/clis/producthunt/posts.d.ts +1 -0
- package/dist/clis/producthunt/posts.js +28 -0
- package/dist/clis/producthunt/today.d.ts +1 -0
- package/dist/clis/producthunt/today.js +35 -0
- package/dist/clis/producthunt/utils.d.ts +29 -0
- package/dist/clis/producthunt/utils.js +99 -0
- package/dist/clis/producthunt/utils.test.d.ts +1 -0
- package/dist/clis/producthunt/utils.test.js +64 -0
- package/dist/clis/reuters/search.js +0 -1
- package/dist/clis/twitter/article.js +4 -28
- package/dist/clis/twitter/likes.d.ts +24 -0
- package/dist/clis/twitter/likes.js +217 -0
- package/dist/clis/twitter/likes.test.d.ts +1 -0
- package/dist/clis/twitter/likes.test.js +85 -0
- package/dist/clis/twitter/profile.js +4 -28
- package/dist/clis/twitter/search.js +7 -4
- package/dist/clis/twitter/search.test.js +56 -2
- package/dist/clis/twitter/shared.d.ts +6 -0
- package/dist/clis/twitter/shared.js +35 -0
- package/dist/clis/twitter/timeline.js +2 -13
- package/dist/clis/weibo/comments.d.ts +1 -0
- package/dist/clis/weibo/comments.js +53 -0
- package/dist/clis/weibo/feed.d.ts +1 -0
- package/dist/clis/weibo/feed.js +56 -0
- package/dist/clis/weibo/hot.js +0 -1
- package/dist/clis/weibo/me.d.ts +1 -0
- package/dist/clis/weibo/me.js +76 -0
- package/dist/clis/weibo/post.d.ts +1 -0
- package/dist/clis/weibo/post.js +75 -0
- package/dist/clis/weibo/user.d.ts +1 -0
- package/dist/clis/weibo/user.js +63 -0
- package/dist/clis/weibo/utils.d.ts +6 -0
- package/dist/clis/weibo/utils.js +30 -0
- package/dist/clis/weixin/download.d.ts +17 -0
- package/dist/clis/weixin/download.js +88 -20
- package/dist/clis/weread/book.js +2 -2
- package/dist/clis/weread/commands.test.d.ts +3 -0
- package/dist/clis/weread/commands.test.js +43 -0
- package/dist/clis/weread/highlights.js +2 -2
- package/dist/clis/weread/notebooks.js +2 -2
- package/dist/clis/weread/notes.js +3 -3
- package/dist/clis/weread/search.js +3 -2
- package/dist/clis/weread/shelf.js +2 -2
- package/dist/clis/weread/utils.d.ts +4 -4
- package/dist/clis/weread/utils.js +32 -14
- package/dist/clis/weread/utils.test.js +1 -28
- package/dist/clis/xiaohongshu/comments.d.ts +5 -0
- package/dist/clis/xiaohongshu/comments.js +74 -0
- package/dist/clis/xiaohongshu/comments.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/comments.test.js +79 -0
- package/dist/clis/xiaohongshu/publish.js +114 -18
- package/dist/clis/xiaohongshu/publish.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/publish.test.js +119 -0
- package/dist/clis/xueqiu/search.yaml +2 -1
- package/dist/clis/yahoo-finance/quote.js +0 -1
- package/dist/clis/youtube/channel.d.ts +1 -0
- package/dist/clis/youtube/channel.js +150 -0
- package/dist/clis/youtube/comments.d.ts +1 -0
- package/dist/clis/youtube/comments.js +95 -0
- package/dist/clis/youtube/search.js +0 -1
- package/dist/clis/zhihu/search.yaml +2 -1
- package/dist/commanderAdapter.d.ts +1 -0
- package/dist/commanderAdapter.js +176 -29
- package/dist/commanderAdapter.test.d.ts +1 -0
- package/dist/commanderAdapter.test.js +62 -0
- package/dist/daemon.js +17 -1
- package/dist/discovery.js +8 -14
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +9 -2
- package/dist/download/index.js +63 -51
- package/dist/download/index.test.js +17 -4
- package/dist/errors.d.ts +3 -1
- package/dist/errors.js +15 -32
- package/dist/execution.d.ts +1 -3
- package/dist/execution.js +21 -1
- package/dist/external-clis.yaml +0 -17
- package/dist/hooks.js +2 -0
- package/dist/main.js +5 -0
- package/dist/output.js +5 -1
- package/dist/pipeline/executor.js +3 -4
- package/dist/plugin-manifest.d.ts +70 -0
- package/dist/plugin-manifest.js +160 -0
- package/dist/plugin-manifest.test.d.ts +4 -0
- package/dist/plugin-manifest.test.js +179 -0
- package/dist/plugin.d.ts +38 -5
- package/dist/plugin.js +267 -33
- package/dist/plugin.test.js +220 -3
- package/dist/registry.d.ts +4 -0
- package/dist/registry.js +2 -0
- package/dist/runtime-detect.d.ts +21 -0
- package/dist/runtime-detect.js +32 -0
- package/dist/runtime-detect.test.d.ts +1 -0
- package/dist/runtime-detect.test.js +27 -0
- package/dist/runtime.js +1 -1
- package/dist/serialization.d.ts +2 -0
- package/dist/serialization.js +6 -0
- package/dist/types.d.ts +1 -0
- package/dist/update-check.d.ts +22 -0
- package/dist/update-check.js +112 -0
- package/dist/weixin-download.test.d.ts +1 -0
- package/dist/weixin-download.test.js +30 -0
- package/dist/weread-private-api-regression.test.d.ts +1 -0
- package/dist/weread-private-api-regression.test.js +122 -0
- package/dist/weread-search-regression.test.d.ts +1 -0
- package/dist/weread-search-regression.test.js +39 -0
- package/dist/yaml-schema.d.ts +3 -0
- package/dist/yaml-schema.js +18 -1
- package/docs/.vitepress/config.mts +17 -0
- package/docs/adapters/browser/36kr.md +47 -0
- package/docs/adapters/browser/douban.md +14 -0
- package/docs/adapters/browser/douyin.md +75 -0
- package/docs/adapters/browser/imdb.md +47 -0
- package/docs/adapters/browser/jd.md +2 -2
- package/docs/adapters/browser/linux-do.md +181 -20
- package/docs/adapters/browser/paperreview.md +43 -0
- package/docs/adapters/browser/producthunt.md +49 -0
- package/docs/adapters/browser/twitter.md +6 -0
- package/docs/adapters/desktop/chatgpt.md +5 -0
- package/docs/adapters/index.md +12 -3
- package/docs/advanced/download.md +4 -0
- package/docs/advanced/rate-limiter-plugin.md +99 -0
- package/docs/guide/electron-app-cli.md +200 -0
- package/docs/guide/getting-started.md +1 -0
- package/docs/guide/plugins.md +87 -0
- package/docs/zh/guide/electron-app-cli.md +188 -0
- package/docs/zh/guide/getting-started.md +1 -0
- package/docs/zh/guide/plugins.md +65 -0
- package/extension/dist/background.js +508 -518
- package/extension/manifest.json +6 -2
- package/extension/package.json +2 -1
- package/extension/popup.html +84 -0
- package/extension/popup.js +25 -0
- package/extension/scripts/package-release.mjs +179 -0
- package/extension/src/background.ts +22 -1
- package/package.json +4 -1
- package/scripts/postinstall.js +10 -0
- package/src/browser/cdp.ts +2 -1
- package/src/browser/discover.ts +8 -3
- package/src/browser/errors.ts +13 -14
- package/src/browser/mcp.ts +2 -1
- package/src/build-manifest.test.ts +23 -0
- package/src/build-manifest.ts +40 -15
- package/src/capabilityRouting.ts +2 -1
- package/src/cli.ts +35 -3
- package/src/clis/36kr/article.ts +69 -0
- package/src/clis/36kr/hot.test.ts +19 -0
- package/src/clis/36kr/hot.ts +100 -0
- package/src/clis/36kr/news.test.ts +90 -0
- package/src/clis/36kr/news.ts +54 -0
- package/src/clis/36kr/search.ts +78 -0
- package/src/clis/apple-podcasts/search.ts +2 -1
- package/src/clis/arxiv/search.ts +2 -2
- package/src/clis/bbc/news.ts +0 -1
- package/src/clis/bilibili/comments.test.ts +102 -0
- package/src/clis/bilibili/comments.ts +44 -0
- package/src/clis/chatgpt/ask.ts +28 -14
- package/src/clis/chatgpt/ax.ts +180 -1
- package/src/clis/chatgpt/model.ts +27 -0
- package/src/clis/chatgpt/send.ts +16 -6
- package/src/clis/ctrip/search.ts +0 -1
- package/src/clis/douban/download.test.ts +196 -0
- package/src/clis/douban/download.ts +78 -0
- package/src/clis/douban/photos.ts +36 -0
- package/src/clis/douban/utils.test.ts +97 -0
- package/src/clis/douban/utils.ts +232 -1
- package/src/clis/douyin/_shared/browser-fetch.test.ts +38 -0
- package/src/clis/douyin/_shared/browser-fetch.ts +45 -0
- package/src/clis/douyin/_shared/creation-id.test.ts +26 -0
- package/src/clis/douyin/_shared/creation-id.ts +8 -0
- package/src/clis/douyin/_shared/imagex-upload.test.ts +113 -0
- package/src/clis/douyin/_shared/imagex-upload.ts +76 -0
- package/src/clis/douyin/_shared/sts2.ts +20 -0
- package/src/clis/douyin/_shared/text-extra.test.ts +42 -0
- package/src/clis/douyin/_shared/text-extra.ts +33 -0
- package/src/clis/douyin/_shared/timing.test.ts +38 -0
- package/src/clis/douyin/_shared/timing.ts +22 -0
- package/src/clis/douyin/_shared/tos-upload-short-read.test.ts +102 -0
- package/src/clis/douyin/_shared/tos-upload.test.ts +281 -0
- package/src/clis/douyin/_shared/tos-upload.ts +444 -0
- package/src/clis/douyin/_shared/transcode.test.ts +117 -0
- package/src/clis/douyin/_shared/transcode.ts +78 -0
- package/src/clis/douyin/_shared/types.ts +29 -0
- package/src/clis/douyin/activities.test.ts +25 -0
- package/src/clis/douyin/activities.ts +23 -0
- package/src/clis/douyin/collections.test.ts +26 -0
- package/src/clis/douyin/collections.ts +25 -0
- package/src/clis/douyin/delete.test.ts +12 -0
- package/src/clis/douyin/delete.ts +20 -0
- package/src/clis/douyin/draft.test.ts +12 -0
- package/src/clis/douyin/draft.ts +282 -0
- package/src/clis/douyin/drafts.test.ts +12 -0
- package/src/clis/douyin/drafts.ts +27 -0
- package/src/clis/douyin/hashtag.test.ts +28 -0
- package/src/clis/douyin/hashtag.ts +56 -0
- package/src/clis/douyin/location.test.ts +26 -0
- package/src/clis/douyin/location.ts +27 -0
- package/src/clis/douyin/profile.test.ts +12 -0
- package/src/clis/douyin/profile.ts +37 -0
- package/src/clis/douyin/publish.test.ts +45 -0
- package/src/clis/douyin/publish.ts +340 -0
- package/src/clis/douyin/stats.test.ts +25 -0
- package/src/clis/douyin/stats.ts +30 -0
- package/src/clis/douyin/update.test.ts +12 -0
- package/src/clis/douyin/update.ts +43 -0
- package/src/clis/douyin/videos.test.ts +12 -0
- package/src/clis/douyin/videos.ts +49 -0
- package/src/clis/hackernews/search.yaml +1 -1
- package/src/clis/imdb/person.ts +232 -0
- package/src/clis/imdb/reviews.ts +111 -0
- package/src/clis/imdb/search.ts +179 -0
- package/src/clis/imdb/title.ts +121 -0
- package/src/clis/imdb/top.ts +67 -0
- package/src/clis/imdb/trending.ts +66 -0
- package/src/clis/imdb/utils.test.ts +117 -0
- package/src/clis/imdb/utils.ts +305 -0
- package/src/clis/instagram/search.yaml +2 -1
- package/src/clis/jd/item.test.ts +18 -1
- package/src/clis/jd/item.ts +18 -15
- package/src/clis/linux-do/categories.yaml +38 -9
- package/src/clis/linux-do/category.ts +37 -0
- package/src/clis/linux-do/feed.test.ts +132 -0
- package/src/clis/linux-do/feed.ts +501 -0
- package/src/clis/linux-do/hot.ts +26 -0
- package/src/clis/linux-do/latest.ts +19 -0
- package/src/clis/linux-do/search.yaml +3 -1
- package/src/clis/linux-do/tags.yaml +41 -0
- package/src/clis/linux-do/topic.yaml +41 -3
- package/src/clis/linux-do/user-posts.yaml +67 -0
- package/src/clis/linux-do/user-topics.yaml +54 -0
- package/src/clis/medium/search.ts +1 -1
- package/src/clis/paperreview/commands.test.ts +283 -0
- package/src/clis/paperreview/feedback.ts +64 -0
- package/src/clis/paperreview/review.ts +47 -0
- package/src/clis/paperreview/submit.ts +119 -0
- package/src/clis/paperreview/utils.test.ts +68 -0
- package/src/clis/paperreview/utils.ts +276 -0
- package/src/clis/producthunt/browse.ts +109 -0
- package/src/clis/producthunt/hot.ts +127 -0
- package/src/clis/producthunt/posts.ts +29 -0
- package/src/clis/producthunt/today.ts +37 -0
- package/src/clis/producthunt/utils.test.ts +72 -0
- package/src/clis/producthunt/utils.ts +122 -0
- package/src/clis/reuters/search.ts +0 -1
- package/src/clis/twitter/article.ts +5 -28
- package/src/clis/twitter/likes.test.ts +91 -0
- package/src/clis/twitter/likes.ts +256 -0
- package/src/clis/twitter/profile.ts +5 -28
- package/src/clis/twitter/search.test.ts +71 -2
- package/src/clis/twitter/search.ts +8 -4
- package/src/clis/twitter/shared.ts +45 -0
- package/src/clis/twitter/timeline.ts +2 -13
- package/src/clis/weibo/comments.ts +54 -0
- package/src/clis/weibo/feed.ts +57 -0
- package/src/clis/weibo/hot.ts +0 -1
- package/src/clis/weibo/me.ts +77 -0
- package/src/clis/weibo/post.ts +77 -0
- package/src/clis/weibo/user.ts +64 -0
- package/src/clis/weibo/utils.ts +32 -0
- package/src/clis/weixin/download.ts +114 -20
- package/src/clis/weread/book.ts +2 -2
- package/src/clis/weread/commands.test.ts +57 -0
- package/src/clis/weread/highlights.ts +2 -2
- package/src/clis/weread/notebooks.ts +2 -2
- package/src/clis/weread/notes.ts +3 -3
- package/src/clis/weread/search.ts +3 -2
- package/src/clis/weread/shelf.ts +2 -2
- package/src/clis/weread/utils.test.ts +1 -32
- package/src/clis/weread/utils.ts +41 -16
- package/src/clis/xiaohongshu/comments.test.ts +96 -0
- package/src/clis/xiaohongshu/comments.ts +81 -0
- package/src/clis/xiaohongshu/publish.test.ts +137 -0
- package/src/clis/xiaohongshu/publish.ts +129 -18
- package/src/clis/xueqiu/search.yaml +2 -1
- package/src/clis/yahoo-finance/quote.ts +0 -1
- package/src/clis/youtube/channel.ts +155 -0
- package/src/clis/youtube/comments.ts +97 -0
- package/src/clis/youtube/search.ts +0 -1
- package/src/clis/zhihu/search.yaml +2 -1
- package/src/commanderAdapter.test.ts +78 -0
- package/src/commanderAdapter.ts +188 -24
- package/src/daemon.ts +19 -1
- package/src/discovery.ts +8 -15
- package/src/doctor.ts +13 -2
- package/src/download/index.test.ts +14 -4
- package/src/download/index.ts +67 -55
- package/src/errors.ts +25 -66
- package/src/execution.ts +28 -3
- package/src/external-clis.yaml +0 -17
- package/src/hooks.ts +1 -0
- package/src/main.ts +6 -0
- package/src/output.ts +3 -1
- package/src/pipeline/executor.ts +4 -6
- package/src/plugin-manifest.test.ts +223 -0
- package/src/plugin-manifest.ts +206 -0
- package/src/plugin.test.ts +246 -2
- package/src/plugin.ts +338 -36
- package/src/registry.ts +6 -1
- package/src/runtime-detect.test.ts +30 -0
- package/src/runtime-detect.ts +36 -0
- package/src/runtime.ts +1 -1
- package/src/serialization.ts +4 -0
- package/src/types.ts +1 -0
- package/src/update-check.ts +114 -0
- package/src/weixin-download.test.ts +64 -0
- package/src/weread-private-api-regression.test.ts +150 -0
- package/src/weread-search-regression.test.ts +44 -0
- package/src/yaml-schema.ts +20 -0
- package/tests/e2e/browser-auth.test.ts +13 -9
- package/tests/e2e/browser-public-extended.test.ts +162 -0
- package/tests/e2e/browser-public.test.ts +55 -136
- package/tests/e2e/helpers.ts +2 -1
- package/tests/e2e/public-commands.test.ts +37 -3
- package/tests/smoke/api-health.test.ts +1 -1
- package/vitest.config.ts +34 -17
- package/dist/clis/linux-do/category.yaml +0 -51
- package/dist/clis/linux-do/hot.yaml +0 -50
- package/dist/clis/linux-do/latest.yaml +0 -40
- package/src/clis/linux-do/category.yaml +0 -51
- package/src/clis/linux-do/hot.yaml +0 -50
- package/src/clis/linux-do/latest.yaml +0 -40
|
@@ -1,582 +1,572 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/** Max reconnect delay (ms) */
|
|
10
|
-
var WS_RECONNECT_MAX_DELAY = 6e4;
|
|
11
|
-
//#endregion
|
|
12
|
-
//#region src/cdp.ts
|
|
13
|
-
/**
|
|
14
|
-
* CDP execution via chrome.debugger API.
|
|
15
|
-
*
|
|
16
|
-
* chrome.debugger only needs the "debugger" permission — no host_permissions.
|
|
17
|
-
* It can attach to any http/https tab. Avoid chrome:// and chrome-extension://
|
|
18
|
-
* tabs (resolveTabId in background.ts filters them).
|
|
19
|
-
*/
|
|
20
|
-
var attached = /* @__PURE__ */ new Set();
|
|
21
|
-
/** Check if a URL can be attached via CDP */
|
|
1
|
+
const DAEMON_PORT = 19825;
|
|
2
|
+
const DAEMON_HOST = "localhost";
|
|
3
|
+
const DAEMON_WS_URL = `ws://${DAEMON_HOST}:${DAEMON_PORT}/ext`;
|
|
4
|
+
const WS_RECONNECT_BASE_DELAY = 2e3;
|
|
5
|
+
const WS_RECONNECT_MAX_DELAY = 6e4;
|
|
6
|
+
|
|
7
|
+
const attached = /* @__PURE__ */ new Set();
|
|
8
|
+
const BLANK_PAGE$1 = "data:text/html,<html></html>";
|
|
22
9
|
function isDebuggableUrl$1(url) {
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
if (!url) return true;
|
|
11
|
+
return url.startsWith("http://") || url.startsWith("https://") || url === BLANK_PAGE$1;
|
|
25
12
|
}
|
|
26
13
|
async function ensureAttached(tabId) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
14
|
+
try {
|
|
15
|
+
const tab = await chrome.tabs.get(tabId);
|
|
16
|
+
if (!isDebuggableUrl$1(tab.url)) {
|
|
17
|
+
attached.delete(tabId);
|
|
18
|
+
throw new Error(`Cannot debug tab ${tabId}: URL is ${tab.url ?? "unknown"}`);
|
|
19
|
+
}
|
|
20
|
+
} catch (e) {
|
|
21
|
+
if (e instanceof Error && e.message.startsWith("Cannot debug tab")) throw e;
|
|
22
|
+
attached.delete(tabId);
|
|
23
|
+
throw new Error(`Tab ${tabId} no longer exists`);
|
|
24
|
+
}
|
|
25
|
+
if (attached.has(tabId)) {
|
|
26
|
+
try {
|
|
27
|
+
await chrome.debugger.sendCommand({ tabId }, "Runtime.evaluate", {
|
|
28
|
+
expression: "1",
|
|
29
|
+
returnByValue: true
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
} catch {
|
|
33
|
+
attached.delete(tabId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
await chrome.debugger.attach({ tabId }, "1.3");
|
|
38
|
+
} catch (e) {
|
|
39
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
40
|
+
const hint = msg.includes("chrome-extension://") ? ". Tip: another Chrome extension may be interfering — try disabling other extensions" : "";
|
|
41
|
+
if (msg.includes("Another debugger is already attached")) {
|
|
42
|
+
try {
|
|
43
|
+
await chrome.debugger.detach({ tabId });
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
await chrome.debugger.attach({ tabId }, "1.3");
|
|
48
|
+
} catch {
|
|
49
|
+
throw new Error(`attach failed: ${msg}${hint}`);
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
throw new Error(`attach failed: ${msg}${hint}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
attached.add(tabId);
|
|
56
|
+
try {
|
|
57
|
+
await chrome.debugger.sendCommand({ tabId }, "Runtime.enable");
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
67
60
|
}
|
|
68
61
|
async function evaluate(tabId, expression) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
62
|
+
await ensureAttached(tabId);
|
|
63
|
+
const result = await chrome.debugger.sendCommand({ tabId }, "Runtime.evaluate", {
|
|
64
|
+
expression,
|
|
65
|
+
returnByValue: true,
|
|
66
|
+
awaitPromise: true
|
|
67
|
+
});
|
|
68
|
+
if (result.exceptionDetails) {
|
|
69
|
+
const errMsg = result.exceptionDetails.exception?.description || result.exceptionDetails.text || "Eval error";
|
|
70
|
+
throw new Error(errMsg);
|
|
71
|
+
}
|
|
72
|
+
return result.result?.value;
|
|
80
73
|
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Capture a screenshot via CDP Page.captureScreenshot.
|
|
84
|
-
* Returns base64-encoded image data.
|
|
85
|
-
*/
|
|
74
|
+
const evaluateAsync = evaluate;
|
|
86
75
|
async function screenshot(tabId, options = {}) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
76
|
+
await ensureAttached(tabId);
|
|
77
|
+
const format = options.format ?? "png";
|
|
78
|
+
if (options.fullPage) {
|
|
79
|
+
const metrics = await chrome.debugger.sendCommand({ tabId }, "Page.getLayoutMetrics");
|
|
80
|
+
const size = metrics.cssContentSize || metrics.contentSize;
|
|
81
|
+
if (size) {
|
|
82
|
+
await chrome.debugger.sendCommand({ tabId }, "Emulation.setDeviceMetricsOverride", {
|
|
83
|
+
mobile: false,
|
|
84
|
+
width: Math.ceil(size.width),
|
|
85
|
+
height: Math.ceil(size.height),
|
|
86
|
+
deviceScaleFactor: 1
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const params = { format };
|
|
92
|
+
if (format === "jpeg" && options.quality !== void 0) {
|
|
93
|
+
params.quality = Math.max(0, Math.min(100, options.quality));
|
|
94
|
+
}
|
|
95
|
+
const result = await chrome.debugger.sendCommand({ tabId }, "Page.captureScreenshot", params);
|
|
96
|
+
return result.data;
|
|
97
|
+
} finally {
|
|
98
|
+
if (options.fullPage) {
|
|
99
|
+
await chrome.debugger.sendCommand({ tabId }, "Emulation.clearDeviceMetricsOverride").catch(() => {
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
106
103
|
}
|
|
107
104
|
async function detach(tabId) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
if (!attached.has(tabId)) return;
|
|
106
|
+
attached.delete(tabId);
|
|
107
|
+
try {
|
|
108
|
+
await chrome.debugger.detach({ tabId });
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
113
111
|
}
|
|
114
112
|
function registerListeners() {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
113
|
+
chrome.tabs.onRemoved.addListener((tabId) => {
|
|
114
|
+
attached.delete(tabId);
|
|
115
|
+
});
|
|
116
|
+
chrome.debugger.onDetach.addListener((source) => {
|
|
117
|
+
if (source.tabId) attached.delete(source.tabId);
|
|
118
|
+
});
|
|
119
|
+
chrome.tabs.onUpdated.addListener(async (tabId, info) => {
|
|
120
|
+
if (info.url && !isDebuggableUrl$1(info.url)) {
|
|
121
|
+
await detach(tabId);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
124
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
var _origError = console.error.bind(console);
|
|
125
|
+
|
|
126
|
+
let ws = null;
|
|
127
|
+
let reconnectTimer = null;
|
|
128
|
+
let reconnectAttempts = 0;
|
|
129
|
+
const _origLog = console.log.bind(console);
|
|
130
|
+
const _origWarn = console.warn.bind(console);
|
|
131
|
+
const _origError = console.error.bind(console);
|
|
133
132
|
function forwardLog(level, args) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
msg,
|
|
141
|
-
ts: Date.now()
|
|
142
|
-
}));
|
|
143
|
-
} catch {}
|
|
133
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
|
134
|
+
try {
|
|
135
|
+
const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
136
|
+
ws.send(JSON.stringify({ type: "log", level, msg, ts: Date.now() }));
|
|
137
|
+
} catch {
|
|
138
|
+
}
|
|
144
139
|
}
|
|
145
140
|
console.log = (...args) => {
|
|
146
|
-
|
|
147
|
-
|
|
141
|
+
_origLog(...args);
|
|
142
|
+
forwardLog("info", args);
|
|
148
143
|
};
|
|
149
144
|
console.warn = (...args) => {
|
|
150
|
-
|
|
151
|
-
|
|
145
|
+
_origWarn(...args);
|
|
146
|
+
forwardLog("warn", args);
|
|
152
147
|
};
|
|
153
148
|
console.error = (...args) => {
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
_origError(...args);
|
|
150
|
+
forwardLog("error", args);
|
|
156
151
|
};
|
|
157
152
|
function connect() {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
153
|
+
if (ws?.readyState === WebSocket.OPEN || ws?.readyState === WebSocket.CONNECTING) return;
|
|
154
|
+
try {
|
|
155
|
+
ws = new WebSocket(DAEMON_WS_URL);
|
|
156
|
+
} catch {
|
|
157
|
+
scheduleReconnect();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
ws.onopen = () => {
|
|
161
|
+
console.log("[opencli] Connected to daemon");
|
|
162
|
+
reconnectAttempts = 0;
|
|
163
|
+
if (reconnectTimer) {
|
|
164
|
+
clearTimeout(reconnectTimer);
|
|
165
|
+
reconnectTimer = null;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
ws.onmessage = async (event) => {
|
|
169
|
+
try {
|
|
170
|
+
const command = JSON.parse(event.data);
|
|
171
|
+
const result = await handleCommand(command);
|
|
172
|
+
ws?.send(JSON.stringify(result));
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error("[opencli] Message handling error:", err);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
ws.onclose = () => {
|
|
178
|
+
console.log("[opencli] Disconnected from daemon");
|
|
179
|
+
ws = null;
|
|
180
|
+
scheduleReconnect();
|
|
181
|
+
};
|
|
182
|
+
ws.onerror = () => {
|
|
183
|
+
ws?.close();
|
|
184
|
+
};
|
|
189
185
|
}
|
|
186
|
+
const MAX_EAGER_ATTEMPTS = 6;
|
|
190
187
|
function scheduleReconnect() {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
188
|
+
if (reconnectTimer) return;
|
|
189
|
+
reconnectAttempts++;
|
|
190
|
+
if (reconnectAttempts > MAX_EAGER_ATTEMPTS) return;
|
|
191
|
+
const delay = Math.min(WS_RECONNECT_BASE_DELAY * Math.pow(2, reconnectAttempts - 1), WS_RECONNECT_MAX_DELAY);
|
|
192
|
+
reconnectTimer = setTimeout(() => {
|
|
193
|
+
reconnectTimer = null;
|
|
194
|
+
connect();
|
|
195
|
+
}, delay);
|
|
198
196
|
}
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
const automationSessions = /* @__PURE__ */ new Map();
|
|
198
|
+
const WINDOW_IDLE_TIMEOUT = 12e4;
|
|
201
199
|
function getWorkspaceKey(workspace) {
|
|
202
|
-
|
|
200
|
+
return workspace?.trim() || "default";
|
|
203
201
|
}
|
|
204
202
|
function resetWindowIdleTimer(workspace) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
203
|
+
const session = automationSessions.get(workspace);
|
|
204
|
+
if (!session) return;
|
|
205
|
+
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
206
|
+
session.idleDeadlineAt = Date.now() + WINDOW_IDLE_TIMEOUT;
|
|
207
|
+
session.idleTimer = setTimeout(async () => {
|
|
208
|
+
const current = automationSessions.get(workspace);
|
|
209
|
+
if (!current) return;
|
|
210
|
+
try {
|
|
211
|
+
await chrome.windows.remove(current.windowId);
|
|
212
|
+
console.log(`[opencli] Automation window ${current.windowId} (${workspace}) closed (idle timeout)`);
|
|
213
|
+
} catch {
|
|
214
|
+
}
|
|
215
|
+
automationSessions.delete(workspace);
|
|
216
|
+
}, WINDOW_IDLE_TIMEOUT);
|
|
218
217
|
}
|
|
219
|
-
/** Get or create the dedicated automation window. */
|
|
220
218
|
async function getAutomationWindow(workspace) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
219
|
+
const existing = automationSessions.get(workspace);
|
|
220
|
+
if (existing) {
|
|
221
|
+
try {
|
|
222
|
+
await chrome.windows.get(existing.windowId);
|
|
223
|
+
return existing.windowId;
|
|
224
|
+
} catch {
|
|
225
|
+
automationSessions.delete(workspace);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const win = await chrome.windows.create({
|
|
229
|
+
url: BLANK_PAGE,
|
|
230
|
+
focused: false,
|
|
231
|
+
width: 1280,
|
|
232
|
+
height: 900,
|
|
233
|
+
type: "normal"
|
|
234
|
+
});
|
|
235
|
+
const session = {
|
|
236
|
+
windowId: win.id,
|
|
237
|
+
idleTimer: null,
|
|
238
|
+
idleDeadlineAt: Date.now() + WINDOW_IDLE_TIMEOUT
|
|
239
|
+
};
|
|
240
|
+
automationSessions.set(workspace, session);
|
|
241
|
+
console.log(`[opencli] Created automation window ${session.windowId} (${workspace})`);
|
|
242
|
+
resetWindowIdleTimer(workspace);
|
|
243
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
244
|
+
return session.windowId;
|
|
244
245
|
}
|
|
245
246
|
chrome.windows.onRemoved.addListener((windowId) => {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
for (const [workspace, session] of automationSessions.entries()) {
|
|
248
|
+
if (session.windowId === windowId) {
|
|
249
|
+
console.log(`[opencli] Automation window closed (${workspace})`);
|
|
250
|
+
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
251
|
+
automationSessions.delete(workspace);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
251
254
|
});
|
|
252
|
-
|
|
255
|
+
let initialized = false;
|
|
253
256
|
function initialize() {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
if (initialized) return;
|
|
258
|
+
initialized = true;
|
|
259
|
+
chrome.alarms.create("keepalive", { periodInMinutes: 0.4 });
|
|
260
|
+
registerListeners();
|
|
261
|
+
connect();
|
|
262
|
+
console.log("[opencli] OpenCLI extension initialized");
|
|
260
263
|
}
|
|
261
264
|
chrome.runtime.onInstalled.addListener(() => {
|
|
262
|
-
|
|
265
|
+
initialize();
|
|
263
266
|
});
|
|
264
267
|
chrome.runtime.onStartup.addListener(() => {
|
|
265
|
-
|
|
268
|
+
initialize();
|
|
266
269
|
});
|
|
267
270
|
chrome.alarms.onAlarm.addListener((alarm) => {
|
|
268
|
-
|
|
271
|
+
if (alarm.name === "keepalive") connect();
|
|
272
|
+
});
|
|
273
|
+
chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
|
|
274
|
+
if (msg?.type === "getStatus") {
|
|
275
|
+
sendResponse({
|
|
276
|
+
connected: ws?.readyState === WebSocket.OPEN,
|
|
277
|
+
reconnecting: reconnectTimer !== null
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
return false;
|
|
269
281
|
});
|
|
270
282
|
async function handleCommand(cmd) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
283
|
+
const workspace = getWorkspaceKey(cmd.workspace);
|
|
284
|
+
resetWindowIdleTimer(workspace);
|
|
285
|
+
try {
|
|
286
|
+
switch (cmd.action) {
|
|
287
|
+
case "exec":
|
|
288
|
+
return await handleExec(cmd, workspace);
|
|
289
|
+
case "navigate":
|
|
290
|
+
return await handleNavigate(cmd, workspace);
|
|
291
|
+
case "tabs":
|
|
292
|
+
return await handleTabs(cmd, workspace);
|
|
293
|
+
case "cookies":
|
|
294
|
+
return await handleCookies(cmd);
|
|
295
|
+
case "screenshot":
|
|
296
|
+
return await handleScreenshot(cmd, workspace);
|
|
297
|
+
case "close-window":
|
|
298
|
+
return await handleCloseWindow(cmd, workspace);
|
|
299
|
+
case "sessions":
|
|
300
|
+
return await handleSessions(cmd);
|
|
301
|
+
default:
|
|
302
|
+
return { id: cmd.id, ok: false, error: `Unknown action: ${cmd.action}` };
|
|
303
|
+
}
|
|
304
|
+
} catch (err) {
|
|
305
|
+
return {
|
|
306
|
+
id: cmd.id,
|
|
307
|
+
ok: false,
|
|
308
|
+
error: err instanceof Error ? err.message : String(err)
|
|
309
|
+
};
|
|
310
|
+
}
|
|
295
311
|
}
|
|
296
|
-
|
|
312
|
+
const BLANK_PAGE = "data:text/html,<html></html>";
|
|
297
313
|
function isDebuggableUrl(url) {
|
|
298
|
-
|
|
299
|
-
|
|
314
|
+
if (!url) return true;
|
|
315
|
+
return url.startsWith("http://") || url.startsWith("https://") || url === BLANK_PAGE;
|
|
316
|
+
}
|
|
317
|
+
function isSafeNavigationUrl(url) {
|
|
318
|
+
return url.startsWith("http://") || url.startsWith("https://");
|
|
319
|
+
}
|
|
320
|
+
function normalizeUrlForComparison(url) {
|
|
321
|
+
if (!url) return "";
|
|
322
|
+
try {
|
|
323
|
+
const parsed = new URL(url);
|
|
324
|
+
if (parsed.protocol === "https:" && parsed.port === "443" || parsed.protocol === "http:" && parsed.port === "80") {
|
|
325
|
+
parsed.port = "";
|
|
326
|
+
}
|
|
327
|
+
const pathname = parsed.pathname === "/" ? "" : parsed.pathname;
|
|
328
|
+
return `${parsed.protocol}//${parsed.host}${pathname}${parsed.search}${parsed.hash}`;
|
|
329
|
+
} catch {
|
|
330
|
+
return url;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function isTargetUrl(currentUrl, targetUrl) {
|
|
334
|
+
return normalizeUrlForComparison(currentUrl) === normalizeUrlForComparison(targetUrl);
|
|
300
335
|
}
|
|
301
|
-
/**
|
|
302
|
-
* Resolve target tab in the automation window.
|
|
303
|
-
* If explicit tabId is given, use that directly.
|
|
304
|
-
* Otherwise, find or create a tab in the dedicated automation window.
|
|
305
|
-
*/
|
|
306
336
|
async function resolveTabId(tabId, workspace) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
337
|
+
if (tabId !== void 0) {
|
|
338
|
+
try {
|
|
339
|
+
const tab = await chrome.tabs.get(tabId);
|
|
340
|
+
const session = automationSessions.get(workspace);
|
|
341
|
+
if (isDebuggableUrl(tab.url) && session && tab.windowId === session.windowId) return tabId;
|
|
342
|
+
if (session && tab.windowId !== session.windowId) {
|
|
343
|
+
console.warn(`[opencli] Tab ${tabId} belongs to window ${tab.windowId}, not automation window ${session.windowId}, re-resolving`);
|
|
344
|
+
} else if (!isDebuggableUrl(tab.url)) {
|
|
345
|
+
console.warn(`[opencli] Tab ${tabId} URL is not debuggable (${tab.url}), re-resolving`);
|
|
346
|
+
}
|
|
347
|
+
} catch {
|
|
348
|
+
console.warn(`[opencli] Tab ${tabId} no longer exists, re-resolving`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const windowId = await getAutomationWindow(workspace);
|
|
352
|
+
const tabs = await chrome.tabs.query({ windowId });
|
|
353
|
+
const debuggableTab = tabs.find((t) => t.id && isDebuggableUrl(t.url));
|
|
354
|
+
if (debuggableTab?.id) return debuggableTab.id;
|
|
355
|
+
const reuseTab = tabs.find((t) => t.id);
|
|
356
|
+
if (reuseTab?.id) {
|
|
357
|
+
await chrome.tabs.update(reuseTab.id, { url: BLANK_PAGE });
|
|
358
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
359
|
+
try {
|
|
360
|
+
const updated = await chrome.tabs.get(reuseTab.id);
|
|
361
|
+
if (isDebuggableUrl(updated.url)) return reuseTab.id;
|
|
362
|
+
console.warn(`[opencli] data: URI was intercepted (${updated.url}), creating fresh tab`);
|
|
363
|
+
} catch {
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const newTab = await chrome.tabs.create({ windowId, url: BLANK_PAGE, active: true });
|
|
367
|
+
if (!newTab.id) throw new Error("Failed to create tab in automation window");
|
|
368
|
+
return newTab.id;
|
|
335
369
|
}
|
|
336
370
|
async function listAutomationTabs(workspace) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
371
|
+
const session = automationSessions.get(workspace);
|
|
372
|
+
if (!session) return [];
|
|
373
|
+
try {
|
|
374
|
+
return await chrome.tabs.query({ windowId: session.windowId });
|
|
375
|
+
} catch {
|
|
376
|
+
automationSessions.delete(workspace);
|
|
377
|
+
return [];
|
|
378
|
+
}
|
|
345
379
|
}
|
|
346
380
|
async function listAutomationWebTabs(workspace) {
|
|
347
|
-
|
|
381
|
+
const tabs = await listAutomationTabs(workspace);
|
|
382
|
+
return tabs.filter((tab) => isDebuggableUrl(tab.url));
|
|
348
383
|
}
|
|
349
384
|
async function handleExec(cmd, workspace) {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
return {
|
|
359
|
-
id: cmd.id,
|
|
360
|
-
ok: true,
|
|
361
|
-
data
|
|
362
|
-
};
|
|
363
|
-
} catch (err) {
|
|
364
|
-
return {
|
|
365
|
-
id: cmd.id,
|
|
366
|
-
ok: false,
|
|
367
|
-
error: err instanceof Error ? err.message : String(err)
|
|
368
|
-
};
|
|
369
|
-
}
|
|
385
|
+
if (!cmd.code) return { id: cmd.id, ok: false, error: "Missing code" };
|
|
386
|
+
const tabId = await resolveTabId(cmd.tabId, workspace);
|
|
387
|
+
try {
|
|
388
|
+
const data = await evaluateAsync(tabId, cmd.code);
|
|
389
|
+
return { id: cmd.id, ok: true, data };
|
|
390
|
+
} catch (err) {
|
|
391
|
+
return { id: cmd.id, ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
392
|
+
}
|
|
370
393
|
}
|
|
371
394
|
async function handleNavigate(cmd, workspace) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
395
|
+
if (!cmd.url) return { id: cmd.id, ok: false, error: "Missing url" };
|
|
396
|
+
if (!isSafeNavigationUrl(cmd.url)) {
|
|
397
|
+
return { id: cmd.id, ok: false, error: "Blocked URL scheme -- only http:// and https:// are allowed" };
|
|
398
|
+
}
|
|
399
|
+
const tabId = await resolveTabId(cmd.tabId, workspace);
|
|
400
|
+
const beforeTab = await chrome.tabs.get(tabId);
|
|
401
|
+
const beforeNormalized = normalizeUrlForComparison(beforeTab.url);
|
|
402
|
+
const targetUrl = cmd.url;
|
|
403
|
+
if (beforeTab.status === "complete" && isTargetUrl(beforeTab.url, targetUrl)) {
|
|
404
|
+
return {
|
|
405
|
+
id: cmd.id,
|
|
406
|
+
ok: true,
|
|
407
|
+
data: { title: beforeTab.title, url: beforeTab.url, tabId, timedOut: false }
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
await detach(tabId);
|
|
411
|
+
await chrome.tabs.update(tabId, { url: targetUrl });
|
|
412
|
+
let timedOut = false;
|
|
413
|
+
await new Promise((resolve) => {
|
|
414
|
+
let settled = false;
|
|
415
|
+
let checkTimer = null;
|
|
416
|
+
let timeoutTimer = null;
|
|
417
|
+
const finish = () => {
|
|
418
|
+
if (settled) return;
|
|
419
|
+
settled = true;
|
|
420
|
+
chrome.tabs.onUpdated.removeListener(listener);
|
|
421
|
+
if (checkTimer) clearTimeout(checkTimer);
|
|
422
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
423
|
+
resolve();
|
|
424
|
+
};
|
|
425
|
+
const isNavigationDone = (url) => {
|
|
426
|
+
return isTargetUrl(url, targetUrl) || normalizeUrlForComparison(url) !== beforeNormalized;
|
|
427
|
+
};
|
|
428
|
+
const listener = (id, info, tab2) => {
|
|
429
|
+
if (id !== tabId) return;
|
|
430
|
+
if (info.status === "complete" && isNavigationDone(tab2.url ?? info.url)) {
|
|
431
|
+
finish();
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
chrome.tabs.onUpdated.addListener(listener);
|
|
435
|
+
checkTimer = setTimeout(async () => {
|
|
436
|
+
try {
|
|
437
|
+
const currentTab = await chrome.tabs.get(tabId);
|
|
438
|
+
if (currentTab.status === "complete" && isNavigationDone(currentTab.url)) {
|
|
439
|
+
finish();
|
|
440
|
+
}
|
|
441
|
+
} catch {
|
|
442
|
+
}
|
|
443
|
+
}, 100);
|
|
444
|
+
timeoutTimer = setTimeout(() => {
|
|
445
|
+
timedOut = true;
|
|
446
|
+
console.warn(`[opencli] Navigate to ${targetUrl} timed out after 15s`);
|
|
447
|
+
finish();
|
|
448
|
+
}, 15e3);
|
|
449
|
+
});
|
|
450
|
+
const tab = await chrome.tabs.get(tabId);
|
|
451
|
+
return {
|
|
452
|
+
id: cmd.id,
|
|
453
|
+
ok: true,
|
|
454
|
+
data: { title: tab.title, url: tab.url, tabId, timedOut }
|
|
455
|
+
};
|
|
421
456
|
}
|
|
422
457
|
async function handleTabs(cmd, workspace) {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
error: "Missing index or tabId"
|
|
484
|
-
};
|
|
485
|
-
if (cmd.tabId !== void 0) {
|
|
486
|
-
await chrome.tabs.update(cmd.tabId, { active: true });
|
|
487
|
-
return {
|
|
488
|
-
id: cmd.id,
|
|
489
|
-
ok: true,
|
|
490
|
-
data: { selected: cmd.tabId }
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
const target = (await listAutomationWebTabs(workspace))[cmd.index];
|
|
494
|
-
if (!target?.id) return {
|
|
495
|
-
id: cmd.id,
|
|
496
|
-
ok: false,
|
|
497
|
-
error: `Tab index ${cmd.index} not found`
|
|
498
|
-
};
|
|
499
|
-
await chrome.tabs.update(target.id, { active: true });
|
|
500
|
-
return {
|
|
501
|
-
id: cmd.id,
|
|
502
|
-
ok: true,
|
|
503
|
-
data: { selected: target.id }
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
default: return {
|
|
507
|
-
id: cmd.id,
|
|
508
|
-
ok: false,
|
|
509
|
-
error: `Unknown tabs op: ${cmd.op}`
|
|
510
|
-
};
|
|
511
|
-
}
|
|
458
|
+
switch (cmd.op) {
|
|
459
|
+
case "list": {
|
|
460
|
+
const tabs = await listAutomationWebTabs(workspace);
|
|
461
|
+
const data = tabs.map((t, i) => ({
|
|
462
|
+
index: i,
|
|
463
|
+
tabId: t.id,
|
|
464
|
+
url: t.url,
|
|
465
|
+
title: t.title,
|
|
466
|
+
active: t.active
|
|
467
|
+
}));
|
|
468
|
+
return { id: cmd.id, ok: true, data };
|
|
469
|
+
}
|
|
470
|
+
case "new": {
|
|
471
|
+
if (cmd.url && !isSafeNavigationUrl(cmd.url)) {
|
|
472
|
+
return { id: cmd.id, ok: false, error: "Blocked URL scheme -- only http:// and https:// are allowed" };
|
|
473
|
+
}
|
|
474
|
+
const windowId = await getAutomationWindow(workspace);
|
|
475
|
+
const tab = await chrome.tabs.create({ windowId, url: cmd.url ?? BLANK_PAGE, active: true });
|
|
476
|
+
return { id: cmd.id, ok: true, data: { tabId: tab.id, url: tab.url } };
|
|
477
|
+
}
|
|
478
|
+
case "close": {
|
|
479
|
+
if (cmd.index !== void 0) {
|
|
480
|
+
const tabs = await listAutomationWebTabs(workspace);
|
|
481
|
+
const target = tabs[cmd.index];
|
|
482
|
+
if (!target?.id) return { id: cmd.id, ok: false, error: `Tab index ${cmd.index} not found` };
|
|
483
|
+
await chrome.tabs.remove(target.id);
|
|
484
|
+
await detach(target.id);
|
|
485
|
+
return { id: cmd.id, ok: true, data: { closed: target.id } };
|
|
486
|
+
}
|
|
487
|
+
const tabId = await resolveTabId(cmd.tabId, workspace);
|
|
488
|
+
await chrome.tabs.remove(tabId);
|
|
489
|
+
await detach(tabId);
|
|
490
|
+
return { id: cmd.id, ok: true, data: { closed: tabId } };
|
|
491
|
+
}
|
|
492
|
+
case "select": {
|
|
493
|
+
if (cmd.index === void 0 && cmd.tabId === void 0)
|
|
494
|
+
return { id: cmd.id, ok: false, error: "Missing index or tabId" };
|
|
495
|
+
if (cmd.tabId !== void 0) {
|
|
496
|
+
const session = automationSessions.get(workspace);
|
|
497
|
+
let tab;
|
|
498
|
+
try {
|
|
499
|
+
tab = await chrome.tabs.get(cmd.tabId);
|
|
500
|
+
} catch {
|
|
501
|
+
return { id: cmd.id, ok: false, error: `Tab ${cmd.tabId} no longer exists` };
|
|
502
|
+
}
|
|
503
|
+
if (!session || tab.windowId !== session.windowId) {
|
|
504
|
+
return { id: cmd.id, ok: false, error: `Tab ${cmd.tabId} is not in the automation window` };
|
|
505
|
+
}
|
|
506
|
+
await chrome.tabs.update(cmd.tabId, { active: true });
|
|
507
|
+
return { id: cmd.id, ok: true, data: { selected: cmd.tabId } };
|
|
508
|
+
}
|
|
509
|
+
const tabs = await listAutomationWebTabs(workspace);
|
|
510
|
+
const target = tabs[cmd.index];
|
|
511
|
+
if (!target?.id) return { id: cmd.id, ok: false, error: `Tab index ${cmd.index} not found` };
|
|
512
|
+
await chrome.tabs.update(target.id, { active: true });
|
|
513
|
+
return { id: cmd.id, ok: true, data: { selected: target.id } };
|
|
514
|
+
}
|
|
515
|
+
default:
|
|
516
|
+
return { id: cmd.id, ok: false, error: `Unknown tabs op: ${cmd.op}` };
|
|
517
|
+
}
|
|
512
518
|
}
|
|
513
519
|
async function handleCookies(cmd) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
520
|
+
if (!cmd.domain && !cmd.url) {
|
|
521
|
+
return { id: cmd.id, ok: false, error: "Cookie scope required: provide domain or url to avoid dumping all cookies" };
|
|
522
|
+
}
|
|
523
|
+
const details = {};
|
|
524
|
+
if (cmd.domain) details.domain = cmd.domain;
|
|
525
|
+
if (cmd.url) details.url = cmd.url;
|
|
526
|
+
const cookies = await chrome.cookies.getAll(details);
|
|
527
|
+
const data = cookies.map((c) => ({
|
|
528
|
+
name: c.name,
|
|
529
|
+
value: c.value,
|
|
530
|
+
domain: c.domain,
|
|
531
|
+
path: c.path,
|
|
532
|
+
secure: c.secure,
|
|
533
|
+
httpOnly: c.httpOnly,
|
|
534
|
+
expirationDate: c.expirationDate
|
|
535
|
+
}));
|
|
536
|
+
return { id: cmd.id, ok: true, data };
|
|
531
537
|
}
|
|
532
538
|
async function handleScreenshot(cmd, workspace) {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
};
|
|
545
|
-
} catch (err) {
|
|
546
|
-
return {
|
|
547
|
-
id: cmd.id,
|
|
548
|
-
ok: false,
|
|
549
|
-
error: err instanceof Error ? err.message : String(err)
|
|
550
|
-
};
|
|
551
|
-
}
|
|
539
|
+
const tabId = await resolveTabId(cmd.tabId, workspace);
|
|
540
|
+
try {
|
|
541
|
+
const data = await screenshot(tabId, {
|
|
542
|
+
format: cmd.format,
|
|
543
|
+
quality: cmd.quality,
|
|
544
|
+
fullPage: cmd.fullPage
|
|
545
|
+
});
|
|
546
|
+
return { id: cmd.id, ok: true, data };
|
|
547
|
+
} catch (err) {
|
|
548
|
+
return { id: cmd.id, ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
549
|
+
}
|
|
552
550
|
}
|
|
553
551
|
async function handleCloseWindow(cmd, workspace) {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
ok: true,
|
|
565
|
-
data: { closed: true }
|
|
566
|
-
};
|
|
552
|
+
const session = automationSessions.get(workspace);
|
|
553
|
+
if (session) {
|
|
554
|
+
try {
|
|
555
|
+
await chrome.windows.remove(session.windowId);
|
|
556
|
+
} catch {
|
|
557
|
+
}
|
|
558
|
+
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
559
|
+
automationSessions.delete(workspace);
|
|
560
|
+
}
|
|
561
|
+
return { id: cmd.id, ok: true, data: { closed: true } };
|
|
567
562
|
}
|
|
568
563
|
async function handleSessions(cmd) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
id: cmd.id,
|
|
578
|
-
ok: true,
|
|
579
|
-
data
|
|
580
|
-
};
|
|
564
|
+
const now = Date.now();
|
|
565
|
+
const data = await Promise.all([...automationSessions.entries()].map(async ([workspace, session]) => ({
|
|
566
|
+
workspace,
|
|
567
|
+
windowId: session.windowId,
|
|
568
|
+
tabCount: (await chrome.tabs.query({ windowId: session.windowId })).filter((tab) => isDebuggableUrl(tab.url)).length,
|
|
569
|
+
idleMsRemaining: Math.max(0, session.idleDeadlineAt - now)
|
|
570
|
+
})));
|
|
571
|
+
return { id: cmd.id, ok: true, data };
|
|
581
572
|
}
|
|
582
|
-
//#endregion
|