@jackwener/opencli 1.3.3 → 1.4.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/pull_request_template.md +3 -1
- package/.github/workflows/build-extension.yml +7 -1
- package/.github/workflows/ci.yml +29 -3
- package/.github/workflows/docs.yml +1 -1
- package/.github/workflows/e2e-headed.yml +20 -0
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/security.yml +0 -3
- package/CHANGELOG.md +55 -0
- package/CONTRIBUTING.md +6 -3
- package/README.md +30 -3
- package/README.zh-CN.md +30 -3
- package/SKILL.md +7 -1
- package/TESTING.md +1 -0
- package/chatwise-opencli.ps1 +82 -0
- package/dist/analysis.d.ts +38 -0
- package/dist/analysis.js +166 -0
- package/dist/browser/cdp.d.ts +0 -4
- package/dist/browser/cdp.js +53 -41
- package/dist/browser/cdp.test.d.ts +1 -0
- package/dist/browser/cdp.test.js +52 -0
- package/dist/browser/dom-snapshot.d.ts +2 -2
- package/dist/browser/dom-snapshot.js +54 -1
- package/dist/browser/dom-snapshot.test.js +36 -0
- package/dist/browser/index.d.ts +2 -2
- package/dist/browser/index.js +1 -1
- package/dist/browser/mcp.d.ts +0 -2
- package/dist/browser/mcp.js +2 -3
- package/dist/browser/page.d.ts +4 -3
- package/dist/browser/page.js +34 -37
- package/dist/browser/stealth.d.ts +0 -2
- package/dist/browser/stealth.js +24 -9
- package/dist/browser.test.js +2 -2
- package/dist/build-manifest.js +15 -9
- package/dist/build-manifest.test.js +12 -0
- package/dist/cascade.js +4 -2
- package/dist/cli-manifest.json +639 -258
- package/dist/cli.js +57 -29
- package/dist/clis/_shared/desktop-commands.d.ts +22 -0
- package/dist/clis/_shared/desktop-commands.js +108 -0
- package/dist/clis/antigravity/serve.js +5 -2
- package/dist/clis/arxiv/search.js +1 -1
- package/dist/clis/bilibili/dynamic.test.d.ts +1 -0
- package/dist/clis/bilibili/dynamic.test.js +68 -0
- package/dist/clis/bilibili/favorite.js +4 -2
- package/dist/clis/bilibili/following.js +3 -2
- package/dist/clis/bilibili/subtitle.js +8 -7
- package/dist/clis/bilibili/utils.js +2 -2
- package/dist/clis/boss/batchgreet.js +1 -1
- package/dist/clis/boss/chatlist.js +1 -1
- package/dist/clis/boss/chatmsg.js +1 -1
- package/dist/clis/boss/detail.js +1 -1
- package/dist/clis/boss/exchange.js +1 -1
- package/dist/clis/boss/greet.js +1 -1
- package/dist/clis/boss/invite.js +1 -1
- package/dist/clis/boss/joblist.js +1 -1
- package/dist/clis/boss/mark.js +4 -3
- package/dist/clis/boss/recommend.js +1 -1
- package/dist/clis/boss/resume.js +1 -1
- package/dist/clis/boss/search.js +1 -1
- package/dist/clis/boss/send.js +5 -4
- package/dist/clis/boss/stats.js +1 -1
- package/dist/clis/chatgpt/ask.js +4 -0
- package/dist/clis/chatgpt/new.js +5 -1
- package/dist/clis/chatgpt/read.js +5 -1
- package/dist/clis/chatgpt/send.js +2 -1
- package/dist/clis/chatgpt/status.js +5 -1
- package/dist/clis/chatwise/ask.js +8 -2
- package/dist/clis/chatwise/export.js +2 -0
- package/dist/clis/chatwise/history.js +2 -0
- package/dist/clis/chatwise/model.js +8 -3
- package/dist/clis/chatwise/new.js +3 -18
- package/dist/clis/chatwise/read.js +2 -0
- package/dist/clis/chatwise/screenshot.js +3 -27
- package/dist/clis/chatwise/send.js +8 -2
- package/dist/clis/chatwise/shared.d.ts +2 -0
- package/dist/clis/chatwise/shared.js +6 -0
- package/dist/clis/chatwise/status.js +3 -22
- package/dist/clis/codex/ask.js +6 -2
- package/dist/clis/codex/dump.js +2 -25
- package/dist/clis/codex/new.js +2 -25
- package/dist/clis/codex/screenshot.js +2 -27
- package/dist/clis/codex/send.js +6 -4
- package/dist/clis/codex/status.js +2 -22
- package/dist/clis/cursor/ask.js +2 -1
- package/dist/clis/cursor/composer.js +2 -1
- package/dist/clis/cursor/dump.js +2 -25
- package/dist/clis/cursor/new.js +2 -18
- package/dist/clis/cursor/read.js +2 -1
- package/dist/clis/cursor/screenshot.js +1 -30
- package/dist/clis/cursor/send.js +2 -1
- package/dist/clis/cursor/status.js +2 -21
- package/dist/clis/dictionary/examples.yaml +25 -0
- package/dist/clis/dictionary/search.yaml +27 -0
- package/dist/clis/dictionary/synonyms.yaml +25 -0
- package/dist/clis/douban/book-hot.js +1 -1
- package/dist/clis/douban/movie-hot.js +1 -1
- package/dist/clis/douban/search.js +1 -1
- package/dist/clis/douban/utils.d.ts +4 -1
- package/dist/clis/douban/utils.js +156 -1
- package/dist/clis/doubao/ask.js +1 -1
- package/dist/clis/doubao/new.js +1 -1
- package/dist/clis/doubao/read.js +1 -1
- package/dist/clis/doubao/send.js +1 -1
- package/dist/clis/doubao/status.js +1 -1
- package/dist/clis/doubao-app/ask.js +1 -1
- package/dist/clis/doubao-app/new.js +1 -1
- package/dist/clis/doubao-app/read.js +1 -1
- package/dist/clis/doubao-app/send.js +1 -1
- package/dist/clis/grok/ask.d.ts +4 -0
- package/dist/clis/grok/ask.js +28 -10
- package/dist/clis/grok/ask.test.js +18 -0
- package/dist/clis/jd/item.d.ts +1 -0
- package/dist/clis/jd/item.js +96 -0
- package/dist/clis/jd/item.test.d.ts +1 -0
- package/dist/clis/jd/item.test.js +28 -0
- package/dist/clis/jike/feed.js +1 -1
- package/dist/clis/jike/search.js +1 -1
- package/dist/clis/linkedin/search.js +5 -4
- package/dist/clis/linkedin/timeline.d.ts +21 -0
- package/dist/clis/linkedin/timeline.js +503 -0
- package/dist/clis/linkedin/timeline.test.d.ts +1 -0
- package/dist/clis/linkedin/timeline.test.js +81 -0
- package/dist/clis/medium/feed.js +1 -1
- package/dist/clis/medium/search.js +1 -1
- package/dist/clis/medium/user.js +1 -1
- package/dist/clis/medium/{shared.js → utils.js} +2 -1
- package/dist/clis/pixiv/detail.yaml +49 -0
- package/dist/clis/pixiv/download.d.ts +7 -0
- package/dist/clis/pixiv/download.js +78 -0
- package/dist/clis/pixiv/download.test.d.ts +1 -0
- package/dist/clis/pixiv/download.test.js +87 -0
- package/dist/clis/pixiv/illusts.d.ts +8 -0
- package/dist/clis/pixiv/illusts.js +65 -0
- package/dist/clis/pixiv/illusts.test.d.ts +1 -0
- package/dist/clis/pixiv/illusts.test.js +99 -0
- package/dist/clis/pixiv/ranking.yaml +53 -0
- package/dist/clis/pixiv/search.d.ts +6 -0
- package/dist/clis/pixiv/search.js +43 -0
- package/dist/clis/pixiv/search.test.d.ts +1 -0
- package/dist/clis/pixiv/search.test.js +83 -0
- package/dist/clis/pixiv/test-utils.d.ts +12 -0
- package/dist/clis/pixiv/test-utils.js +23 -0
- package/dist/clis/pixiv/user.yaml +46 -0
- package/dist/clis/pixiv/utils.d.ts +27 -0
- package/dist/clis/pixiv/utils.js +49 -0
- package/dist/clis/reddit/comment.js +2 -1
- package/dist/clis/reddit/read.js +4 -3
- package/dist/clis/reddit/read.test.d.ts +1 -0
- package/dist/clis/reddit/read.test.js +28 -0
- package/dist/clis/reddit/save.js +2 -1
- package/dist/clis/reddit/saved.js +7 -3
- package/dist/clis/reddit/subscribe.js +2 -1
- package/dist/clis/reddit/upvote.js +2 -1
- package/dist/clis/reddit/upvoted.js +7 -3
- package/dist/clis/sinablog/article.js +1 -1
- package/dist/clis/sinablog/hot.js +1 -1
- package/dist/clis/sinablog/user.js +1 -1
- package/dist/clis/substack/feed.js +1 -1
- package/dist/clis/substack/publication.js +1 -1
- package/dist/clis/substack/search.js +3 -2
- package/dist/clis/substack/{shared.js → utils.js} +3 -2
- package/dist/clis/tiktok/search.yaml +2 -1
- package/dist/clis/twitter/accept.js +2 -1
- package/dist/clis/twitter/article.js +4 -1
- package/dist/clis/twitter/block.js +2 -1
- package/dist/clis/twitter/bookmark.js +2 -1
- package/dist/clis/twitter/bookmarks.js +3 -2
- package/dist/clis/twitter/delete.js +2 -1
- package/dist/clis/twitter/follow.js +2 -1
- package/dist/clis/twitter/followers.js +3 -2
- package/dist/clis/twitter/following.js +3 -2
- package/dist/clis/twitter/hide-reply.js +2 -1
- package/dist/clis/twitter/like.js +2 -1
- package/dist/clis/twitter/notifications.js +2 -1
- package/dist/clis/twitter/post.js +2 -1
- package/dist/clis/twitter/profile.js +5 -2
- package/dist/clis/twitter/reply-dm.js +2 -1
- package/dist/clis/twitter/reply.js +2 -1
- package/dist/clis/twitter/search.js +30 -13
- package/dist/clis/twitter/search.test.d.ts +1 -0
- package/dist/clis/twitter/search.test.js +104 -0
- package/dist/clis/twitter/thread.js +2 -2
- package/dist/clis/twitter/timeline.js +3 -2
- package/dist/clis/twitter/trending.js +3 -2
- package/dist/clis/twitter/unblock.js +2 -1
- package/dist/clis/twitter/unbookmark.js +2 -1
- package/dist/clis/twitter/unfollow.js +2 -1
- package/dist/clis/v2ex/daily.js +3 -2
- package/dist/clis/v2ex/me.js +3 -2
- package/dist/clis/v2ex/notifications.js +4 -4
- package/dist/clis/web/read.d.ts +16 -0
- package/dist/clis/web/read.js +202 -0
- package/dist/clis/xueqiu/danjuan-utils.d.ts +55 -0
- package/dist/clis/xueqiu/danjuan-utils.js +126 -0
- package/dist/clis/xueqiu/danjuan-utils.test.d.ts +1 -0
- package/dist/clis/xueqiu/danjuan-utils.test.js +41 -0
- package/dist/clis/xueqiu/fund-holdings.d.ts +1 -0
- package/dist/clis/xueqiu/fund-holdings.js +28 -0
- package/dist/clis/xueqiu/fund-snapshot.d.ts +1 -0
- package/dist/clis/xueqiu/fund-snapshot.js +25 -0
- package/dist/clis/youtube/transcript.js +5 -4
- package/dist/clis/youtube/video.js +3 -2
- package/dist/daemon.js +7 -3
- package/dist/discovery.js +11 -10
- package/dist/doctor.js +2 -1
- package/dist/download/index.d.ts +4 -12
- package/dist/download/index.js +33 -12
- package/dist/download/index.test.js +79 -2
- package/dist/download/media-download.js +4 -2
- package/dist/engine.test.js +76 -4
- package/dist/execution.d.ts +1 -9
- package/dist/execution.js +56 -46
- package/dist/explore.js +12 -111
- package/dist/external-clis.yaml +0 -8
- package/dist/external.js +7 -5
- package/dist/external.test.js +4 -0
- package/dist/generate.d.ts +0 -9
- package/dist/generate.js +4 -20
- package/dist/hooks.d.ts +46 -0
- package/dist/hooks.js +56 -0
- package/dist/hooks.test.d.ts +4 -0
- package/dist/hooks.test.js +92 -0
- package/dist/interceptor.js +70 -23
- package/dist/main.js +2 -0
- package/dist/output.js +12 -6
- package/dist/pipeline/executor.js +1 -1
- package/dist/pipeline/steps/browser.js +1 -3
- package/dist/pipeline/steps/download.js +42 -26
- package/dist/pipeline/steps/download.test.d.ts +1 -0
- package/dist/pipeline/steps/download.test.js +101 -0
- package/dist/pipeline/steps/fetch.js +40 -22
- package/dist/pipeline/steps/fetch.test.d.ts +1 -0
- package/dist/pipeline/steps/fetch.test.js +123 -0
- package/dist/pipeline/steps/transform.js +2 -6
- package/dist/pipeline/template.js +66 -52
- package/dist/pipeline/template.test.js +28 -0
- package/dist/pipeline/transform.test.js +18 -0
- package/dist/plugin.d.ts +40 -1
- package/dist/plugin.js +214 -17
- package/dist/plugin.test.d.ts +1 -1
- package/dist/plugin.test.js +219 -3
- package/dist/record.js +6 -98
- package/dist/registry-api.d.ts +2 -0
- package/dist/registry-api.js +1 -0
- package/dist/registry.d.ts +5 -2
- package/dist/registry.js +1 -2
- package/dist/runtime.d.ts +0 -1
- package/dist/runtime.js +14 -4
- package/dist/snapshotFormatter.d.ts +7 -14
- package/dist/snapshotFormatter.js +38 -78
- package/dist/utils.d.ts +9 -0
- package/dist/utils.js +29 -0
- package/dist/validate.js +3 -5
- package/dist/yaml-schema.d.ts +26 -0
- package/dist/yaml-schema.js +5 -0
- package/docs/.vitepress/config.mts +3 -0
- package/docs/adapters/browser/dictionary.md +27 -0
- package/docs/adapters/browser/jd.md +27 -0
- package/docs/adapters/browser/linkedin.md +6 -0
- package/docs/adapters/browser/pixiv.md +92 -0
- package/docs/adapters/browser/web.md +30 -0
- package/docs/adapters/browser/xueqiu.md +27 -9
- package/docs/adapters/index.md +3 -1
- package/docs/comparison.md +125 -0
- package/docs/developer/contributing.md +21 -2
- package/docs/developer/testing.md +14 -8
- package/docs/developer/ts-adapter.md +18 -0
- package/docs/developer/yaml-adapter.md +16 -0
- package/docs/guide/plugins.md +10 -0
- package/docs/zh/guide/plugins.md +10 -0
- package/extension/dist/background.js +519 -444
- package/extension/manifest.json +1 -1
- package/extension/package.json +1 -1
- package/extension/src/background.test.ts +46 -1
- package/extension/src/background.ts +108 -33
- package/extension/src/cdp.ts +9 -9
- package/package.json +3 -2
- package/scripts/check-doc-coverage.sh +2 -0
- package/src/analysis.ts +170 -0
- package/src/browser/cdp.test.ts +66 -0
- package/src/browser/cdp.ts +59 -44
- package/src/browser/dom-snapshot.test.ts +42 -0
- package/src/browser/dom-snapshot.ts +56 -3
- package/src/browser/index.ts +2 -2
- package/src/browser/mcp.ts +2 -4
- package/src/browser/page.ts +34 -37
- package/src/browser/stealth.ts +24 -10
- package/src/browser.test.ts +2 -2
- package/src/build-manifest.test.ts +14 -0
- package/src/build-manifest.ts +13 -31
- package/src/cascade.ts +5 -3
- package/src/cli.ts +66 -34
- package/src/clis/_shared/desktop-commands.ts +121 -0
- package/src/clis/antigravity/serve.ts +6 -3
- package/src/clis/arxiv/search.ts +1 -1
- package/src/clis/bilibili/dynamic.test.ts +79 -0
- package/src/clis/bilibili/favorite.ts +5 -2
- package/src/clis/bilibili/following.ts +3 -2
- package/src/clis/bilibili/subtitle.ts +8 -7
- package/src/clis/bilibili/utils.ts +2 -2
- package/src/clis/boss/batchgreet.ts +1 -1
- package/src/clis/boss/chatlist.ts +1 -1
- package/src/clis/boss/chatmsg.ts +1 -1
- package/src/clis/boss/detail.ts +1 -1
- package/src/clis/boss/exchange.ts +1 -1
- package/src/clis/boss/greet.ts +1 -1
- package/src/clis/boss/invite.ts +1 -1
- package/src/clis/boss/joblist.ts +1 -1
- package/src/clis/boss/mark.ts +4 -3
- package/src/clis/boss/recommend.ts +1 -1
- package/src/clis/boss/resume.ts +1 -1
- package/src/clis/boss/search.ts +1 -1
- package/src/clis/boss/send.ts +5 -4
- package/src/clis/boss/stats.ts +1 -1
- package/src/clis/chatgpt/ask.ts +5 -0
- package/src/clis/chatgpt/new.ts +7 -2
- package/src/clis/chatgpt/read.ts +7 -2
- package/src/clis/chatgpt/send.ts +3 -2
- package/src/clis/chatgpt/status.ts +6 -1
- package/src/clis/chatwise/ask.ts +7 -2
- package/src/clis/chatwise/export.ts +2 -0
- package/src/clis/chatwise/history.ts +2 -0
- package/src/clis/chatwise/model.ts +7 -3
- package/src/clis/chatwise/new.ts +3 -20
- package/src/clis/chatwise/read.ts +2 -0
- package/src/clis/chatwise/screenshot.ts +3 -32
- package/src/clis/chatwise/send.ts +7 -2
- package/src/clis/chatwise/shared.ts +8 -0
- package/src/clis/chatwise/status.ts +3 -24
- package/src/clis/codex/ask.ts +5 -2
- package/src/clis/codex/dump.ts +2 -27
- package/src/clis/codex/new.ts +2 -28
- package/src/clis/codex/screenshot.ts +2 -32
- package/src/clis/codex/send.ts +5 -4
- package/src/clis/codex/status.ts +2 -24
- package/src/clis/cursor/ask.ts +2 -1
- package/src/clis/cursor/composer.ts +2 -1
- package/src/clis/cursor/dump.ts +2 -27
- package/src/clis/cursor/new.ts +2 -20
- package/src/clis/cursor/read.ts +2 -1
- package/src/clis/cursor/screenshot.ts +1 -36
- package/src/clis/cursor/send.ts +2 -1
- package/src/clis/cursor/status.ts +2 -22
- package/src/clis/dictionary/examples.yaml +25 -0
- package/src/clis/dictionary/search.yaml +27 -0
- package/src/clis/dictionary/synonyms.yaml +25 -0
- package/src/clis/douban/book-hot.ts +1 -1
- package/src/clis/douban/movie-hot.ts +1 -1
- package/src/clis/douban/search.ts +1 -1
- package/src/clis/douban/utils.ts +165 -1
- package/src/clis/doubao/ask.ts +1 -1
- package/src/clis/doubao/new.ts +1 -1
- package/src/clis/doubao/read.ts +1 -1
- package/src/clis/doubao/send.ts +1 -1
- package/src/clis/doubao/status.ts +1 -1
- package/src/clis/doubao-app/ask.ts +1 -1
- package/src/clis/doubao-app/new.ts +1 -1
- package/src/clis/doubao-app/read.ts +1 -1
- package/src/clis/doubao-app/send.ts +1 -1
- package/src/clis/grok/ask.test.ts +25 -0
- package/src/clis/grok/ask.ts +25 -12
- package/src/clis/jd/item.test.ts +35 -0
- package/src/clis/jd/item.ts +101 -0
- package/src/clis/jike/feed.ts +1 -1
- package/src/clis/jike/search.ts +1 -1
- package/src/clis/linkedin/search.ts +5 -4
- package/src/clis/linkedin/timeline.test.ts +99 -0
- package/src/clis/linkedin/timeline.ts +532 -0
- package/src/clis/medium/feed.ts +1 -1
- package/src/clis/medium/search.ts +1 -1
- package/src/clis/medium/user.ts +1 -1
- package/src/clis/medium/{shared.ts → utils.ts} +2 -1
- package/src/clis/pixiv/detail.yaml +49 -0
- package/src/clis/pixiv/download.test.ts +114 -0
- package/src/clis/pixiv/download.ts +91 -0
- package/src/clis/pixiv/illusts.test.ts +115 -0
- package/src/clis/pixiv/illusts.ts +78 -0
- package/src/clis/pixiv/ranking.yaml +53 -0
- package/src/clis/pixiv/search.test.ts +97 -0
- package/src/clis/pixiv/search.ts +53 -0
- package/src/clis/pixiv/test-utils.ts +29 -0
- package/src/clis/pixiv/user.yaml +46 -0
- package/src/clis/pixiv/utils.ts +62 -0
- package/src/clis/reddit/comment.ts +2 -1
- package/src/clis/reddit/read.test.ts +34 -0
- package/src/clis/reddit/read.ts +4 -3
- package/src/clis/reddit/save.ts +2 -1
- package/src/clis/reddit/saved.ts +6 -2
- package/src/clis/reddit/subscribe.ts +2 -1
- package/src/clis/reddit/upvote.ts +2 -1
- package/src/clis/reddit/upvoted.ts +6 -2
- package/src/clis/sinablog/article.ts +1 -1
- package/src/clis/sinablog/hot.ts +1 -1
- package/src/clis/sinablog/user.ts +1 -1
- package/src/clis/substack/feed.ts +1 -1
- package/src/clis/substack/publication.ts +1 -1
- package/src/clis/substack/search.ts +3 -2
- package/src/clis/substack/{shared.ts → utils.ts} +3 -2
- package/src/clis/tiktok/search.yaml +2 -1
- package/src/clis/twitter/accept.ts +2 -1
- package/src/clis/twitter/article.ts +3 -1
- package/src/clis/twitter/block.ts +2 -1
- package/src/clis/twitter/bookmark.ts +2 -1
- package/src/clis/twitter/bookmarks.ts +3 -2
- package/src/clis/twitter/delete.ts +2 -1
- package/src/clis/twitter/follow.ts +2 -1
- package/src/clis/twitter/followers.ts +3 -2
- package/src/clis/twitter/following.ts +3 -2
- package/src/clis/twitter/hide-reply.ts +2 -1
- package/src/clis/twitter/like.ts +2 -1
- package/src/clis/twitter/notifications.ts +2 -1
- package/src/clis/twitter/post.ts +2 -1
- package/src/clis/twitter/profile.ts +4 -2
- package/src/clis/twitter/reply-dm.ts +2 -1
- package/src/clis/twitter/reply.ts +2 -1
- package/src/clis/twitter/search.test.ts +113 -0
- package/src/clis/twitter/search.ts +38 -14
- package/src/clis/twitter/thread.ts +2 -2
- package/src/clis/twitter/timeline.ts +3 -2
- package/src/clis/twitter/trending.ts +3 -2
- package/src/clis/twitter/unblock.ts +2 -1
- package/src/clis/twitter/unbookmark.ts +2 -1
- package/src/clis/twitter/unfollow.ts +2 -1
- package/src/clis/v2ex/daily.ts +3 -2
- package/src/clis/v2ex/me.ts +3 -2
- package/src/clis/v2ex/notifications.ts +3 -4
- package/src/clis/web/read.ts +210 -0
- package/src/clis/xueqiu/danjuan-utils.test.ts +49 -0
- package/src/clis/xueqiu/danjuan-utils.ts +176 -0
- package/src/clis/xueqiu/fund-holdings.ts +32 -0
- package/src/clis/xueqiu/fund-snapshot.ts +27 -0
- package/src/clis/youtube/transcript.ts +5 -4
- package/src/clis/youtube/video.ts +3 -2
- package/src/daemon.ts +5 -4
- package/src/discovery.ts +12 -34
- package/src/doctor.ts +3 -2
- package/src/download/index.test.ts +93 -2
- package/src/download/index.ts +44 -23
- package/src/download/media-download.ts +5 -3
- package/src/engine.test.ts +84 -3
- package/src/execution.ts +62 -46
- package/src/explore.ts +21 -90
- package/src/external-clis.yaml +0 -8
- package/src/external.test.ts +9 -0
- package/src/external.ts +12 -10
- package/src/generate.ts +4 -41
- package/src/hooks.test.ts +126 -0
- package/src/hooks.ts +90 -0
- package/src/interceptor.ts +73 -23
- package/src/main.ts +2 -0
- package/src/output.ts +14 -6
- package/src/pipeline/executor.ts +1 -1
- package/src/pipeline/steps/browser.ts +1 -3
- package/src/pipeline/steps/download.test.ts +136 -0
- package/src/pipeline/steps/download.ts +47 -34
- package/src/pipeline/steps/fetch.test.ts +179 -0
- package/src/pipeline/steps/fetch.ts +39 -23
- package/src/pipeline/steps/transform.ts +2 -6
- package/src/pipeline/template.test.ts +28 -0
- package/src/pipeline/template.ts +67 -79
- package/src/pipeline/transform.test.ts +20 -0
- package/src/plugin.test.ts +251 -3
- package/src/plugin.ts +265 -21
- package/src/record.ts +12 -84
- package/src/registry-api.ts +2 -0
- package/src/registry.ts +7 -4
- package/src/runtime.ts +14 -4
- package/src/snapshotFormatter.ts +43 -121
- package/src/utils.ts +39 -0
- package/src/validate.ts +3 -5
- package/src/yaml-schema.ts +28 -0
- package/tests/e2e/browser-auth.test.ts +25 -0
- package/tests/e2e/plugin-management.test.ts +137 -0
- package/tests/e2e/public-commands.test.ts +34 -1
- package/vitest.config.ts +19 -1
- package/.github/workflows/pkg-pr-new.yml +0 -30
- package/dist/clis/douban/shared.d.ts +0 -4
- package/dist/clis/douban/shared.js +0 -155
- package/src/clis/douban/shared.ts +0 -165
- /package/dist/clis/boss/{common.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/boss/{common.js → utils.js} +0 -0
- /package/dist/clis/doubao/{common.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/doubao/{common.js → utils.js} +0 -0
- /package/dist/clis/doubao-app/{common.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/doubao-app/{common.js → utils.js} +0 -0
- /package/dist/clis/jike/{shared.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/jike/{shared.js → utils.js} +0 -0
- /package/dist/clis/medium/{shared.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/sinablog/{shared.d.ts → utils.d.ts} +0 -0
- /package/dist/clis/sinablog/{shared.js → utils.js} +0 -0
- /package/dist/clis/substack/{shared.d.ts → utils.d.ts} +0 -0
- /package/src/clis/boss/{common.ts → utils.ts} +0 -0
- /package/src/clis/doubao/{common.ts → utils.ts} +0 -0
- /package/src/clis/doubao-app/{common.ts → utils.ts} +0 -0
- /package/src/clis/jike/{shared.ts → utils.ts} +0 -0
- /package/src/clis/sinablog/{shared.ts → utils.ts} +0 -0
|
@@ -1,507 +1,582 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
//#region src/protocol.ts
|
|
2
|
+
/** Default daemon port */
|
|
3
|
+
var DAEMON_PORT = 19825;
|
|
4
|
+
var DAEMON_HOST = "localhost";
|
|
5
|
+
var DAEMON_WS_URL = `ws://${DAEMON_HOST}:${DAEMON_PORT}/ext`;
|
|
6
|
+
`${DAEMON_HOST}${DAEMON_PORT}`;
|
|
7
|
+
/** Base reconnect delay for extension WebSocket (ms) */
|
|
8
|
+
var WS_RECONNECT_BASE_DELAY = 2e3;
|
|
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 */
|
|
8
22
|
function isDebuggableUrl$1(url) {
|
|
9
|
-
|
|
10
|
-
|
|
23
|
+
if (!url) return true;
|
|
24
|
+
return !url.startsWith("chrome://") && !url.startsWith("chrome-extension://");
|
|
11
25
|
}
|
|
12
26
|
async function ensureAttached(tabId) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
attached.add(tabId);
|
|
55
|
-
try {
|
|
56
|
-
await chrome.debugger.sendCommand({ tabId }, "Runtime.enable");
|
|
57
|
-
} catch {
|
|
58
|
-
}
|
|
27
|
+
try {
|
|
28
|
+
const tab = await chrome.tabs.get(tabId);
|
|
29
|
+
if (!isDebuggableUrl$1(tab.url)) {
|
|
30
|
+
attached.delete(tabId);
|
|
31
|
+
throw new Error(`Cannot debug tab ${tabId}: URL is ${tab.url ?? "unknown"}`);
|
|
32
|
+
}
|
|
33
|
+
} catch (e) {
|
|
34
|
+
if (e instanceof Error && e.message.startsWith("Cannot debug tab")) throw e;
|
|
35
|
+
attached.delete(tabId);
|
|
36
|
+
throw new Error(`Tab ${tabId} no longer exists`);
|
|
37
|
+
}
|
|
38
|
+
if (attached.has(tabId)) try {
|
|
39
|
+
await chrome.debugger.sendCommand({ tabId }, "Runtime.evaluate", {
|
|
40
|
+
expression: "1",
|
|
41
|
+
returnByValue: true
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
} catch {
|
|
45
|
+
attached.delete(tabId);
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
await chrome.debugger.attach({ tabId }, "1.3");
|
|
49
|
+
} catch (e) {
|
|
50
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
51
|
+
const hint = msg.includes("chrome-extension://") ? ". Tip: another Chrome extension may be interfering — try disabling other extensions" : "";
|
|
52
|
+
if (msg.includes("Another debugger is already attached")) {
|
|
53
|
+
try {
|
|
54
|
+
await chrome.debugger.detach({ tabId });
|
|
55
|
+
} catch {}
|
|
56
|
+
try {
|
|
57
|
+
await chrome.debugger.attach({ tabId }, "1.3");
|
|
58
|
+
} catch {
|
|
59
|
+
throw new Error(`attach failed: ${msg}${hint}`);
|
|
60
|
+
}
|
|
61
|
+
} else throw new Error(`attach failed: ${msg}${hint}`);
|
|
62
|
+
}
|
|
63
|
+
attached.add(tabId);
|
|
64
|
+
try {
|
|
65
|
+
await chrome.debugger.sendCommand({ tabId }, "Runtime.enable");
|
|
66
|
+
} catch {}
|
|
59
67
|
}
|
|
60
68
|
async function evaluate(tabId, expression) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
await ensureAttached(tabId);
|
|
70
|
+
const result = await chrome.debugger.sendCommand({ tabId }, "Runtime.evaluate", {
|
|
71
|
+
expression,
|
|
72
|
+
returnByValue: true,
|
|
73
|
+
awaitPromise: true
|
|
74
|
+
});
|
|
75
|
+
if (result.exceptionDetails) {
|
|
76
|
+
const errMsg = result.exceptionDetails.exception?.description || result.exceptionDetails.text || "Eval error";
|
|
77
|
+
throw new Error(errMsg);
|
|
78
|
+
}
|
|
79
|
+
return result.result?.value;
|
|
72
80
|
}
|
|
73
|
-
|
|
81
|
+
var evaluateAsync = evaluate;
|
|
82
|
+
/**
|
|
83
|
+
* Capture a screenshot via CDP Page.captureScreenshot.
|
|
84
|
+
* Returns base64-encoded image data.
|
|
85
|
+
*/
|
|
74
86
|
async function screenshot(tabId, options = {}) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const result = await chrome.debugger.sendCommand({ tabId }, "Page.captureScreenshot", params);
|
|
95
|
-
return result.data;
|
|
96
|
-
} finally {
|
|
97
|
-
if (options.fullPage) {
|
|
98
|
-
await chrome.debugger.sendCommand({ tabId }, "Emulation.clearDeviceMetricsOverride").catch(() => {
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
87
|
+
await ensureAttached(tabId);
|
|
88
|
+
const format = options.format ?? "png";
|
|
89
|
+
if (options.fullPage) {
|
|
90
|
+
const metrics = await chrome.debugger.sendCommand({ tabId }, "Page.getLayoutMetrics");
|
|
91
|
+
const size = metrics.cssContentSize || metrics.contentSize;
|
|
92
|
+
if (size) await chrome.debugger.sendCommand({ tabId }, "Emulation.setDeviceMetricsOverride", {
|
|
93
|
+
mobile: false,
|
|
94
|
+
width: Math.ceil(size.width),
|
|
95
|
+
height: Math.ceil(size.height),
|
|
96
|
+
deviceScaleFactor: 1
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const params = { format };
|
|
101
|
+
if (format === "jpeg" && options.quality !== void 0) params.quality = Math.max(0, Math.min(100, options.quality));
|
|
102
|
+
return (await chrome.debugger.sendCommand({ tabId }, "Page.captureScreenshot", params)).data;
|
|
103
|
+
} finally {
|
|
104
|
+
if (options.fullPage) await chrome.debugger.sendCommand({ tabId }, "Emulation.clearDeviceMetricsOverride").catch(() => {});
|
|
105
|
+
}
|
|
102
106
|
}
|
|
103
|
-
function detach(tabId) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
107
|
+
async function detach(tabId) {
|
|
108
|
+
if (!attached.has(tabId)) return;
|
|
109
|
+
attached.delete(tabId);
|
|
110
|
+
try {
|
|
111
|
+
await chrome.debugger.detach({ tabId });
|
|
112
|
+
} catch {}
|
|
110
113
|
}
|
|
111
114
|
function registerListeners() {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
attached.delete(tabId);
|
|
122
|
-
try {
|
|
123
|
-
chrome.debugger.detach({ tabId });
|
|
124
|
-
} catch {
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
});
|
|
115
|
+
chrome.tabs.onRemoved.addListener((tabId) => {
|
|
116
|
+
attached.delete(tabId);
|
|
117
|
+
});
|
|
118
|
+
chrome.debugger.onDetach.addListener((source) => {
|
|
119
|
+
if (source.tabId) attached.delete(source.tabId);
|
|
120
|
+
});
|
|
121
|
+
chrome.tabs.onUpdated.addListener(async (tabId, info) => {
|
|
122
|
+
if (info.url && !isDebuggableUrl$1(info.url)) await detach(tabId);
|
|
123
|
+
});
|
|
129
124
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/background.ts
|
|
127
|
+
var ws = null;
|
|
128
|
+
var reconnectTimer = null;
|
|
129
|
+
var reconnectAttempts = 0;
|
|
130
|
+
var _origLog = console.log.bind(console);
|
|
131
|
+
var _origWarn = console.warn.bind(console);
|
|
132
|
+
var _origError = console.error.bind(console);
|
|
137
133
|
function forwardLog(level, args) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
134
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
|
135
|
+
try {
|
|
136
|
+
const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
137
|
+
ws.send(JSON.stringify({
|
|
138
|
+
type: "log",
|
|
139
|
+
level,
|
|
140
|
+
msg,
|
|
141
|
+
ts: Date.now()
|
|
142
|
+
}));
|
|
143
|
+
} catch {}
|
|
144
144
|
}
|
|
145
145
|
console.log = (...args) => {
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
_origLog(...args);
|
|
147
|
+
forwardLog("info", args);
|
|
148
148
|
};
|
|
149
149
|
console.warn = (...args) => {
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
_origWarn(...args);
|
|
151
|
+
forwardLog("warn", args);
|
|
152
152
|
};
|
|
153
153
|
console.error = (...args) => {
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
_origError(...args);
|
|
155
|
+
forwardLog("error", args);
|
|
156
156
|
};
|
|
157
157
|
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
|
-
|
|
189
|
-
};
|
|
158
|
+
if (ws?.readyState === WebSocket.OPEN || ws?.readyState === WebSocket.CONNECTING) return;
|
|
159
|
+
try {
|
|
160
|
+
ws = new WebSocket(DAEMON_WS_URL);
|
|
161
|
+
} catch {
|
|
162
|
+
scheduleReconnect();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
ws.onopen = () => {
|
|
166
|
+
console.log("[opencli] Connected to daemon");
|
|
167
|
+
reconnectAttempts = 0;
|
|
168
|
+
if (reconnectTimer) {
|
|
169
|
+
clearTimeout(reconnectTimer);
|
|
170
|
+
reconnectTimer = null;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
ws.onmessage = async (event) => {
|
|
174
|
+
try {
|
|
175
|
+
const result = await handleCommand(JSON.parse(event.data));
|
|
176
|
+
ws?.send(JSON.stringify(result));
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.error("[opencli] Message handling error:", err);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
ws.onclose = () => {
|
|
182
|
+
console.log("[opencli] Disconnected from daemon");
|
|
183
|
+
ws = null;
|
|
184
|
+
scheduleReconnect();
|
|
185
|
+
};
|
|
186
|
+
ws.onerror = () => {
|
|
187
|
+
ws?.close();
|
|
188
|
+
};
|
|
190
189
|
}
|
|
191
190
|
function scheduleReconnect() {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
191
|
+
if (reconnectTimer) return;
|
|
192
|
+
reconnectAttempts++;
|
|
193
|
+
const delay = Math.min(WS_RECONNECT_BASE_DELAY * Math.pow(2, reconnectAttempts - 1), WS_RECONNECT_MAX_DELAY);
|
|
194
|
+
reconnectTimer = setTimeout(() => {
|
|
195
|
+
reconnectTimer = null;
|
|
196
|
+
connect();
|
|
197
|
+
}, delay);
|
|
199
198
|
}
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
var automationSessions = /* @__PURE__ */ new Map();
|
|
200
|
+
var WINDOW_IDLE_TIMEOUT = 12e4;
|
|
202
201
|
function getWorkspaceKey(workspace) {
|
|
203
|
-
|
|
202
|
+
return workspace?.trim() || "default";
|
|
204
203
|
}
|
|
205
204
|
function resetWindowIdleTimer(workspace) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}, WINDOW_IDLE_TIMEOUT);
|
|
205
|
+
const session = automationSessions.get(workspace);
|
|
206
|
+
if (!session) return;
|
|
207
|
+
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
208
|
+
session.idleDeadlineAt = Date.now() + WINDOW_IDLE_TIMEOUT;
|
|
209
|
+
session.idleTimer = setTimeout(async () => {
|
|
210
|
+
const current = automationSessions.get(workspace);
|
|
211
|
+
if (!current) return;
|
|
212
|
+
try {
|
|
213
|
+
await chrome.windows.remove(current.windowId);
|
|
214
|
+
console.log(`[opencli] Automation window ${current.windowId} (${workspace}) closed (idle timeout)`);
|
|
215
|
+
} catch {}
|
|
216
|
+
automationSessions.delete(workspace);
|
|
217
|
+
}, WINDOW_IDLE_TIMEOUT);
|
|
220
218
|
}
|
|
219
|
+
/** Get or create the dedicated automation window. */
|
|
221
220
|
async function getAutomationWindow(workspace) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
resetWindowIdleTimer(workspace);
|
|
246
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
247
|
-
return session.windowId;
|
|
221
|
+
const existing = automationSessions.get(workspace);
|
|
222
|
+
if (existing) try {
|
|
223
|
+
await chrome.windows.get(existing.windowId);
|
|
224
|
+
return existing.windowId;
|
|
225
|
+
} catch {
|
|
226
|
+
automationSessions.delete(workspace);
|
|
227
|
+
}
|
|
228
|
+
const session = {
|
|
229
|
+
windowId: (await chrome.windows.create({
|
|
230
|
+
url: "data:text/html,<html></html>",
|
|
231
|
+
focused: false,
|
|
232
|
+
width: 1280,
|
|
233
|
+
height: 900,
|
|
234
|
+
type: "normal"
|
|
235
|
+
})).id,
|
|
236
|
+
idleTimer: null,
|
|
237
|
+
idleDeadlineAt: Date.now() + WINDOW_IDLE_TIMEOUT
|
|
238
|
+
};
|
|
239
|
+
automationSessions.set(workspace, session);
|
|
240
|
+
console.log(`[opencli] Created automation window ${session.windowId} (${workspace})`);
|
|
241
|
+
resetWindowIdleTimer(workspace);
|
|
242
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
243
|
+
return session.windowId;
|
|
248
244
|
}
|
|
249
245
|
chrome.windows.onRemoved.addListener((windowId) => {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
}
|
|
246
|
+
for (const [workspace, session] of automationSessions.entries()) if (session.windowId === windowId) {
|
|
247
|
+
console.log(`[opencli] Automation window closed (${workspace})`);
|
|
248
|
+
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
249
|
+
automationSessions.delete(workspace);
|
|
250
|
+
}
|
|
257
251
|
});
|
|
258
|
-
|
|
252
|
+
var initialized = false;
|
|
259
253
|
function initialize() {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
254
|
+
if (initialized) return;
|
|
255
|
+
initialized = true;
|
|
256
|
+
chrome.alarms.create("keepalive", { periodInMinutes: .4 });
|
|
257
|
+
registerListeners();
|
|
258
|
+
connect();
|
|
259
|
+
console.log("[opencli] OpenCLI extension initialized");
|
|
266
260
|
}
|
|
267
261
|
chrome.runtime.onInstalled.addListener(() => {
|
|
268
|
-
|
|
262
|
+
initialize();
|
|
269
263
|
});
|
|
270
264
|
chrome.runtime.onStartup.addListener(() => {
|
|
271
|
-
|
|
265
|
+
initialize();
|
|
272
266
|
});
|
|
273
267
|
chrome.alarms.onAlarm.addListener((alarm) => {
|
|
274
|
-
|
|
268
|
+
if (alarm.name === "keepalive") connect();
|
|
275
269
|
});
|
|
276
270
|
async function handleCommand(cmd) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
ok: false,
|
|
302
|
-
error: err instanceof Error ? err.message : String(err)
|
|
303
|
-
};
|
|
304
|
-
}
|
|
271
|
+
const workspace = getWorkspaceKey(cmd.workspace);
|
|
272
|
+
resetWindowIdleTimer(workspace);
|
|
273
|
+
try {
|
|
274
|
+
switch (cmd.action) {
|
|
275
|
+
case "exec": return await handleExec(cmd, workspace);
|
|
276
|
+
case "navigate": return await handleNavigate(cmd, workspace);
|
|
277
|
+
case "tabs": return await handleTabs(cmd, workspace);
|
|
278
|
+
case "cookies": return await handleCookies(cmd);
|
|
279
|
+
case "screenshot": return await handleScreenshot(cmd, workspace);
|
|
280
|
+
case "close-window": return await handleCloseWindow(cmd, workspace);
|
|
281
|
+
case "sessions": return await handleSessions(cmd);
|
|
282
|
+
default: return {
|
|
283
|
+
id: cmd.id,
|
|
284
|
+
ok: false,
|
|
285
|
+
error: `Unknown action: ${cmd.action}`
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
} catch (err) {
|
|
289
|
+
return {
|
|
290
|
+
id: cmd.id,
|
|
291
|
+
ok: false,
|
|
292
|
+
error: err instanceof Error ? err.message : String(err)
|
|
293
|
+
};
|
|
294
|
+
}
|
|
305
295
|
}
|
|
296
|
+
/** Check if a URL can be attached via CDP (not chrome:// or chrome-extension://) */
|
|
306
297
|
function isDebuggableUrl(url) {
|
|
307
|
-
|
|
308
|
-
|
|
298
|
+
if (!url) return true;
|
|
299
|
+
return !url.startsWith("chrome://") && !url.startsWith("chrome-extension://");
|
|
309
300
|
}
|
|
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
|
+
*/
|
|
310
306
|
async function resolveTabId(tabId, workspace) {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
307
|
+
if (tabId !== void 0) try {
|
|
308
|
+
const tab = await chrome.tabs.get(tabId);
|
|
309
|
+
if (isDebuggableUrl(tab.url)) return tabId;
|
|
310
|
+
console.warn(`[opencli] Tab ${tabId} URL is not debuggable (${tab.url}), re-resolving`);
|
|
311
|
+
} catch {
|
|
312
|
+
console.warn(`[opencli] Tab ${tabId} no longer exists, re-resolving`);
|
|
313
|
+
}
|
|
314
|
+
const windowId = await getAutomationWindow(workspace);
|
|
315
|
+
const tabs = await chrome.tabs.query({ windowId });
|
|
316
|
+
const debuggableTab = tabs.find((t) => t.id && isDebuggableUrl(t.url));
|
|
317
|
+
if (debuggableTab?.id) return debuggableTab.id;
|
|
318
|
+
const reuseTab = tabs.find((t) => t.id);
|
|
319
|
+
if (reuseTab?.id) {
|
|
320
|
+
await chrome.tabs.update(reuseTab.id, { url: "data:text/html,<html></html>" });
|
|
321
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
322
|
+
try {
|
|
323
|
+
const updated = await chrome.tabs.get(reuseTab.id);
|
|
324
|
+
if (isDebuggableUrl(updated.url)) return reuseTab.id;
|
|
325
|
+
console.warn(`[opencli] data: URI was intercepted (${updated.url}), creating fresh tab`);
|
|
326
|
+
} catch {}
|
|
327
|
+
}
|
|
328
|
+
const newTab = await chrome.tabs.create({
|
|
329
|
+
windowId,
|
|
330
|
+
url: "data:text/html,<html></html>",
|
|
331
|
+
active: true
|
|
332
|
+
});
|
|
333
|
+
if (!newTab.id) throw new Error("Failed to create tab in automation window");
|
|
334
|
+
return newTab.id;
|
|
338
335
|
}
|
|
339
336
|
async function listAutomationTabs(workspace) {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
337
|
+
const session = automationSessions.get(workspace);
|
|
338
|
+
if (!session) return [];
|
|
339
|
+
try {
|
|
340
|
+
return await chrome.tabs.query({ windowId: session.windowId });
|
|
341
|
+
} catch {
|
|
342
|
+
automationSessions.delete(workspace);
|
|
343
|
+
return [];
|
|
344
|
+
}
|
|
348
345
|
}
|
|
349
346
|
async function listAutomationWebTabs(workspace) {
|
|
350
|
-
|
|
351
|
-
return tabs.filter((tab) => isDebuggableUrl(tab.url));
|
|
347
|
+
return (await listAutomationTabs(workspace)).filter((tab) => isDebuggableUrl(tab.url));
|
|
352
348
|
}
|
|
353
349
|
async function handleExec(cmd, workspace) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
350
|
+
if (!cmd.code) return {
|
|
351
|
+
id: cmd.id,
|
|
352
|
+
ok: false,
|
|
353
|
+
error: "Missing code"
|
|
354
|
+
};
|
|
355
|
+
const tabId = await resolveTabId(cmd.tabId, workspace);
|
|
356
|
+
try {
|
|
357
|
+
const data = await evaluateAsync(tabId, cmd.code);
|
|
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
|
+
}
|
|
362
370
|
}
|
|
363
371
|
async function handleNavigate(cmd, workspace) {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
|
|
372
|
+
if (!cmd.url) return {
|
|
373
|
+
id: cmd.id,
|
|
374
|
+
ok: false,
|
|
375
|
+
error: "Missing url"
|
|
376
|
+
};
|
|
377
|
+
const tabId = await resolveTabId(cmd.tabId, workspace);
|
|
378
|
+
const beforeUrl = (await chrome.tabs.get(tabId)).url ?? "";
|
|
379
|
+
const targetUrl = cmd.url;
|
|
380
|
+
await detach(tabId);
|
|
381
|
+
await chrome.tabs.update(tabId, { url: targetUrl });
|
|
382
|
+
let timedOut = false;
|
|
383
|
+
await new Promise((resolve) => {
|
|
384
|
+
let urlChanged = false;
|
|
385
|
+
const listener = (id, info, tab) => {
|
|
386
|
+
if (id !== tabId) return;
|
|
387
|
+
if (info.url && info.url !== beforeUrl) urlChanged = true;
|
|
388
|
+
if (urlChanged && info.status === "complete") {
|
|
389
|
+
chrome.tabs.onUpdated.removeListener(listener);
|
|
390
|
+
resolve();
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
chrome.tabs.onUpdated.addListener(listener);
|
|
394
|
+
setTimeout(async () => {
|
|
395
|
+
try {
|
|
396
|
+
const currentTab = await chrome.tabs.get(tabId);
|
|
397
|
+
if (currentTab.url !== beforeUrl && currentTab.status === "complete") {
|
|
398
|
+
chrome.tabs.onUpdated.removeListener(listener);
|
|
399
|
+
resolve();
|
|
400
|
+
}
|
|
401
|
+
} catch {}
|
|
402
|
+
}, 100);
|
|
403
|
+
setTimeout(() => {
|
|
404
|
+
chrome.tabs.onUpdated.removeListener(listener);
|
|
405
|
+
timedOut = true;
|
|
406
|
+
console.warn(`[opencli] Navigate to ${targetUrl} timed out after 15s`);
|
|
407
|
+
resolve();
|
|
408
|
+
}, 15e3);
|
|
409
|
+
});
|
|
410
|
+
const tab = await chrome.tabs.get(tabId);
|
|
411
|
+
return {
|
|
412
|
+
id: cmd.id,
|
|
413
|
+
ok: true,
|
|
414
|
+
data: {
|
|
415
|
+
title: tab.title,
|
|
416
|
+
url: tab.url,
|
|
417
|
+
tabId,
|
|
418
|
+
timedOut
|
|
419
|
+
}
|
|
420
|
+
};
|
|
407
421
|
}
|
|
408
422
|
async function handleTabs(cmd, workspace) {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
|
|
423
|
+
switch (cmd.op) {
|
|
424
|
+
case "list": {
|
|
425
|
+
const data = (await listAutomationWebTabs(workspace)).map((t, i) => ({
|
|
426
|
+
index: i,
|
|
427
|
+
tabId: t.id,
|
|
428
|
+
url: t.url,
|
|
429
|
+
title: t.title,
|
|
430
|
+
active: t.active
|
|
431
|
+
}));
|
|
432
|
+
return {
|
|
433
|
+
id: cmd.id,
|
|
434
|
+
ok: true,
|
|
435
|
+
data
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
case "new": {
|
|
439
|
+
const windowId = await getAutomationWindow(workspace);
|
|
440
|
+
const tab = await chrome.tabs.create({
|
|
441
|
+
windowId,
|
|
442
|
+
url: cmd.url ?? "data:text/html,<html></html>",
|
|
443
|
+
active: true
|
|
444
|
+
});
|
|
445
|
+
return {
|
|
446
|
+
id: cmd.id,
|
|
447
|
+
ok: true,
|
|
448
|
+
data: {
|
|
449
|
+
tabId: tab.id,
|
|
450
|
+
url: tab.url
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
case "close": {
|
|
455
|
+
if (cmd.index !== void 0) {
|
|
456
|
+
const target = (await listAutomationWebTabs(workspace))[cmd.index];
|
|
457
|
+
if (!target?.id) return {
|
|
458
|
+
id: cmd.id,
|
|
459
|
+
ok: false,
|
|
460
|
+
error: `Tab index ${cmd.index} not found`
|
|
461
|
+
};
|
|
462
|
+
await chrome.tabs.remove(target.id);
|
|
463
|
+
await detach(target.id);
|
|
464
|
+
return {
|
|
465
|
+
id: cmd.id,
|
|
466
|
+
ok: true,
|
|
467
|
+
data: { closed: target.id }
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const tabId = await resolveTabId(cmd.tabId, workspace);
|
|
471
|
+
await chrome.tabs.remove(tabId);
|
|
472
|
+
await detach(tabId);
|
|
473
|
+
return {
|
|
474
|
+
id: cmd.id,
|
|
475
|
+
ok: true,
|
|
476
|
+
data: { closed: tabId }
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
case "select": {
|
|
480
|
+
if (cmd.index === void 0 && cmd.tabId === void 0) return {
|
|
481
|
+
id: cmd.id,
|
|
482
|
+
ok: false,
|
|
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
|
+
}
|
|
456
512
|
}
|
|
457
513
|
async function handleCookies(cmd) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
514
|
+
const details = {};
|
|
515
|
+
if (cmd.domain) details.domain = cmd.domain;
|
|
516
|
+
if (cmd.url) details.url = cmd.url;
|
|
517
|
+
const data = (await chrome.cookies.getAll(details)).map((c) => ({
|
|
518
|
+
name: c.name,
|
|
519
|
+
value: c.value,
|
|
520
|
+
domain: c.domain,
|
|
521
|
+
path: c.path,
|
|
522
|
+
secure: c.secure,
|
|
523
|
+
httpOnly: c.httpOnly,
|
|
524
|
+
expirationDate: c.expirationDate
|
|
525
|
+
}));
|
|
526
|
+
return {
|
|
527
|
+
id: cmd.id,
|
|
528
|
+
ok: true,
|
|
529
|
+
data
|
|
530
|
+
};
|
|
472
531
|
}
|
|
473
532
|
async function handleScreenshot(cmd, workspace) {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
533
|
+
const tabId = await resolveTabId(cmd.tabId, workspace);
|
|
534
|
+
try {
|
|
535
|
+
const data = await screenshot(tabId, {
|
|
536
|
+
format: cmd.format,
|
|
537
|
+
quality: cmd.quality,
|
|
538
|
+
fullPage: cmd.fullPage
|
|
539
|
+
});
|
|
540
|
+
return {
|
|
541
|
+
id: cmd.id,
|
|
542
|
+
ok: true,
|
|
543
|
+
data
|
|
544
|
+
};
|
|
545
|
+
} catch (err) {
|
|
546
|
+
return {
|
|
547
|
+
id: cmd.id,
|
|
548
|
+
ok: false,
|
|
549
|
+
error: err instanceof Error ? err.message : String(err)
|
|
550
|
+
};
|
|
551
|
+
}
|
|
485
552
|
}
|
|
486
553
|
async function handleCloseWindow(cmd, workspace) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
554
|
+
const session = automationSessions.get(workspace);
|
|
555
|
+
if (session) {
|
|
556
|
+
try {
|
|
557
|
+
await chrome.windows.remove(session.windowId);
|
|
558
|
+
} catch {}
|
|
559
|
+
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
560
|
+
automationSessions.delete(workspace);
|
|
561
|
+
}
|
|
562
|
+
return {
|
|
563
|
+
id: cmd.id,
|
|
564
|
+
ok: true,
|
|
565
|
+
data: { closed: true }
|
|
566
|
+
};
|
|
497
567
|
}
|
|
498
568
|
async function handleSessions(cmd) {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
569
|
+
const now = Date.now();
|
|
570
|
+
const data = await Promise.all([...automationSessions.entries()].map(async ([workspace, session]) => ({
|
|
571
|
+
workspace,
|
|
572
|
+
windowId: session.windowId,
|
|
573
|
+
tabCount: (await chrome.tabs.query({ windowId: session.windowId })).filter((tab) => isDebuggableUrl(tab.url)).length,
|
|
574
|
+
idleMsRemaining: Math.max(0, session.idleDeadlineAt - now)
|
|
575
|
+
})));
|
|
576
|
+
return {
|
|
577
|
+
id: cmd.id,
|
|
578
|
+
ok: true,
|
|
579
|
+
data
|
|
580
|
+
};
|
|
507
581
|
}
|
|
582
|
+
//#endregion
|