@jackwener/opencli 1.6.1 → 1.6.2
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/CONTRIBUTING.md +1 -1
- package/README.md +27 -45
- package/README.zh-CN.md +32 -34
- package/autoresearch/browse-tasks.json +18 -20
- package/autoresearch/commands/debug.ts +163 -0
- package/autoresearch/commands/fix.ts +145 -0
- package/autoresearch/commands/plan.ts +88 -0
- package/autoresearch/commands/run.ts +138 -0
- package/autoresearch/config.ts +82 -0
- package/autoresearch/engine.ts +359 -0
- package/autoresearch/eval-all.ts +127 -0
- package/autoresearch/eval-browse.ts +1 -1
- package/autoresearch/eval-publish.ts +238 -0
- package/autoresearch/eval-save.ts +249 -0
- package/autoresearch/eval-skill.ts +14 -8
- package/autoresearch/eval-v2ex.ts +220 -0
- package/autoresearch/eval-zhihu.ts +230 -0
- package/autoresearch/logger.ts +69 -0
- package/autoresearch/presets/combined-reliability.ts +27 -0
- package/autoresearch/presets/index.ts +23 -0
- package/autoresearch/presets/operate-reliability.ts +24 -0
- package/autoresearch/presets/save-reliability.ts +26 -0
- package/autoresearch/presets/skill-quality.ts +20 -0
- package/autoresearch/presets/v2ex-reliability.ts +24 -0
- package/autoresearch/presets/zhihu-reliability.ts +25 -0
- package/autoresearch/publish-tasks.json +345 -0
- package/autoresearch/run-save.sh +11 -0
- package/autoresearch/save-adapters/xhs-explore-deep.ts +64 -0
- package/autoresearch/save-adapters/xhs-note-comments.ts +61 -0
- package/autoresearch/save-adapters/xhs-search-full.ts +62 -0
- package/autoresearch/save-adapters/zhihu-hot-detail.ts +52 -0
- package/autoresearch/save-adapters/zhihu-question-full.ts +57 -0
- package/autoresearch/save-adapters/zhihu-search-detail.ts +53 -0
- package/autoresearch/save-tasks.json +281 -0
- package/autoresearch/v2ex-tasks.json +899 -0
- package/autoresearch/zhihu-tasks.json +848 -0
- package/dist/browser/base-page.d.ts +4 -2
- package/dist/browser/base-page.js +37 -4
- package/dist/browser/bridge.js +10 -8
- package/dist/browser/cdp.js +2 -6
- package/dist/browser/daemon-client.d.ts +11 -1
- package/dist/browser/daemon-client.js +3 -0
- package/dist/browser/dom-helpers.d.ts +4 -2
- package/dist/browser/dom-helpers.js +42 -31
- package/dist/browser/dom-snapshot.js +23 -1
- package/dist/browser/page.d.ts +7 -2
- package/dist/browser/page.js +112 -30
- package/dist/browser.test.js +1 -1
- package/dist/build-manifest.d.ts +1 -0
- package/dist/build-manifest.js +1 -0
- package/dist/cli-manifest.json +1135 -184
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +48 -7
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +88 -0
- package/dist/clis/1688/item.d.ts +70 -0
- package/dist/clis/1688/item.js +187 -0
- package/dist/clis/1688/item.test.d.ts +1 -0
- package/dist/clis/1688/item.test.js +67 -0
- package/dist/clis/1688/search.d.ts +56 -0
- package/dist/clis/1688/search.js +309 -0
- package/dist/clis/1688/search.test.d.ts +1 -0
- package/dist/clis/1688/search.test.js +75 -0
- package/dist/clis/1688/shared.d.ts +112 -0
- package/dist/clis/1688/shared.js +514 -0
- package/dist/clis/1688/shared.test.d.ts +1 -0
- package/dist/clis/1688/shared.test.js +57 -0
- package/dist/clis/1688/store.d.ts +45 -0
- package/dist/clis/1688/store.js +226 -0
- package/dist/clis/1688/store.test.d.ts +1 -0
- package/dist/clis/1688/store.test.js +62 -0
- package/dist/clis/amazon/bestsellers.d.ts +0 -20
- package/dist/clis/amazon/bestsellers.js +6 -129
- package/dist/clis/amazon/bestsellers.test.js +12 -3
- package/dist/clis/amazon/movers-shakers.d.ts +1 -0
- package/dist/clis/amazon/movers-shakers.js +7 -0
- package/dist/clis/amazon/new-releases.d.ts +1 -0
- package/dist/clis/amazon/new-releases.js +7 -0
- package/dist/clis/amazon/rankings.d.ts +59 -0
- package/dist/clis/amazon/rankings.js +226 -0
- package/dist/clis/amazon/rankings.test.d.ts +1 -0
- package/dist/clis/amazon/rankings.test.js +41 -0
- package/dist/clis/amazon/shared.d.ts +11 -0
- package/dist/clis/amazon/shared.js +121 -11
- package/dist/clis/amazon/shared.test.js +11 -0
- package/dist/clis/bilibili/comments.js +2 -2
- package/dist/clis/bilibili/comments.test.js +3 -2
- package/dist/clis/bilibili/download.js +2 -1
- package/dist/clis/bilibili/subtitle.js +4 -3
- package/dist/clis/bilibili/subtitle.test.js +2 -1
- package/dist/clis/bilibili/utils.d.ts +5 -0
- package/dist/clis/bilibili/utils.js +30 -0
- package/dist/clis/bilibili/utils.test.d.ts +1 -0
- package/dist/clis/bilibili/utils.test.js +17 -0
- package/dist/clis/douban/marks.js +1 -1
- package/dist/clis/douban/subject.yaml +50 -19
- package/dist/clis/doubao/utils.js +32 -12
- package/dist/clis/douyin/_shared/browser-fetch.test.js +0 -1
- package/dist/clis/douyin/_shared/transcode.test.js +0 -2
- package/dist/clis/douyin/draft.test.js +0 -2
- package/dist/clis/facebook/search.test.js +0 -2
- package/dist/clis/gemini/ask.js +9 -3
- package/dist/clis/gemini/ask.test.d.ts +1 -0
- package/dist/clis/gemini/ask.test.js +100 -0
- package/dist/clis/gemini/reply-state.test.d.ts +1 -0
- package/dist/clis/gemini/reply-state.test.js +641 -0
- package/dist/clis/gemini/utils.d.ts +44 -1
- package/dist/clis/gemini/utils.js +528 -61
- package/dist/clis/gemini/utils.test.js +149 -2
- package/dist/clis/hupu/detail.d.ts +1 -0
- package/dist/clis/hupu/detail.js +72 -0
- package/dist/clis/hupu/hot.yaml +43 -0
- package/dist/clis/hupu/like.d.ts +1 -0
- package/dist/clis/hupu/like.js +75 -0
- package/dist/clis/hupu/reply.d.ts +1 -0
- package/dist/clis/hupu/reply.js +71 -0
- package/dist/clis/hupu/search.d.ts +1 -0
- package/dist/clis/hupu/search.js +59 -0
- package/dist/clis/hupu/unlike.d.ts +1 -0
- package/dist/clis/hupu/unlike.js +75 -0
- package/dist/clis/hupu/utils.d.ts +20 -0
- package/dist/clis/hupu/utils.js +319 -0
- package/dist/clis/instagram/_shared/private-publish.d.ts +138 -0
- package/dist/clis/instagram/_shared/private-publish.js +1030 -0
- package/dist/clis/instagram/_shared/private-publish.test.d.ts +1 -0
- package/dist/clis/instagram/_shared/private-publish.test.js +705 -0
- package/dist/clis/instagram/_shared/protocol-capture.d.ts +26 -0
- package/dist/clis/instagram/_shared/protocol-capture.js +282 -0
- package/dist/clis/instagram/_shared/protocol-capture.test.d.ts +1 -0
- package/dist/clis/instagram/_shared/protocol-capture.test.js +114 -0
- package/dist/clis/instagram/_shared/runtime-info.d.ts +9 -0
- package/dist/clis/instagram/_shared/runtime-info.js +81 -0
- package/dist/clis/instagram/note.d.ts +1 -0
- package/dist/clis/instagram/note.js +222 -0
- package/dist/clis/instagram/note.test.d.ts +1 -0
- package/dist/clis/instagram/note.test.js +81 -0
- package/dist/clis/instagram/post.d.ts +4 -0
- package/dist/clis/instagram/post.js +1496 -0
- package/dist/clis/instagram/post.test.d.ts +1 -0
- package/dist/clis/instagram/post.test.js +1647 -0
- package/dist/clis/instagram/reel.d.ts +1 -0
- package/dist/clis/instagram/reel.js +826 -0
- package/dist/clis/instagram/reel.test.d.ts +1 -0
- package/dist/clis/instagram/reel.test.js +167 -0
- package/dist/clis/instagram/story.d.ts +1 -0
- package/dist/clis/instagram/story.js +115 -0
- package/dist/clis/instagram/story.test.d.ts +1 -0
- package/dist/clis/instagram/story.test.js +167 -0
- package/dist/clis/sinafinance/stock-rank.d.ts +4 -0
- package/dist/clis/sinafinance/stock-rank.js +65 -0
- package/dist/clis/substack/utils.test.js +0 -2
- package/dist/clis/twitter/post.js +72 -45
- package/dist/clis/twitter/post.test.d.ts +1 -0
- package/dist/clis/twitter/post.test.js +116 -0
- package/dist/clis/twitter/reply.d.ts +12 -0
- package/dist/clis/twitter/reply.js +257 -35
- package/dist/clis/twitter/reply.test.d.ts +1 -0
- package/dist/clis/twitter/reply.test.js +151 -0
- package/dist/clis/xianyu/chat.d.ts +7 -0
- package/dist/clis/xianyu/chat.js +146 -0
- package/dist/clis/xianyu/chat.test.d.ts +1 -0
- package/dist/clis/xianyu/chat.test.js +15 -0
- package/dist/clis/xianyu/item.d.ts +7 -0
- package/dist/clis/xianyu/item.js +152 -0
- package/dist/clis/xianyu/item.test.d.ts +1 -0
- package/dist/clis/xianyu/item.test.js +56 -0
- package/dist/clis/xianyu/search.d.ts +10 -0
- package/dist/clis/xianyu/search.js +134 -0
- package/dist/clis/xianyu/search.test.d.ts +1 -0
- package/dist/clis/xianyu/search.test.js +17 -0
- package/dist/clis/xianyu/utils.d.ts +1 -0
- package/dist/clis/xianyu/utils.js +8 -0
- package/dist/clis/xiaoe/catalog.yaml +129 -0
- package/dist/clis/xiaoe/content.yaml +43 -0
- package/dist/clis/xiaoe/courses.yaml +73 -0
- package/dist/clis/xiaoe/detail.yaml +39 -0
- package/dist/clis/xiaoe/play-url.yaml +124 -0
- package/dist/clis/xiaohongshu/comments.test.js +0 -2
- package/dist/clis/xiaohongshu/creator-note-detail.test.js +0 -2
- package/dist/clis/xiaohongshu/creator-notes.test.js +0 -2
- package/dist/clis/xiaohongshu/download.test.js +0 -2
- package/dist/clis/xiaohongshu/note.test.js +0 -2
- package/dist/clis/xiaohongshu/publish.test.js +0 -2
- package/dist/clis/xiaohongshu/search.js +29 -20
- package/dist/clis/xiaohongshu/search.test.js +56 -48
- package/dist/clis/yuanbao/ask.d.ts +21 -0
- package/dist/clis/yuanbao/ask.js +427 -0
- package/dist/clis/yuanbao/ask.test.d.ts +1 -0
- package/dist/clis/yuanbao/ask.test.js +124 -0
- package/dist/clis/yuanbao/new.d.ts +1 -0
- package/dist/clis/yuanbao/new.js +70 -0
- package/dist/clis/yuanbao/new.test.d.ts +1 -0
- package/dist/clis/yuanbao/new.test.js +30 -0
- package/dist/clis/yuanbao/shared.d.ts +13 -0
- package/dist/clis/yuanbao/shared.js +49 -0
- package/dist/clis/zhihu/question.js +30 -19
- package/dist/clis/zhihu/question.test.js +34 -16
- package/dist/commanderAdapter.js +8 -4
- package/dist/commanderAdapter.test.js +42 -0
- package/dist/completion.js +3 -1
- package/dist/completion.test.d.ts +1 -0
- package/dist/completion.test.js +23 -0
- package/dist/doctor.js +1 -1
- package/dist/electron-apps.d.ts +2 -0
- package/dist/electron-apps.js +7 -1
- package/dist/errors.js +1 -1
- package/dist/execution.js +25 -35
- package/dist/explore.js +1 -1
- package/dist/launcher.d.ts +4 -0
- package/dist/launcher.js +64 -8
- package/dist/launcher.test.js +88 -7
- package/dist/output.d.ts +2 -0
- package/dist/output.js +10 -1
- package/dist/output.test.d.ts +0 -3
- package/dist/output.test.js +59 -92
- package/dist/pipeline/executor.test.js +0 -2
- package/dist/pipeline/steps/download.test.js +0 -2
- package/dist/registry.d.ts +2 -0
- package/dist/serialization.d.ts +1 -0
- package/dist/serialization.js +1 -0
- package/dist/types.d.ts +9 -2
- package/docs/.vitepress/config.mts +4 -0
- package/docs/adapters/browser/1688.md +52 -0
- package/docs/adapters/browser/36kr.md +2 -1
- package/docs/adapters/browser/doubao.md +5 -1
- package/docs/adapters/browser/hupu.md +53 -0
- package/docs/adapters/browser/sinafinance.md +32 -2
- package/docs/adapters/browser/weibo.md +6 -1
- package/docs/adapters/browser/wikipedia.md +2 -0
- package/docs/adapters/browser/xianyu.md +42 -0
- package/docs/adapters/browser/xiaoe.md +44 -0
- package/docs/adapters/browser/yuanbao.md +64 -0
- package/docs/adapters/index.md +14 -5
- package/docs/comparison.md +1 -1
- package/docs/developer/ai-workflow.md +2 -2
- package/docs/developer/contributing.md +1 -1
- package/docs/developer/testing.md +2 -0
- package/docs/guide/plugins.md +1 -0
- package/docs/guide/troubleshooting.md +11 -0
- package/docs/superpowers/specs/2026-04-03-v2ex-autoresearch-design.md +41 -0
- package/docs/zh/guide/plugins.md +1 -0
- package/extension/dist/background.js +1127 -0
- package/extension/src/background.test.ts +39 -0
- package/extension/src/background.ts +223 -34
- package/extension/src/cdp.ts +194 -4
- package/extension/src/protocol.ts +22 -1
- package/package.json +3 -2
- package/scripts/postinstall.js +1 -1
- package/skills/opencli-explorer/SKILL.md +1 -1
- package/skills/opencli-oneshot/SKILL.md +2 -2
- package/skills/opencli-operate/SKILL.md +120 -27
- package/skills/opencli-usage/SKILL.md +31 -20
- package/skills/opencli-usage/browser.md +114 -16
- package/skills/opencli-usage/public-api.md +32 -3
- package/skills/smart-search/SKILL.md +156 -0
- package/skills/smart-search/references/sources-ai.md +74 -0
- package/skills/smart-search/references/sources-info.md +43 -0
- package/skills/smart-search/references/sources-media.md +50 -0
- package/skills/smart-search/references/sources-other.md +42 -0
- package/skills/smart-search/references/sources-shopping.md +31 -0
- package/skills/smart-search/references/sources-social.md +51 -0
- package/skills/smart-search/references/sources-tech.md +42 -0
- package/skills/smart-search/references/sources-travel.md +20 -0
- package/src/browser/base-page.ts +41 -6
- package/src/browser/bridge.ts +11 -8
- package/src/browser/cdp.ts +1 -8
- package/src/browser/daemon-client.ts +11 -1
- package/src/browser/dom-helpers.ts +43 -31
- package/src/browser/dom-snapshot.ts +23 -1
- package/src/browser/page.ts +115 -31
- package/src/browser.test.ts +1 -1
- package/src/build-manifest.ts +2 -0
- package/src/cli.test.ts +133 -0
- package/src/cli.ts +73 -11
- package/src/clis/1688/item.test.ts +69 -0
- package/src/clis/1688/item.ts +282 -0
- package/src/clis/1688/search.test.ts +81 -0
- package/src/clis/1688/search.ts +402 -0
- package/src/clis/1688/shared.test.ts +75 -0
- package/src/clis/1688/shared.ts +623 -0
- package/src/clis/1688/store.test.ts +69 -0
- package/src/clis/1688/store.ts +300 -0
- package/src/clis/amazon/bestsellers.test.ts +12 -3
- package/src/clis/amazon/bestsellers.ts +6 -178
- package/src/clis/amazon/movers-shakers.ts +8 -0
- package/src/clis/amazon/new-releases.ts +8 -0
- package/src/clis/amazon/rankings.test.ts +47 -0
- package/src/clis/amazon/rankings.ts +312 -0
- package/src/clis/amazon/shared.test.ts +16 -0
- package/src/clis/amazon/shared.ts +134 -12
- package/src/clis/bilibili/comments.test.ts +4 -3
- package/src/clis/bilibili/comments.ts +2 -2
- package/src/clis/bilibili/download.ts +2 -1
- package/src/clis/bilibili/subtitle.test.ts +2 -1
- package/src/clis/bilibili/subtitle.ts +4 -3
- package/src/clis/bilibili/utils.test.ts +21 -0
- package/src/clis/bilibili/utils.ts +27 -0
- package/src/clis/douban/marks.ts +1 -1
- package/src/clis/douban/subject.yaml +50 -19
- package/src/clis/doubao/utils.ts +32 -12
- package/src/clis/douyin/_shared/browser-fetch.test.ts +0 -1
- package/src/clis/douyin/_shared/transcode.test.ts +0 -2
- package/src/clis/douyin/draft.test.ts +0 -2
- package/src/clis/facebook/search.test.ts +0 -2
- package/src/clis/gemini/ask.test.ts +116 -0
- package/src/clis/gemini/ask.ts +10 -3
- package/src/clis/gemini/reply-state.test.ts +708 -0
- package/src/clis/gemini/utils.test.ts +184 -2
- package/src/clis/gemini/utils.ts +588 -60
- package/src/clis/hupu/detail.ts +126 -0
- package/src/clis/hupu/hot.yaml +43 -0
- package/src/clis/hupu/like.ts +76 -0
- package/src/clis/hupu/reply.ts +76 -0
- package/src/clis/hupu/search.ts +95 -0
- package/src/clis/hupu/unlike.ts +76 -0
- package/src/clis/hupu/utils.ts +381 -0
- package/src/clis/instagram/_shared/private-publish.test.ts +827 -0
- package/src/clis/instagram/_shared/private-publish.ts +1303 -0
- package/src/clis/instagram/_shared/protocol-capture.test.ts +148 -0
- package/src/clis/instagram/_shared/protocol-capture.ts +321 -0
- package/src/clis/instagram/_shared/runtime-info.ts +91 -0
- package/src/clis/instagram/note.test.ts +96 -0
- package/src/clis/instagram/note.ts +254 -0
- package/src/clis/instagram/post.test.ts +1716 -0
- package/src/clis/instagram/post.ts +1620 -0
- package/src/clis/instagram/reel.test.ts +191 -0
- package/src/clis/instagram/reel.ts +886 -0
- package/src/clis/instagram/story.test.ts +191 -0
- package/src/clis/instagram/story.ts +151 -0
- package/src/clis/sinafinance/stock-rank.ts +68 -0
- package/src/clis/substack/utils.test.ts +0 -2
- package/src/clis/twitter/post.test.ts +157 -0
- package/src/clis/twitter/post.ts +82 -48
- package/src/clis/twitter/reply.test.ts +177 -0
- package/src/clis/twitter/reply.ts +285 -39
- package/src/clis/xianyu/chat.test.ts +20 -0
- package/src/clis/xianyu/chat.ts +175 -0
- package/src/clis/xianyu/item.test.ts +67 -0
- package/src/clis/xianyu/item.ts +172 -0
- package/src/clis/xianyu/search.test.ts +22 -0
- package/src/clis/xianyu/search.ts +151 -0
- package/src/clis/xianyu/utils.ts +9 -0
- package/src/clis/xiaoe/catalog.yaml +129 -0
- package/src/clis/xiaoe/content.yaml +43 -0
- package/src/clis/xiaoe/courses.yaml +73 -0
- package/src/clis/xiaoe/detail.yaml +39 -0
- package/src/clis/xiaoe/play-url.yaml +124 -0
- package/src/clis/xiaohongshu/comments.test.ts +0 -2
- package/src/clis/xiaohongshu/creator-note-detail.test.ts +0 -2
- package/src/clis/xiaohongshu/creator-notes.test.ts +0 -2
- package/src/clis/xiaohongshu/download.test.ts +0 -2
- package/src/clis/xiaohongshu/note.test.ts +0 -2
- package/src/clis/xiaohongshu/publish.test.ts +0 -2
- package/src/clis/xiaohongshu/search.test.ts +59 -48
- package/src/clis/xiaohongshu/search.ts +31 -21
- package/src/clis/yuanbao/ask.test.ts +156 -0
- package/src/clis/yuanbao/ask.ts +522 -0
- package/src/clis/yuanbao/new.test.ts +36 -0
- package/src/clis/yuanbao/new.ts +81 -0
- package/src/clis/yuanbao/shared.ts +57 -0
- package/src/clis/zhihu/question.test.ts +42 -17
- package/src/clis/zhihu/question.ts +31 -26
- package/src/commanderAdapter.test.ts +51 -0
- package/src/commanderAdapter.ts +8 -4
- package/src/completion.test.ts +30 -0
- package/src/completion.ts +3 -1
- package/src/doctor.ts +1 -1
- package/src/electron-apps.ts +9 -1
- package/src/errors.ts +1 -1
- package/src/execution.ts +26 -30
- package/src/explore.ts +1 -1
- package/src/launcher.test.ts +121 -7
- package/src/launcher.ts +87 -9
- package/src/output.test.ts +50 -90
- package/src/output.ts +10 -1
- package/src/pipeline/executor.test.ts +0 -2
- package/src/pipeline/steps/download.test.ts +0 -2
- package/src/registry.ts +2 -0
- package/src/serialization.ts +2 -0
- package/src/types.ts +9 -2
- package/tests/e2e/browser-auth.test.ts +9 -0
- package/CLI-EXPLORER.md +0 -724
- package/CLI-ONESHOT.md +0 -216
- package/SKILL.md +0 -59
|
@@ -73,6 +73,12 @@ function createChromeMock() {
|
|
|
73
73
|
if (!tab) throw new Error(`Unknown tab ${tabId}`);
|
|
74
74
|
return tab;
|
|
75
75
|
}),
|
|
76
|
+
move: vi.fn(async (tabId: number, moveProps: { windowId: number; index: number }) => {
|
|
77
|
+
const tab = tabs.find((entry) => entry.id === tabId);
|
|
78
|
+
if (!tab) throw new Error(`Unknown tab ${tabId}`);
|
|
79
|
+
tab.windowId = moveProps.windowId;
|
|
80
|
+
return tab;
|
|
81
|
+
}),
|
|
76
82
|
onUpdated: { addListener: vi.fn(), removeListener: vi.fn() } as Listener<(id: number, info: chrome.tabs.TabChangeInfo) => void>,
|
|
77
83
|
},
|
|
78
84
|
windows: {
|
|
@@ -219,6 +225,39 @@ describe('background tab isolation', () => {
|
|
|
219
225
|
}));
|
|
220
226
|
});
|
|
221
227
|
|
|
228
|
+
it('moves drifted tab back to automation window instead of creating a new one', async () => {
|
|
229
|
+
const { chrome, tabs } = createChromeMock();
|
|
230
|
+
// Tab 1 belongs to automation window 1 but drifted to window 2
|
|
231
|
+
tabs[0].windowId = 2;
|
|
232
|
+
tabs[0].url = 'https://twitter.com/home';
|
|
233
|
+
vi.stubGlobal('chrome', chrome);
|
|
234
|
+
|
|
235
|
+
const mod = await import('./background');
|
|
236
|
+
mod.__test__.setAutomationWindowId('site:twitter', 1);
|
|
237
|
+
|
|
238
|
+
const tabId = await mod.__test__.resolveTabId(1, 'site:twitter');
|
|
239
|
+
|
|
240
|
+
// Should have moved tab 1 back to window 1 and reused it
|
|
241
|
+
expect(chrome.tabs.move).toHaveBeenCalledWith(1, { windowId: 1, index: -1 });
|
|
242
|
+
expect(tabId).toBe(1);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('falls through to re-resolve when drifted tab move fails', async () => {
|
|
246
|
+
const { chrome, tabs } = createChromeMock();
|
|
247
|
+
tabs[0].windowId = 2;
|
|
248
|
+
tabs[0].url = 'https://twitter.com/home';
|
|
249
|
+
// Make move fail
|
|
250
|
+
chrome.tabs.move = vi.fn(async () => { throw new Error('Cannot move tab'); });
|
|
251
|
+
vi.stubGlobal('chrome', chrome);
|
|
252
|
+
|
|
253
|
+
const mod = await import('./background');
|
|
254
|
+
mod.__test__.setAutomationWindowId('site:twitter', 1);
|
|
255
|
+
|
|
256
|
+
// Should still resolve (by finding/creating a tab in the correct window)
|
|
257
|
+
const tabId = await mod.__test__.resolveTabId(1, 'site:twitter');
|
|
258
|
+
expect(typeof tabId).toBe('number');
|
|
259
|
+
});
|
|
260
|
+
|
|
222
261
|
it('idle timeout closes the automation window for site:notebooklm', async () => {
|
|
223
262
|
const { chrome, tabs } = createChromeMock();
|
|
224
263
|
tabs[0].url = 'https://notebooklm.google.com/';
|
|
@@ -117,6 +117,8 @@ type AutomationSession = {
|
|
|
117
117
|
windowId: number;
|
|
118
118
|
idleTimer: ReturnType<typeof setTimeout> | null;
|
|
119
119
|
idleDeadlineAt: number;
|
|
120
|
+
owned: boolean;
|
|
121
|
+
preferredTabId: number | null;
|
|
120
122
|
};
|
|
121
123
|
|
|
122
124
|
const automationSessions = new Map<string, AutomationSession>();
|
|
@@ -134,6 +136,11 @@ function resetWindowIdleTimer(workspace: string): void {
|
|
|
134
136
|
session.idleTimer = setTimeout(async () => {
|
|
135
137
|
const current = automationSessions.get(workspace);
|
|
136
138
|
if (!current) return;
|
|
139
|
+
if (!current.owned) {
|
|
140
|
+
console.log(`[opencli] Borrowed workspace ${workspace} detached from window ${current.windowId} (idle timeout)`);
|
|
141
|
+
automationSessions.delete(workspace);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
137
144
|
try {
|
|
138
145
|
await chrome.windows.remove(current.windowId);
|
|
139
146
|
console.log(`[opencli] Automation window ${current.windowId} (${workspace}) closed (idle timeout)`);
|
|
@@ -144,8 +151,11 @@ function resetWindowIdleTimer(workspace: string): void {
|
|
|
144
151
|
}, WINDOW_IDLE_TIMEOUT);
|
|
145
152
|
}
|
|
146
153
|
|
|
147
|
-
/** Get or create the dedicated automation window.
|
|
148
|
-
|
|
154
|
+
/** Get or create the dedicated automation window.
|
|
155
|
+
* @param initialUrl — if provided (http/https), used as the initial page instead of about:blank.
|
|
156
|
+
* This avoids an extra blank-page→target-domain navigation on first command.
|
|
157
|
+
*/
|
|
158
|
+
async function getAutomationWindow(workspace: string, initialUrl?: string): Promise<number> {
|
|
149
159
|
// Check if our window is still alive
|
|
150
160
|
const existing = automationSessions.get(workspace);
|
|
151
161
|
if (existing) {
|
|
@@ -158,12 +168,13 @@ async function getAutomationWindow(workspace: string): Promise<number> {
|
|
|
158
168
|
}
|
|
159
169
|
}
|
|
160
170
|
|
|
161
|
-
//
|
|
162
|
-
|
|
171
|
+
// Use the target URL directly if it's a safe navigation URL, otherwise fall back to about:blank.
|
|
172
|
+
const startUrl = (initialUrl && isSafeNavigationUrl(initialUrl)) ? initialUrl : BLANK_PAGE;
|
|
173
|
+
|
|
163
174
|
// Note: Do NOT set `state` parameter here. Chrome 146+ rejects 'normal' as an invalid
|
|
164
175
|
// state value for windows.create(). The window defaults to 'normal' state anyway.
|
|
165
176
|
const win = await chrome.windows.create({
|
|
166
|
-
url:
|
|
177
|
+
url: startUrl,
|
|
167
178
|
focused: false,
|
|
168
179
|
width: 1280,
|
|
169
180
|
height: 900,
|
|
@@ -173,12 +184,33 @@ async function getAutomationWindow(workspace: string): Promise<number> {
|
|
|
173
184
|
windowId: win.id!,
|
|
174
185
|
idleTimer: null,
|
|
175
186
|
idleDeadlineAt: Date.now() + WINDOW_IDLE_TIMEOUT,
|
|
187
|
+
owned: true,
|
|
188
|
+
preferredTabId: null,
|
|
176
189
|
};
|
|
177
190
|
automationSessions.set(workspace, session);
|
|
178
|
-
console.log(`[opencli] Created automation window ${session.windowId} (${workspace})`);
|
|
191
|
+
console.log(`[opencli] Created automation window ${session.windowId} (${workspace}, start=${startUrl})`);
|
|
179
192
|
resetWindowIdleTimer(workspace);
|
|
180
|
-
//
|
|
181
|
-
|
|
193
|
+
// Wait for the initial tab to finish loading instead of a fixed 200ms sleep.
|
|
194
|
+
const tabs = await chrome.tabs.query({ windowId: win.id! });
|
|
195
|
+
if (tabs[0]?.id) {
|
|
196
|
+
await new Promise<void>((resolve) => {
|
|
197
|
+
const timeout = setTimeout(resolve, 500); // fallback cap
|
|
198
|
+
const listener = (tabId: number, info: chrome.tabs.TabChangeInfo) => {
|
|
199
|
+
if (tabId === tabs[0].id && info.status === 'complete') {
|
|
200
|
+
chrome.tabs.onUpdated.removeListener(listener);
|
|
201
|
+
clearTimeout(timeout);
|
|
202
|
+
resolve();
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
// Check if already complete before listening
|
|
206
|
+
if (tabs[0].status === 'complete') {
|
|
207
|
+
clearTimeout(timeout);
|
|
208
|
+
resolve();
|
|
209
|
+
} else {
|
|
210
|
+
chrome.tabs.onUpdated.addListener(listener);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
182
214
|
return session.windowId;
|
|
183
215
|
}
|
|
184
216
|
|
|
@@ -256,6 +288,14 @@ async function handleCommand(cmd: Command): Promise<Result> {
|
|
|
256
288
|
return await handleSessions(cmd);
|
|
257
289
|
case 'set-file-input':
|
|
258
290
|
return await handleSetFileInput(cmd, workspace);
|
|
291
|
+
case 'insert-text':
|
|
292
|
+
return await handleInsertText(cmd, workspace);
|
|
293
|
+
case 'bind-current':
|
|
294
|
+
return await handleBindCurrent(cmd, workspace);
|
|
295
|
+
case 'network-capture-start':
|
|
296
|
+
return await handleNetworkCaptureStart(cmd, workspace);
|
|
297
|
+
case 'network-capture-read':
|
|
298
|
+
return await handleNetworkCaptureRead(cmd, workspace);
|
|
259
299
|
default:
|
|
260
300
|
return { id: cmd.id, ok: false, error: `Unknown action: ${cmd.action}` };
|
|
261
301
|
}
|
|
@@ -303,7 +343,31 @@ function isTargetUrl(currentUrl: string | undefined, targetUrl: string): boolean
|
|
|
303
343
|
return normalizeUrlForComparison(currentUrl) === normalizeUrlForComparison(targetUrl);
|
|
304
344
|
}
|
|
305
345
|
|
|
306
|
-
function
|
|
346
|
+
function matchesDomain(url: string | undefined, domain: string): boolean {
|
|
347
|
+
if (!url) return false;
|
|
348
|
+
try {
|
|
349
|
+
const parsed = new URL(url);
|
|
350
|
+
return parsed.hostname === domain || parsed.hostname.endsWith(`.${domain}`);
|
|
351
|
+
} catch {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function matchesBindCriteria(tab: chrome.tabs.Tab, cmd: Command): boolean {
|
|
357
|
+
if (!tab.id || !isDebuggableUrl(tab.url)) return false;
|
|
358
|
+
if (cmd.matchDomain && !matchesDomain(tab.url, cmd.matchDomain)) return false;
|
|
359
|
+
if (cmd.matchPathPrefix) {
|
|
360
|
+
try {
|
|
361
|
+
const parsed = new URL(tab.url!);
|
|
362
|
+
if (!parsed.pathname.startsWith(cmd.matchPathPrefix)) return false;
|
|
363
|
+
} catch {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function setWorkspaceSession(workspace: string, session: Omit<AutomationSession, 'idleTimer' | 'idleDeadlineAt'>): void {
|
|
307
371
|
const existing = automationSessions.get(workspace);
|
|
308
372
|
if (existing?.idleTimer) clearTimeout(existing.idleTimer);
|
|
309
373
|
automationSessions.set(workspace, {
|
|
@@ -313,50 +377,69 @@ function setWorkspaceSession(workspace: string, session: Pick<AutomationSession,
|
|
|
313
377
|
});
|
|
314
378
|
}
|
|
315
379
|
|
|
380
|
+
type ResolvedTab = { tabId: number; tab: chrome.tabs.Tab | null };
|
|
381
|
+
|
|
316
382
|
/**
|
|
317
|
-
* Resolve target tab in the automation window
|
|
318
|
-
*
|
|
319
|
-
* Otherwise, find or create a tab in the dedicated automation window.
|
|
383
|
+
* Resolve target tab in the automation window, returning both the tabId and
|
|
384
|
+
* the Tab object (when available) so callers can skip a redundant chrome.tabs.get().
|
|
320
385
|
*/
|
|
321
|
-
async function
|
|
386
|
+
async function resolveTab(tabId: number | undefined, workspace: string, initialUrl?: string): Promise<ResolvedTab> {
|
|
322
387
|
// Even when an explicit tabId is provided, validate it is still debuggable.
|
|
323
|
-
// This prevents issues when extensions hijack the tab URL to chrome-extension://
|
|
324
|
-
// or when the tab has been closed by the user.
|
|
325
388
|
if (tabId !== undefined) {
|
|
326
389
|
try {
|
|
327
390
|
const tab = await chrome.tabs.get(tabId);
|
|
328
391
|
const session = automationSessions.get(workspace);
|
|
329
|
-
const matchesSession = session
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
392
|
+
const matchesSession = session
|
|
393
|
+
? (session.preferredTabId !== null ? session.preferredTabId === tabId : tab.windowId === session.windowId)
|
|
394
|
+
: false;
|
|
395
|
+
if (isDebuggableUrl(tab.url) && matchesSession) return { tabId, tab };
|
|
396
|
+
if (session && !matchesSession && session.preferredTabId === null && isDebuggableUrl(tab.url)) {
|
|
397
|
+
// Tab drifted to another window but content is still valid.
|
|
398
|
+
// Try to move it back instead of abandoning it.
|
|
399
|
+
console.warn(`[opencli] Tab ${tabId} drifted to window ${tab.windowId}, moving back to ${session.windowId}`);
|
|
400
|
+
try {
|
|
401
|
+
await chrome.tabs.move(tabId, { windowId: session.windowId, index: -1 });
|
|
402
|
+
const moved = await chrome.tabs.get(tabId);
|
|
403
|
+
if (moved.windowId === session.windowId && isDebuggableUrl(moved.url)) {
|
|
404
|
+
return { tabId, tab: moved };
|
|
405
|
+
}
|
|
406
|
+
} catch (moveErr) {
|
|
407
|
+
console.warn(`[opencli] Failed to move tab back: ${moveErr}`);
|
|
408
|
+
}
|
|
333
409
|
} else if (!isDebuggableUrl(tab.url)) {
|
|
334
|
-
// Tab exists but URL is not debuggable — fall through to auto-resolve
|
|
335
410
|
console.warn(`[opencli] Tab ${tabId} URL is not debuggable (${tab.url}), re-resolving`);
|
|
336
411
|
}
|
|
337
412
|
} catch {
|
|
338
|
-
// Tab was closed — fall through to auto-resolve
|
|
339
413
|
console.warn(`[opencli] Tab ${tabId} no longer exists, re-resolving`);
|
|
340
414
|
}
|
|
341
415
|
}
|
|
342
416
|
|
|
417
|
+
const existingSession = automationSessions.get(workspace);
|
|
418
|
+
if (existingSession?.preferredTabId !== null) {
|
|
419
|
+
try {
|
|
420
|
+
const preferredTab = await chrome.tabs.get(existingSession.preferredTabId);
|
|
421
|
+
if (isDebuggableUrl(preferredTab.url)) return { tabId: preferredTab.id!, tab: preferredTab };
|
|
422
|
+
} catch {
|
|
423
|
+
automationSessions.delete(workspace);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
343
427
|
// Get (or create) the automation window
|
|
344
|
-
const windowId = await getAutomationWindow(workspace);
|
|
428
|
+
const windowId = await getAutomationWindow(workspace, initialUrl);
|
|
345
429
|
|
|
346
430
|
// Prefer an existing debuggable tab
|
|
347
431
|
const tabs = await chrome.tabs.query({ windowId });
|
|
348
432
|
const debuggableTab = tabs.find(t => t.id && isDebuggableUrl(t.url));
|
|
349
|
-
if (debuggableTab?.id) return debuggableTab.id;
|
|
433
|
+
if (debuggableTab?.id) return { tabId: debuggableTab.id, tab: debuggableTab };
|
|
350
434
|
|
|
351
435
|
// No debuggable tab — another extension may have hijacked the tab URL.
|
|
352
|
-
// Try to reuse by navigating to a data: URI (not interceptable by New Tab Override).
|
|
353
436
|
const reuseTab = tabs.find(t => t.id);
|
|
354
437
|
if (reuseTab?.id) {
|
|
355
438
|
await chrome.tabs.update(reuseTab.id, { url: BLANK_PAGE });
|
|
356
439
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
357
440
|
try {
|
|
358
441
|
const updated = await chrome.tabs.get(reuseTab.id);
|
|
359
|
-
if (isDebuggableUrl(updated.url)) return reuseTab.id;
|
|
442
|
+
if (isDebuggableUrl(updated.url)) return { tabId: reuseTab.id, tab: updated };
|
|
360
443
|
console.warn(`[opencli] data: URI was intercepted (${updated.url}), creating fresh tab`);
|
|
361
444
|
} catch {
|
|
362
445
|
// Tab was closed during navigation
|
|
@@ -366,12 +449,26 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi
|
|
|
366
449
|
// Fallback: create a new tab
|
|
367
450
|
const newTab = await chrome.tabs.create({ windowId, url: BLANK_PAGE, active: true });
|
|
368
451
|
if (!newTab.id) throw new Error('Failed to create tab in automation window');
|
|
369
|
-
return newTab.id;
|
|
452
|
+
return { tabId: newTab.id, tab: newTab };
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/** Convenience wrapper returning just the tabId (used by most handlers) */
|
|
456
|
+
async function resolveTabId(tabId: number | undefined, workspace: string, initialUrl?: string): Promise<number> {
|
|
457
|
+
const resolved = await resolveTab(tabId, workspace, initialUrl);
|
|
458
|
+
return resolved.tabId;
|
|
370
459
|
}
|
|
371
460
|
|
|
372
461
|
async function listAutomationTabs(workspace: string): Promise<chrome.tabs.Tab[]> {
|
|
373
462
|
const session = automationSessions.get(workspace);
|
|
374
463
|
if (!session) return [];
|
|
464
|
+
if (session.preferredTabId !== null) {
|
|
465
|
+
try {
|
|
466
|
+
return [await chrome.tabs.get(session.preferredTabId)];
|
|
467
|
+
} catch {
|
|
468
|
+
automationSessions.delete(workspace);
|
|
469
|
+
return [];
|
|
470
|
+
}
|
|
471
|
+
}
|
|
375
472
|
try {
|
|
376
473
|
return await chrome.tabs.query({ windowId: session.windowId });
|
|
377
474
|
} catch {
|
|
@@ -402,9 +499,11 @@ async function handleNavigate(cmd: Command, workspace: string): Promise<Result>
|
|
|
402
499
|
if (!isSafeNavigationUrl(cmd.url)) {
|
|
403
500
|
return { id: cmd.id, ok: false, error: 'Blocked URL scheme -- only http:// and https:// are allowed' };
|
|
404
501
|
}
|
|
405
|
-
|
|
502
|
+
// Pass target URL so that first-time window creation can start on the right domain
|
|
503
|
+
const resolved = await resolveTab(cmd.tabId, workspace, cmd.url);
|
|
504
|
+
const tabId = resolved.tabId;
|
|
406
505
|
|
|
407
|
-
const beforeTab = await chrome.tabs.get(tabId);
|
|
506
|
+
const beforeTab = resolved.tab ?? await chrome.tabs.get(tabId);
|
|
408
507
|
const beforeNormalized = normalizeUrlForComparison(beforeTab.url);
|
|
409
508
|
const targetUrl = cmd.url;
|
|
410
509
|
|
|
@@ -475,7 +574,22 @@ async function handleNavigate(cmd: Command, workspace: string): Promise<Result>
|
|
|
475
574
|
}, 15000);
|
|
476
575
|
});
|
|
477
576
|
|
|
478
|
-
|
|
577
|
+
let tab = await chrome.tabs.get(tabId);
|
|
578
|
+
|
|
579
|
+
// Post-navigation drift detection: if the tab moved to another window
|
|
580
|
+
// during navigation (e.g. a tab-management extension regrouped it),
|
|
581
|
+
// try to move it back to maintain session isolation.
|
|
582
|
+
const session = automationSessions.get(workspace);
|
|
583
|
+
if (session && tab.windowId !== session.windowId) {
|
|
584
|
+
console.warn(`[opencli] Tab ${tabId} drifted to window ${tab.windowId} during navigation, moving back to ${session.windowId}`);
|
|
585
|
+
try {
|
|
586
|
+
await chrome.tabs.move(tabId, { windowId: session.windowId, index: -1 });
|
|
587
|
+
tab = await chrome.tabs.get(tabId);
|
|
588
|
+
} catch (moveErr) {
|
|
589
|
+
console.warn(`[opencli] Failed to recover drifted tab: ${moveErr}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
479
593
|
return {
|
|
480
594
|
id: cmd.id,
|
|
481
595
|
ok: true,
|
|
@@ -628,10 +742,12 @@ async function handleCdp(cmd: Command, workspace: string): Promise<Result> {
|
|
|
628
742
|
async function handleCloseWindow(cmd: Command, workspace: string): Promise<Result> {
|
|
629
743
|
const session = automationSessions.get(workspace);
|
|
630
744
|
if (session) {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
745
|
+
if (session.owned) {
|
|
746
|
+
try {
|
|
747
|
+
await chrome.windows.remove(session.windowId);
|
|
748
|
+
} catch {
|
|
749
|
+
// Window may already be closed
|
|
750
|
+
}
|
|
635
751
|
}
|
|
636
752
|
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
637
753
|
automationSessions.delete(workspace);
|
|
@@ -652,6 +768,39 @@ async function handleSetFileInput(cmd: Command, workspace: string): Promise<Resu
|
|
|
652
768
|
}
|
|
653
769
|
}
|
|
654
770
|
|
|
771
|
+
async function handleInsertText(cmd: Command, workspace: string): Promise<Result> {
|
|
772
|
+
if (typeof cmd.text !== 'string') {
|
|
773
|
+
return { id: cmd.id, ok: false, error: 'Missing text payload' };
|
|
774
|
+
}
|
|
775
|
+
const tabId = await resolveTabId(cmd.tabId, workspace);
|
|
776
|
+
try {
|
|
777
|
+
await executor.insertText(tabId, cmd.text);
|
|
778
|
+
return { id: cmd.id, ok: true, data: { inserted: true } };
|
|
779
|
+
} catch (err) {
|
|
780
|
+
return { id: cmd.id, ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
async function handleNetworkCaptureStart(cmd: Command, workspace: string): Promise<Result> {
|
|
785
|
+
const tabId = await resolveTabId(cmd.tabId, workspace);
|
|
786
|
+
try {
|
|
787
|
+
await executor.startNetworkCapture(tabId, cmd.pattern);
|
|
788
|
+
return { id: cmd.id, ok: true, data: { started: true } };
|
|
789
|
+
} catch (err) {
|
|
790
|
+
return { id: cmd.id, ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
async function handleNetworkCaptureRead(cmd: Command, workspace: string): Promise<Result> {
|
|
795
|
+
const tabId = await resolveTabId(cmd.tabId, workspace);
|
|
796
|
+
try {
|
|
797
|
+
const data = await executor.readNetworkCapture(tabId);
|
|
798
|
+
return { id: cmd.id, ok: true, data };
|
|
799
|
+
} catch (err) {
|
|
800
|
+
return { id: cmd.id, ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
655
804
|
async function handleSessions(cmd: Command): Promise<Result> {
|
|
656
805
|
const now = Date.now();
|
|
657
806
|
const data = await Promise.all([...automationSessions.entries()].map(async ([workspace, session]) => ({
|
|
@@ -663,11 +812,49 @@ async function handleSessions(cmd: Command): Promise<Result> {
|
|
|
663
812
|
return { id: cmd.id, ok: true, data };
|
|
664
813
|
}
|
|
665
814
|
|
|
815
|
+
async function handleBindCurrent(cmd: Command, workspace: string): Promise<Result> {
|
|
816
|
+
const activeTabs = await chrome.tabs.query({ active: true, lastFocusedWindow: true });
|
|
817
|
+
const fallbackTabs = await chrome.tabs.query({ lastFocusedWindow: true });
|
|
818
|
+
const allTabs = await chrome.tabs.query({});
|
|
819
|
+
const boundTab = activeTabs.find((tab) => matchesBindCriteria(tab, cmd))
|
|
820
|
+
?? fallbackTabs.find((tab) => matchesBindCriteria(tab, cmd))
|
|
821
|
+
?? allTabs.find((tab) => matchesBindCriteria(tab, cmd));
|
|
822
|
+
if (!boundTab?.id) {
|
|
823
|
+
return {
|
|
824
|
+
id: cmd.id,
|
|
825
|
+
ok: false,
|
|
826
|
+
error: cmd.matchDomain || cmd.matchPathPrefix
|
|
827
|
+
? `No visible tab matching ${cmd.matchDomain ?? 'domain'}${cmd.matchPathPrefix ? ` ${cmd.matchPathPrefix}` : ''}`
|
|
828
|
+
: 'No active debuggable tab found',
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
setWorkspaceSession(workspace, {
|
|
833
|
+
windowId: boundTab.windowId,
|
|
834
|
+
owned: false,
|
|
835
|
+
preferredTabId: boundTab.id,
|
|
836
|
+
});
|
|
837
|
+
resetWindowIdleTimer(workspace);
|
|
838
|
+
console.log(`[opencli] Workspace ${workspace} explicitly bound to tab ${boundTab.id} (${boundTab.url})`);
|
|
839
|
+
return {
|
|
840
|
+
id: cmd.id,
|
|
841
|
+
ok: true,
|
|
842
|
+
data: {
|
|
843
|
+
tabId: boundTab.id,
|
|
844
|
+
windowId: boundTab.windowId,
|
|
845
|
+
url: boundTab.url,
|
|
846
|
+
title: boundTab.title,
|
|
847
|
+
workspace,
|
|
848
|
+
},
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
|
|
666
852
|
export const __test__ = {
|
|
667
853
|
handleNavigate,
|
|
668
854
|
isTargetUrl,
|
|
669
855
|
handleTabs,
|
|
670
856
|
handleSessions,
|
|
857
|
+
handleBindCurrent,
|
|
671
858
|
resolveTabId,
|
|
672
859
|
resetWindowIdleTimer,
|
|
673
860
|
getSession: (workspace: string = 'default') => automationSessions.get(workspace) ?? null,
|
|
@@ -681,9 +868,11 @@ export const __test__ = {
|
|
|
681
868
|
}
|
|
682
869
|
setWorkspaceSession(workspace, {
|
|
683
870
|
windowId,
|
|
871
|
+
owned: true,
|
|
872
|
+
preferredTabId: null,
|
|
684
873
|
});
|
|
685
874
|
},
|
|
686
|
-
setSession: (workspace: string, session: { windowId: number }) => {
|
|
875
|
+
setSession: (workspace: string, session: { windowId: number; owned: boolean; preferredTabId: number | null }) => {
|
|
687
876
|
setWorkspaceSession(workspace, session);
|
|
688
877
|
},
|
|
689
878
|
};
|