@jackwener/opencli 1.5.8 → 1.5.9
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/CHANGELOG.md +21 -0
- package/README.md +17 -1
- package/README.zh-CN.md +17 -1
- package/dist/browser/base-page.d.ts +48 -0
- package/dist/browser/base-page.js +160 -0
- package/dist/browser/cdp.js +4 -106
- package/dist/browser/daemon-client.d.ts +1 -7
- package/dist/browser/daemon-client.js +2 -9
- package/dist/browser/discover.d.ts +1 -4
- package/dist/browser/discover.js +1 -4
- package/dist/browser/errors.d.ts +4 -0
- package/dist/browser/errors.js +20 -0
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/page.d.ts +6 -35
- package/dist/browser/page.js +10 -189
- package/dist/browser/tabs.js +5 -5
- package/dist/browser.test.js +15 -15
- package/dist/cli-manifest.json +294 -22
- package/dist/clis/amazon/bestsellers.d.ts +21 -0
- package/dist/clis/amazon/bestsellers.js +130 -0
- package/dist/clis/amazon/bestsellers.test.js +20 -0
- package/dist/clis/amazon/discussion.d.ts +20 -0
- package/dist/clis/amazon/discussion.js +91 -0
- package/dist/clis/amazon/discussion.test.js +36 -0
- package/dist/clis/amazon/offer.d.ts +23 -0
- package/dist/clis/amazon/offer.js +140 -0
- package/dist/clis/amazon/offer.test.d.ts +1 -0
- package/dist/clis/amazon/offer.test.js +29 -0
- package/dist/clis/amazon/product.d.ts +18 -0
- package/dist/clis/amazon/product.js +92 -0
- package/dist/clis/amazon/product.test.d.ts +1 -0
- package/dist/clis/amazon/product.test.js +24 -0
- package/dist/clis/amazon/search.d.ts +18 -0
- package/dist/clis/amazon/search.js +87 -0
- package/dist/clis/amazon/search.test.d.ts +1 -0
- package/dist/clis/amazon/search.test.js +22 -0
- package/dist/clis/amazon/shared.d.ts +64 -0
- package/dist/clis/amazon/shared.js +255 -0
- package/dist/clis/amazon/shared.test.d.ts +1 -0
- package/dist/clis/amazon/shared.test.js +33 -0
- package/dist/clis/gemini/ask.d.ts +1 -0
- package/dist/clis/gemini/ask.js +40 -0
- package/dist/clis/gemini/image.d.ts +1 -0
- package/dist/clis/gemini/image.js +105 -0
- package/dist/clis/gemini/new.d.ts +1 -0
- package/dist/clis/gemini/new.js +20 -0
- package/dist/clis/gemini/utils.d.ts +34 -0
- package/dist/clis/gemini/utils.js +463 -0
- package/dist/clis/gemini/utils.test.d.ts +1 -0
- package/dist/clis/gemini/utils.test.js +31 -0
- package/dist/clis/notebooklm/compat.test.d.ts +1 -1
- package/dist/clis/notebooklm/compat.test.js +3 -3
- package/dist/clis/notebooklm/current.js +2 -3
- package/dist/clis/notebooklm/get.js +2 -3
- package/dist/clis/notebooklm/history.js +2 -3
- package/dist/clis/notebooklm/note-list.js +2 -3
- package/dist/clis/notebooklm/notes-get.js +2 -3
- package/dist/clis/notebooklm/open.d.ts +1 -0
- package/dist/clis/notebooklm/open.js +41 -0
- package/dist/clis/notebooklm/open.test.d.ts +1 -0
- package/dist/clis/notebooklm/open.test.js +63 -0
- package/dist/clis/notebooklm/source-fulltext.js +2 -3
- package/dist/clis/notebooklm/source-get.js +2 -3
- package/dist/clis/notebooklm/source-guide.js +2 -3
- package/dist/clis/notebooklm/source-list.js +2 -3
- package/dist/clis/notebooklm/status.js +1 -2
- package/dist/clis/notebooklm/summary.js +2 -3
- package/dist/clis/notebooklm/utils.d.ts +2 -1
- package/dist/clis/notebooklm/utils.js +20 -21
- package/dist/clis/xiaohongshu/creator-note-detail.test.js +11 -11
- package/dist/clis/xiaohongshu/creator-notes-summary.test.js +6 -6
- package/dist/clis/xiaohongshu/creator-notes.test.js +22 -22
- package/dist/commanderAdapter.js +6 -3
- package/dist/commanderAdapter.test.js +33 -0
- package/dist/commands/daemon.js +1 -1
- package/dist/commands/daemon.test.js +1 -1
- package/dist/doctor.d.ts +1 -2
- package/dist/doctor.js +7 -8
- package/dist/explore.js +1 -1
- package/dist/output.js +28 -0
- package/dist/output.test.js +15 -0
- package/dist/pipeline/executor.js +2 -7
- package/dist/pipeline/steps/browser.js +1 -1
- package/dist/pipeline/template.js +25 -3
- package/dist/record.d.ts +50 -0
- package/dist/record.js +298 -57
- package/dist/record.test.d.ts +1 -0
- package/dist/record.test.js +293 -0
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +1 -0
- package/dist/registry.test.js +10 -0
- package/dist/runtime.js +3 -3
- package/dist/snapshotFormatter.d.ts +1 -1
- package/dist/snapshotFormatter.js +4 -4
- package/dist/snapshotFormatter.test.d.ts +1 -1
- package/dist/snapshotFormatter.test.js +2 -2
- package/dist/types.d.ts +3 -1
- package/dist/types.js +1 -1
- package/docs/.vitepress/config.mts +2 -0
- package/docs/adapters/browser/amazon.md +53 -0
- package/docs/adapters/browser/gemini.md +72 -0
- package/docs/adapters/browser/notebooklm.md +5 -5
- package/docs/adapters/index.md +3 -1
- package/extension/dist/background.js +5 -143
- package/extension/src/background.test.ts +7 -163
- package/extension/src/background.ts +7 -157
- package/extension/src/protocol.ts +1 -5
- package/package.json +1 -1
- package/skills/opencli-explorer/SKILL.md +847 -0
- package/skills/opencli-oneshot/SKILL.md +216 -0
- package/skills/opencli-usage/SKILL.md +71 -0
- package/skills/opencli-usage/browser.md +429 -0
- package/skills/opencli-usage/desktop.md +118 -0
- package/skills/opencli-usage/plugins.md +82 -0
- package/skills/opencli-usage/public-api.md +149 -0
- package/src/browser/base-page.ts +197 -0
- package/src/browser/cdp.ts +7 -131
- package/src/browser/daemon-client.ts +3 -14
- package/src/browser/discover.ts +1 -4
- package/src/browser/errors.ts +22 -0
- package/src/browser/index.ts +1 -1
- package/src/browser/page.ts +13 -212
- package/src/browser/tabs.ts +5 -5
- package/src/browser.test.ts +15 -15
- package/src/clis/amazon/bestsellers.test.ts +22 -0
- package/src/clis/amazon/bestsellers.ts +180 -0
- package/src/clis/amazon/discussion.test.ts +38 -0
- package/src/clis/amazon/discussion.ts +131 -0
- package/src/clis/amazon/offer.test.ts +35 -0
- package/src/clis/amazon/offer.ts +185 -0
- package/src/clis/amazon/product.test.ts +26 -0
- package/src/clis/amazon/product.ts +131 -0
- package/src/clis/amazon/search.test.ts +24 -0
- package/src/clis/amazon/search.ts +128 -0
- package/src/clis/amazon/shared.test.ts +37 -0
- package/src/clis/amazon/shared.ts +316 -0
- package/src/clis/gemini/ask.ts +46 -0
- package/src/clis/gemini/image.ts +115 -0
- package/src/clis/gemini/new.ts +22 -0
- package/src/clis/gemini/utils.test.ts +36 -0
- package/src/clis/gemini/utils.ts +523 -0
- package/src/clis/notebooklm/compat.test.ts +3 -3
- package/src/clis/notebooklm/current.ts +2 -3
- package/src/clis/notebooklm/get.ts +1 -3
- package/src/clis/notebooklm/history.ts +1 -3
- package/src/clis/notebooklm/note-list.ts +1 -3
- package/src/clis/notebooklm/notes-get.ts +1 -3
- package/src/clis/notebooklm/open.test.ts +78 -0
- package/src/clis/notebooklm/open.ts +61 -0
- package/src/clis/notebooklm/source-fulltext.ts +1 -3
- package/src/clis/notebooklm/source-get.ts +1 -3
- package/src/clis/notebooklm/source-guide.ts +1 -3
- package/src/clis/notebooklm/source-list.ts +1 -3
- package/src/clis/notebooklm/status.ts +1 -2
- package/src/clis/notebooklm/summary.ts +1 -3
- package/src/clis/notebooklm/utils.ts +29 -20
- package/src/clis/xiaohongshu/creator-note-detail.test.ts +11 -11
- package/src/clis/xiaohongshu/creator-notes-summary.test.ts +6 -6
- package/src/clis/xiaohongshu/creator-notes.test.ts +22 -22
- package/src/commanderAdapter.test.ts +47 -0
- package/src/commanderAdapter.ts +7 -3
- package/src/commands/daemon.test.ts +1 -1
- package/src/commands/daemon.ts +1 -1
- package/src/doctor.ts +7 -8
- package/src/explore.ts +1 -1
- package/src/output.test.ts +17 -0
- package/src/output.ts +27 -0
- package/src/pipeline/executor.ts +2 -7
- package/src/pipeline/steps/browser.ts +1 -1
- package/src/pipeline/template.ts +27 -4
- package/src/record.test.ts +362 -0
- package/src/record.ts +341 -62
- package/src/registry.test.ts +12 -0
- package/src/registry.ts +3 -0
- package/src/runtime.ts +3 -3
- package/src/snapshotFormatter.test.ts +2 -2
- package/src/snapshotFormatter.ts +4 -4
- package/src/types.ts +3 -1
- package/.agents/skills/cross-project-adapter-migration/SKILL.md +0 -249
- package/.agents/workflows/cross-project-adapter-migration.md +0 -54
- package/SKILL.md +0 -879
- package/dist/clis/notebooklm/bind-current.js +0 -29
- package/dist/clis/notebooklm/bind-current.test.d.ts +0 -1
- package/dist/clis/notebooklm/bind-current.test.js +0 -35
- package/dist/clis/notebooklm/binding.test.js +0 -44
- package/src/clis/notebooklm/bind-current.test.ts +0 -43
- package/src/clis/notebooklm/bind-current.ts +0 -36
- package/src/clis/notebooklm/binding.test.ts +0 -53
- /package/dist/browser/{mcp.d.ts → bridge.d.ts} +0 -0
- /package/dist/browser/{mcp.js → bridge.js} +0 -0
- /package/dist/clis/{notebooklm/bind-current.d.ts → amazon/bestsellers.test.d.ts} +0 -0
- /package/dist/clis/{notebooklm/binding.test.d.ts → amazon/discussion.test.d.ts} +0 -0
- /package/src/browser/{mcp.ts → bridge.ts} +0 -0
|
@@ -301,11 +301,6 @@ function resetWindowIdleTimer(workspace) {
|
|
|
301
301
|
session.idleTimer = setTimeout(async () => {
|
|
302
302
|
const current = automationSessions.get(workspace);
|
|
303
303
|
if (!current) return;
|
|
304
|
-
if (!current.owned) {
|
|
305
|
-
console.log(`[opencli] Borrowed workspace ${workspace} detached from window ${current.windowId} (idle timeout)`);
|
|
306
|
-
automationSessions.delete(workspace);
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
304
|
try {
|
|
310
305
|
await chrome.windows.remove(current.windowId);
|
|
311
306
|
console.log(`[opencli] Automation window ${current.windowId} (${workspace}) closed (idle timeout)`);
|
|
@@ -334,9 +329,7 @@ async function getAutomationWindow(workspace) {
|
|
|
334
329
|
const session = {
|
|
335
330
|
windowId: win.id,
|
|
336
331
|
idleTimer: null,
|
|
337
|
-
idleDeadlineAt: Date.now() + WINDOW_IDLE_TIMEOUT
|
|
338
|
-
owned: true,
|
|
339
|
-
preferredTabId: null
|
|
332
|
+
idleDeadlineAt: Date.now() + WINDOW_IDLE_TIMEOUT
|
|
340
333
|
};
|
|
341
334
|
automationSessions.set(workspace, session);
|
|
342
335
|
console.log(`[opencli] Created automation window ${session.windowId} (${workspace})`);
|
|
@@ -401,8 +394,6 @@ async function handleCommand(cmd) {
|
|
|
401
394
|
return await handleSessions(cmd);
|
|
402
395
|
case "set-file-input":
|
|
403
396
|
return await handleSetFileInput(cmd, workspace);
|
|
404
|
-
case "bind-current":
|
|
405
|
-
return await handleBindCurrent(cmd, workspace);
|
|
406
397
|
default:
|
|
407
398
|
return { id: cmd.id, ok: false, error: `Unknown action: ${cmd.action}` };
|
|
408
399
|
}
|
|
@@ -438,88 +429,12 @@ function normalizeUrlForComparison(url) {
|
|
|
438
429
|
function isTargetUrl(currentUrl, targetUrl) {
|
|
439
430
|
return normalizeUrlForComparison(currentUrl) === normalizeUrlForComparison(targetUrl);
|
|
440
431
|
}
|
|
441
|
-
function matchesDomain(url, domain) {
|
|
442
|
-
if (!url) return false;
|
|
443
|
-
try {
|
|
444
|
-
const parsed = new URL(url);
|
|
445
|
-
return parsed.hostname === domain || parsed.hostname.endsWith(`.${domain}`);
|
|
446
|
-
} catch {
|
|
447
|
-
return false;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
function matchesBindCriteria(tab, cmd) {
|
|
451
|
-
if (!tab.id || !isDebuggableUrl(tab.url)) return false;
|
|
452
|
-
if (cmd.matchDomain && !matchesDomain(tab.url, cmd.matchDomain)) return false;
|
|
453
|
-
if (cmd.matchPathPrefix) {
|
|
454
|
-
try {
|
|
455
|
-
const parsed = new URL(tab.url);
|
|
456
|
-
if (!parsed.pathname.startsWith(cmd.matchPathPrefix)) return false;
|
|
457
|
-
} catch {
|
|
458
|
-
return false;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
return true;
|
|
462
|
-
}
|
|
463
|
-
function isNotebooklmWorkspace(workspace) {
|
|
464
|
-
return workspace === "site:notebooklm";
|
|
465
|
-
}
|
|
466
|
-
function classifyNotebooklmUrl(url) {
|
|
467
|
-
if (!url) return "other";
|
|
468
|
-
try {
|
|
469
|
-
const parsed = new URL(url);
|
|
470
|
-
if (parsed.hostname !== "notebooklm.google.com") return "other";
|
|
471
|
-
return parsed.pathname.startsWith("/notebook/") ? "notebook" : "home";
|
|
472
|
-
} catch {
|
|
473
|
-
return "other";
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
function scoreWorkspaceTab(workspace, tab) {
|
|
477
|
-
if (!tab.id || !isDebuggableUrl(tab.url)) return -1;
|
|
478
|
-
if (isNotebooklmWorkspace(workspace)) {
|
|
479
|
-
const kind = classifyNotebooklmUrl(tab.url);
|
|
480
|
-
if (kind === "other") return -1;
|
|
481
|
-
if (kind === "notebook") return tab.active ? 400 : 300;
|
|
482
|
-
return tab.active ? 200 : 100;
|
|
483
|
-
}
|
|
484
|
-
return -1;
|
|
485
|
-
}
|
|
486
|
-
function setWorkspaceSession(workspace, session) {
|
|
487
|
-
const existing = automationSessions.get(workspace);
|
|
488
|
-
if (existing?.idleTimer) clearTimeout(existing.idleTimer);
|
|
489
|
-
automationSessions.set(workspace, {
|
|
490
|
-
...session,
|
|
491
|
-
idleTimer: null,
|
|
492
|
-
idleDeadlineAt: Date.now() + WINDOW_IDLE_TIMEOUT
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
async function maybeBindWorkspaceToExistingTab(workspace) {
|
|
496
|
-
if (!isNotebooklmWorkspace(workspace)) return null;
|
|
497
|
-
const tabs = await chrome.tabs.query({});
|
|
498
|
-
let bestTab = null;
|
|
499
|
-
let bestScore = -1;
|
|
500
|
-
for (const tab of tabs) {
|
|
501
|
-
const score = scoreWorkspaceTab(workspace, tab);
|
|
502
|
-
if (score > bestScore) {
|
|
503
|
-
bestScore = score;
|
|
504
|
-
bestTab = tab;
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
if (!bestTab?.id || bestScore < 0) return null;
|
|
508
|
-
setWorkspaceSession(workspace, {
|
|
509
|
-
windowId: bestTab.windowId,
|
|
510
|
-
owned: false,
|
|
511
|
-
preferredTabId: bestTab.id
|
|
512
|
-
});
|
|
513
|
-
console.log(`[opencli] Workspace ${workspace} bound to existing tab ${bestTab.id} in window ${bestTab.windowId}`);
|
|
514
|
-
resetWindowIdleTimer(workspace);
|
|
515
|
-
return bestTab.id;
|
|
516
|
-
}
|
|
517
432
|
async function resolveTabId(tabId, workspace) {
|
|
518
433
|
if (tabId !== void 0) {
|
|
519
434
|
try {
|
|
520
435
|
const tab = await chrome.tabs.get(tabId);
|
|
521
436
|
const session = automationSessions.get(workspace);
|
|
522
|
-
const matchesSession = session ?
|
|
437
|
+
const matchesSession = session ? tab.windowId === session.windowId : false;
|
|
523
438
|
if (isDebuggableUrl(tab.url) && matchesSession) return tabId;
|
|
524
439
|
if (session && !matchesSession) {
|
|
525
440
|
console.warn(`[opencli] Tab ${tabId} is not bound to workspace ${workspace}, re-resolving`);
|
|
@@ -530,18 +445,6 @@ async function resolveTabId(tabId, workspace) {
|
|
|
530
445
|
console.warn(`[opencli] Tab ${tabId} no longer exists, re-resolving`);
|
|
531
446
|
}
|
|
532
447
|
}
|
|
533
|
-
const adoptedTabId = await maybeBindWorkspaceToExistingTab(workspace);
|
|
534
|
-
if (adoptedTabId !== null) return adoptedTabId;
|
|
535
|
-
const existingSession = automationSessions.get(workspace);
|
|
536
|
-
if (existingSession && existingSession.preferredTabId !== null) {
|
|
537
|
-
try {
|
|
538
|
-
const preferredTabId = existingSession.preferredTabId;
|
|
539
|
-
const preferredTab = await chrome.tabs.get(preferredTabId);
|
|
540
|
-
if (isDebuggableUrl(preferredTab.url)) return preferredTab.id;
|
|
541
|
-
} catch {
|
|
542
|
-
automationSessions.delete(workspace);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
448
|
const windowId = await getAutomationWindow(workspace);
|
|
546
449
|
const tabs = await chrome.tabs.query({ windowId });
|
|
547
450
|
const debuggableTab = tabs.find((t) => t.id && isDebuggableUrl(t.url));
|
|
@@ -564,14 +467,6 @@ async function resolveTabId(tabId, workspace) {
|
|
|
564
467
|
async function listAutomationTabs(workspace) {
|
|
565
468
|
const session = automationSessions.get(workspace);
|
|
566
469
|
if (!session) return [];
|
|
567
|
-
if (session.preferredTabId !== null) {
|
|
568
|
-
try {
|
|
569
|
-
return [await chrome.tabs.get(session.preferredTabId)];
|
|
570
|
-
} catch {
|
|
571
|
-
automationSessions.delete(workspace);
|
|
572
|
-
return [];
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
470
|
try {
|
|
576
471
|
return await chrome.tabs.query({ windowId: session.windowId });
|
|
577
472
|
} catch {
|
|
@@ -753,11 +648,9 @@ async function handleScreenshot(cmd, workspace) {
|
|
|
753
648
|
async function handleCloseWindow(cmd, workspace) {
|
|
754
649
|
const session = automationSessions.get(workspace);
|
|
755
650
|
if (session) {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
} catch {
|
|
760
|
-
}
|
|
651
|
+
try {
|
|
652
|
+
await chrome.windows.remove(session.windowId);
|
|
653
|
+
} catch {
|
|
761
654
|
}
|
|
762
655
|
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
763
656
|
automationSessions.delete(workspace);
|
|
@@ -786,34 +679,3 @@ async function handleSessions(cmd) {
|
|
|
786
679
|
})));
|
|
787
680
|
return { id: cmd.id, ok: true, data };
|
|
788
681
|
}
|
|
789
|
-
async function handleBindCurrent(cmd, workspace) {
|
|
790
|
-
const activeTabs = await chrome.tabs.query({ active: true, lastFocusedWindow: true });
|
|
791
|
-
const fallbackTabs = await chrome.tabs.query({ lastFocusedWindow: true });
|
|
792
|
-
const allTabs = await chrome.tabs.query({});
|
|
793
|
-
const boundTab = activeTabs.find((tab) => matchesBindCriteria(tab, cmd)) ?? fallbackTabs.find((tab) => matchesBindCriteria(tab, cmd)) ?? allTabs.find((tab) => matchesBindCriteria(tab, cmd));
|
|
794
|
-
if (!boundTab?.id) {
|
|
795
|
-
return {
|
|
796
|
-
id: cmd.id,
|
|
797
|
-
ok: false,
|
|
798
|
-
error: cmd.matchDomain || cmd.matchPathPrefix ? `No visible tab matching ${cmd.matchDomain ?? "domain"}${cmd.matchPathPrefix ? ` ${cmd.matchPathPrefix}` : ""}` : "No active debuggable tab found"
|
|
799
|
-
};
|
|
800
|
-
}
|
|
801
|
-
setWorkspaceSession(workspace, {
|
|
802
|
-
windowId: boundTab.windowId,
|
|
803
|
-
owned: false,
|
|
804
|
-
preferredTabId: boundTab.id
|
|
805
|
-
});
|
|
806
|
-
resetWindowIdleTimer(workspace);
|
|
807
|
-
console.log(`[opencli] Workspace ${workspace} explicitly bound to tab ${boundTab.id} (${boundTab.url})`);
|
|
808
|
-
return {
|
|
809
|
-
id: cmd.id,
|
|
810
|
-
ok: true,
|
|
811
|
-
data: {
|
|
812
|
-
tabId: boundTab.id,
|
|
813
|
-
windowId: boundTab.windowId,
|
|
814
|
-
url: boundTab.url,
|
|
815
|
-
title: boundTab.title,
|
|
816
|
-
workspace
|
|
817
|
-
}
|
|
818
|
-
};
|
|
819
|
-
}
|
|
@@ -200,7 +200,7 @@ describe('background tab isolation', () => {
|
|
|
200
200
|
]));
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
-
it('
|
|
203
|
+
it('keeps site:notebooklm inside its owned automation window instead of rebinding to a user tab', async () => {
|
|
204
204
|
const { chrome, tabs } = createChromeMock();
|
|
205
205
|
tabs[0].url = 'https://notebooklm.google.com/';
|
|
206
206
|
tabs[0].title = 'NotebookLM Home';
|
|
@@ -213,184 +213,28 @@ describe('background tab isolation', () => {
|
|
|
213
213
|
|
|
214
214
|
const tabId = await mod.__test__.resolveTabId(undefined, 'site:notebooklm');
|
|
215
215
|
|
|
216
|
-
expect(tabId).toBe(
|
|
216
|
+
expect(tabId).toBe(1);
|
|
217
217
|
expect(mod.__test__.getSession('site:notebooklm')).toEqual(expect.objectContaining({
|
|
218
|
-
windowId:
|
|
219
|
-
preferredTabId: 2,
|
|
220
|
-
owned: false,
|
|
218
|
+
windowId: 1,
|
|
221
219
|
}));
|
|
222
220
|
});
|
|
223
221
|
|
|
224
|
-
it('
|
|
222
|
+
it('idle timeout closes the automation window for site:notebooklm', async () => {
|
|
225
223
|
const { chrome, tabs } = createChromeMock();
|
|
226
224
|
tabs[0].url = 'https://notebooklm.google.com/';
|
|
227
225
|
tabs[0].title = 'NotebookLM Home';
|
|
228
226
|
tabs[0].active = true;
|
|
229
|
-
tabs[1].url = 'https://notebooklm.google.com/notebook/nb-passive';
|
|
230
|
-
tabs[1].title = 'Notebook';
|
|
231
|
-
tabs[1].active = false;
|
|
232
|
-
vi.stubGlobal('chrome', chrome);
|
|
233
|
-
|
|
234
|
-
const mod = await import('./background');
|
|
235
|
-
mod.__test__.setAutomationWindowId('site:notebooklm', 1);
|
|
236
|
-
|
|
237
|
-
const tabId = await mod.__test__.resolveTabId(undefined, 'site:notebooklm');
|
|
238
|
-
|
|
239
|
-
expect(tabId).toBe(2);
|
|
240
|
-
expect(mod.__test__.getSession('site:notebooklm')).toEqual(expect.objectContaining({
|
|
241
|
-
windowId: 2,
|
|
242
|
-
preferredTabId: 2,
|
|
243
|
-
owned: false,
|
|
244
|
-
}));
|
|
245
|
-
});
|
|
246
227
|
|
|
247
|
-
it('detaches an adopted workspace session on idle instead of closing the user window', async () => {
|
|
248
|
-
const { chrome } = createChromeMock();
|
|
249
|
-
vi.stubGlobal('chrome', chrome);
|
|
250
228
|
vi.useFakeTimers();
|
|
229
|
+
vi.stubGlobal('chrome', chrome);
|
|
251
230
|
|
|
252
231
|
const mod = await import('./background');
|
|
253
|
-
mod.__test__.
|
|
254
|
-
windowId: 2,
|
|
255
|
-
preferredTabId: 2,
|
|
256
|
-
owned: false,
|
|
257
|
-
});
|
|
232
|
+
mod.__test__.setAutomationWindowId('site:notebooklm', 1);
|
|
258
233
|
|
|
259
234
|
mod.__test__.resetWindowIdleTimer('site:notebooklm');
|
|
260
235
|
await vi.advanceTimersByTimeAsync(30001);
|
|
261
236
|
|
|
262
|
-
expect(chrome.windows.remove).
|
|
237
|
+
expect(chrome.windows.remove).toHaveBeenCalledWith(1);
|
|
263
238
|
expect(mod.__test__.getSession('site:notebooklm')).toBeNull();
|
|
264
239
|
});
|
|
265
|
-
|
|
266
|
-
it('binds the active NotebookLM tab into the workspace explicitly', async () => {
|
|
267
|
-
const { chrome, tabs } = createChromeMock();
|
|
268
|
-
tabs[1].url = 'https://notebooklm.google.com/notebook/nb-active';
|
|
269
|
-
tabs[1].title = 'Bound Notebook';
|
|
270
|
-
tabs[1].active = true;
|
|
271
|
-
vi.stubGlobal('chrome', chrome);
|
|
272
|
-
|
|
273
|
-
const mod = await import('./background');
|
|
274
|
-
const result = await mod.__test__.handleBindCurrent(
|
|
275
|
-
{
|
|
276
|
-
id: 'bind-current',
|
|
277
|
-
action: 'bind-current',
|
|
278
|
-
workspace: 'site:notebooklm',
|
|
279
|
-
matchDomain: 'notebooklm.google.com',
|
|
280
|
-
matchPathPrefix: '/notebook/',
|
|
281
|
-
},
|
|
282
|
-
'site:notebooklm',
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
expect(result).toEqual({
|
|
286
|
-
id: 'bind-current',
|
|
287
|
-
ok: true,
|
|
288
|
-
data: expect.objectContaining({
|
|
289
|
-
tabId: 2,
|
|
290
|
-
windowId: 2,
|
|
291
|
-
url: 'https://notebooklm.google.com/notebook/nb-active',
|
|
292
|
-
title: 'Bound Notebook',
|
|
293
|
-
workspace: 'site:notebooklm',
|
|
294
|
-
}),
|
|
295
|
-
});
|
|
296
|
-
expect(mod.__test__.getSession('site:notebooklm')).toEqual(expect.objectContaining({
|
|
297
|
-
windowId: 2,
|
|
298
|
-
preferredTabId: 2,
|
|
299
|
-
owned: false,
|
|
300
|
-
}));
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it('bind-current falls back to another matching notebook tab in the current window', async () => {
|
|
304
|
-
const { chrome, tabs } = createChromeMock();
|
|
305
|
-
tabs[0].windowId = 2;
|
|
306
|
-
tabs[0].url = 'https://notebooklm.google.com/';
|
|
307
|
-
tabs[0].title = 'NotebookLM Home';
|
|
308
|
-
tabs[0].active = true;
|
|
309
|
-
tabs[1].url = 'https://notebooklm.google.com/notebook/nb-passive';
|
|
310
|
-
tabs[1].title = 'Passive Notebook';
|
|
311
|
-
tabs[1].active = false;
|
|
312
|
-
vi.stubGlobal('chrome', chrome);
|
|
313
|
-
|
|
314
|
-
const mod = await import('./background');
|
|
315
|
-
const result = await mod.__test__.handleBindCurrent(
|
|
316
|
-
{
|
|
317
|
-
id: 'bind-fallback',
|
|
318
|
-
action: 'bind-current',
|
|
319
|
-
workspace: 'site:notebooklm',
|
|
320
|
-
matchDomain: 'notebooklm.google.com',
|
|
321
|
-
matchPathPrefix: '/notebook/',
|
|
322
|
-
},
|
|
323
|
-
'site:notebooklm',
|
|
324
|
-
);
|
|
325
|
-
|
|
326
|
-
expect(result).toEqual({
|
|
327
|
-
id: 'bind-fallback',
|
|
328
|
-
ok: true,
|
|
329
|
-
data: expect.objectContaining({
|
|
330
|
-
tabId: 2,
|
|
331
|
-
windowId: 2,
|
|
332
|
-
url: 'https://notebooklm.google.com/notebook/nb-passive',
|
|
333
|
-
title: 'Passive Notebook',
|
|
334
|
-
}),
|
|
335
|
-
});
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it('bind-current falls back to a matching notebook tab in another window of the same profile', async () => {
|
|
339
|
-
const { chrome, tabs } = createChromeMock();
|
|
340
|
-
tabs[0].windowId = 3;
|
|
341
|
-
tabs[0].url = 'https://notebooklm.google.com/';
|
|
342
|
-
tabs[0].title = 'NotebookLM Home';
|
|
343
|
-
tabs[0].active = true;
|
|
344
|
-
tabs[1].windowId = 2;
|
|
345
|
-
tabs[1].url = 'https://notebooklm.google.com/notebook/nb-other-window';
|
|
346
|
-
tabs[1].title = 'Notebook In Other Window';
|
|
347
|
-
tabs[1].active = false;
|
|
348
|
-
vi.stubGlobal('chrome', chrome);
|
|
349
|
-
|
|
350
|
-
const mod = await import('./background');
|
|
351
|
-
const result = await mod.__test__.handleBindCurrent(
|
|
352
|
-
{
|
|
353
|
-
id: 'bind-cross-window',
|
|
354
|
-
action: 'bind-current',
|
|
355
|
-
workspace: 'site:notebooklm',
|
|
356
|
-
matchDomain: 'notebooklm.google.com',
|
|
357
|
-
matchPathPrefix: '/notebook/',
|
|
358
|
-
},
|
|
359
|
-
'site:notebooklm',
|
|
360
|
-
);
|
|
361
|
-
|
|
362
|
-
expect(result).toEqual({
|
|
363
|
-
id: 'bind-cross-window',
|
|
364
|
-
ok: true,
|
|
365
|
-
data: expect.objectContaining({
|
|
366
|
-
tabId: 2,
|
|
367
|
-
windowId: 2,
|
|
368
|
-
url: 'https://notebooklm.google.com/notebook/nb-other-window',
|
|
369
|
-
title: 'Notebook In Other Window',
|
|
370
|
-
}),
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
it('rejects bind-current when the active tab is not NotebookLM', async () => {
|
|
375
|
-
const { chrome } = createChromeMock();
|
|
376
|
-
vi.stubGlobal('chrome', chrome);
|
|
377
|
-
|
|
378
|
-
const mod = await import('./background');
|
|
379
|
-
const result = await mod.__test__.handleBindCurrent(
|
|
380
|
-
{
|
|
381
|
-
id: 'bind-miss',
|
|
382
|
-
action: 'bind-current',
|
|
383
|
-
workspace: 'site:notebooklm',
|
|
384
|
-
matchDomain: 'notebooklm.google.com',
|
|
385
|
-
matchPathPrefix: '/notebook/',
|
|
386
|
-
},
|
|
387
|
-
'site:notebooklm',
|
|
388
|
-
);
|
|
389
|
-
|
|
390
|
-
expect(result).toEqual({
|
|
391
|
-
id: 'bind-miss',
|
|
392
|
-
ok: false,
|
|
393
|
-
error: 'No visible tab matching notebooklm.google.com /notebook/',
|
|
394
|
-
});
|
|
395
|
-
});
|
|
396
240
|
});
|
|
@@ -117,8 +117,6 @@ type AutomationSession = {
|
|
|
117
117
|
windowId: number;
|
|
118
118
|
idleTimer: ReturnType<typeof setTimeout> | null;
|
|
119
119
|
idleDeadlineAt: number;
|
|
120
|
-
owned: boolean;
|
|
121
|
-
preferredTabId: number | null;
|
|
122
120
|
};
|
|
123
121
|
|
|
124
122
|
const automationSessions = new Map<string, AutomationSession>();
|
|
@@ -136,11 +134,6 @@ function resetWindowIdleTimer(workspace: string): void {
|
|
|
136
134
|
session.idleTimer = setTimeout(async () => {
|
|
137
135
|
const current = automationSessions.get(workspace);
|
|
138
136
|
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
|
-
}
|
|
144
137
|
try {
|
|
145
138
|
await chrome.windows.remove(current.windowId);
|
|
146
139
|
console.log(`[opencli] Automation window ${current.windowId} (${workspace}) closed (idle timeout)`);
|
|
@@ -180,8 +173,6 @@ async function getAutomationWindow(workspace: string): Promise<number> {
|
|
|
180
173
|
windowId: win.id!,
|
|
181
174
|
idleTimer: null,
|
|
182
175
|
idleDeadlineAt: Date.now() + WINDOW_IDLE_TIMEOUT,
|
|
183
|
-
owned: true,
|
|
184
|
-
preferredTabId: null,
|
|
185
176
|
};
|
|
186
177
|
automationSessions.set(workspace, session);
|
|
187
178
|
console.log(`[opencli] Created automation window ${session.windowId} (${workspace})`);
|
|
@@ -263,8 +254,6 @@ async function handleCommand(cmd: Command): Promise<Result> {
|
|
|
263
254
|
return await handleSessions(cmd);
|
|
264
255
|
case 'set-file-input':
|
|
265
256
|
return await handleSetFileInput(cmd, workspace);
|
|
266
|
-
case 'bind-current':
|
|
267
|
-
return await handleBindCurrent(cmd, workspace);
|
|
268
257
|
default:
|
|
269
258
|
return { id: cmd.id, ok: false, error: `Unknown action: ${cmd.action}` };
|
|
270
259
|
}
|
|
@@ -312,57 +301,7 @@ function isTargetUrl(currentUrl: string | undefined, targetUrl: string): boolean
|
|
|
312
301
|
return normalizeUrlForComparison(currentUrl) === normalizeUrlForComparison(targetUrl);
|
|
313
302
|
}
|
|
314
303
|
|
|
315
|
-
function
|
|
316
|
-
if (!url) return false;
|
|
317
|
-
try {
|
|
318
|
-
const parsed = new URL(url);
|
|
319
|
-
return parsed.hostname === domain || parsed.hostname.endsWith(`.${domain}`);
|
|
320
|
-
} catch {
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function matchesBindCriteria(tab: chrome.tabs.Tab, cmd: Command): boolean {
|
|
326
|
-
if (!tab.id || !isDebuggableUrl(tab.url)) return false;
|
|
327
|
-
if (cmd.matchDomain && !matchesDomain(tab.url, cmd.matchDomain)) return false;
|
|
328
|
-
if (cmd.matchPathPrefix) {
|
|
329
|
-
try {
|
|
330
|
-
const parsed = new URL(tab.url!);
|
|
331
|
-
if (!parsed.pathname.startsWith(cmd.matchPathPrefix)) return false;
|
|
332
|
-
} catch {
|
|
333
|
-
return false;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
return true;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function isNotebooklmWorkspace(workspace: string): boolean {
|
|
340
|
-
return workspace === 'site:notebooklm';
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function classifyNotebooklmUrl(url?: string): 'notebook' | 'home' | 'other' {
|
|
344
|
-
if (!url) return 'other';
|
|
345
|
-
try {
|
|
346
|
-
const parsed = new URL(url);
|
|
347
|
-
if (parsed.hostname !== 'notebooklm.google.com') return 'other';
|
|
348
|
-
return parsed.pathname.startsWith('/notebook/') ? 'notebook' : 'home';
|
|
349
|
-
} catch {
|
|
350
|
-
return 'other';
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function scoreWorkspaceTab(workspace: string, tab: chrome.tabs.Tab): number {
|
|
355
|
-
if (!tab.id || !isDebuggableUrl(tab.url)) return -1;
|
|
356
|
-
if (isNotebooklmWorkspace(workspace)) {
|
|
357
|
-
const kind = classifyNotebooklmUrl(tab.url);
|
|
358
|
-
if (kind === 'other') return -1;
|
|
359
|
-
if (kind === 'notebook') return tab.active ? 400 : 300;
|
|
360
|
-
return tab.active ? 200 : 100;
|
|
361
|
-
}
|
|
362
|
-
return -1;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function setWorkspaceSession(workspace: string, session: Omit<AutomationSession, 'idleTimer' | 'idleDeadlineAt'>): void {
|
|
304
|
+
function setWorkspaceSession(workspace: string, session: Pick<AutomationSession, 'windowId'>): void {
|
|
366
305
|
const existing = automationSessions.get(workspace);
|
|
367
306
|
if (existing?.idleTimer) clearTimeout(existing.idleTimer);
|
|
368
307
|
automationSessions.set(workspace, {
|
|
@@ -372,29 +311,6 @@ function setWorkspaceSession(workspace: string, session: Omit<AutomationSession,
|
|
|
372
311
|
});
|
|
373
312
|
}
|
|
374
313
|
|
|
375
|
-
async function maybeBindWorkspaceToExistingTab(workspace: string): Promise<number | null> {
|
|
376
|
-
if (!isNotebooklmWorkspace(workspace)) return null;
|
|
377
|
-
const tabs = await chrome.tabs.query({});
|
|
378
|
-
let bestTab: chrome.tabs.Tab | null = null;
|
|
379
|
-
let bestScore = -1;
|
|
380
|
-
for (const tab of tabs) {
|
|
381
|
-
const score = scoreWorkspaceTab(workspace, tab);
|
|
382
|
-
if (score > bestScore) {
|
|
383
|
-
bestScore = score;
|
|
384
|
-
bestTab = tab;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
if (!bestTab?.id || bestScore < 0) return null;
|
|
388
|
-
setWorkspaceSession(workspace, {
|
|
389
|
-
windowId: bestTab.windowId,
|
|
390
|
-
owned: false,
|
|
391
|
-
preferredTabId: bestTab.id,
|
|
392
|
-
});
|
|
393
|
-
console.log(`[opencli] Workspace ${workspace} bound to existing tab ${bestTab.id} in window ${bestTab.windowId}`);
|
|
394
|
-
resetWindowIdleTimer(workspace);
|
|
395
|
-
return bestTab.id;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
314
|
/**
|
|
399
315
|
* Resolve target tab in the automation window.
|
|
400
316
|
* If explicit tabId is given, use that directly.
|
|
@@ -408,9 +324,7 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi
|
|
|
408
324
|
try {
|
|
409
325
|
const tab = await chrome.tabs.get(tabId);
|
|
410
326
|
const session = automationSessions.get(workspace);
|
|
411
|
-
const matchesSession = session
|
|
412
|
-
? (session.preferredTabId !== null ? session.preferredTabId === tabId : tab.windowId === session.windowId)
|
|
413
|
-
: false;
|
|
327
|
+
const matchesSession = session ? tab.windowId === session.windowId : false;
|
|
414
328
|
if (isDebuggableUrl(tab.url) && matchesSession) return tabId;
|
|
415
329
|
if (session && !matchesSession) {
|
|
416
330
|
console.warn(`[opencli] Tab ${tabId} is not bound to workspace ${workspace}, re-resolving`);
|
|
@@ -424,20 +338,6 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi
|
|
|
424
338
|
}
|
|
425
339
|
}
|
|
426
340
|
|
|
427
|
-
const adoptedTabId = await maybeBindWorkspaceToExistingTab(workspace);
|
|
428
|
-
if (adoptedTabId !== null) return adoptedTabId;
|
|
429
|
-
|
|
430
|
-
const existingSession = automationSessions.get(workspace);
|
|
431
|
-
if (existingSession && existingSession.preferredTabId !== null) {
|
|
432
|
-
try {
|
|
433
|
-
const preferredTabId = existingSession.preferredTabId;
|
|
434
|
-
const preferredTab = await chrome.tabs.get(preferredTabId);
|
|
435
|
-
if (isDebuggableUrl(preferredTab.url)) return preferredTab.id!;
|
|
436
|
-
} catch {
|
|
437
|
-
automationSessions.delete(workspace);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
341
|
// Get (or create) the automation window
|
|
442
342
|
const windowId = await getAutomationWindow(workspace);
|
|
443
343
|
|
|
@@ -470,14 +370,6 @@ async function resolveTabId(tabId: number | undefined, workspace: string): Promi
|
|
|
470
370
|
async function listAutomationTabs(workspace: string): Promise<chrome.tabs.Tab[]> {
|
|
471
371
|
const session = automationSessions.get(workspace);
|
|
472
372
|
if (!session) return [];
|
|
473
|
-
if (session.preferredTabId !== null) {
|
|
474
|
-
try {
|
|
475
|
-
return [await chrome.tabs.get(session.preferredTabId)];
|
|
476
|
-
} catch {
|
|
477
|
-
automationSessions.delete(workspace);
|
|
478
|
-
return [];
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
373
|
try {
|
|
482
374
|
return await chrome.tabs.query({ windowId: session.windowId });
|
|
483
375
|
} catch {
|
|
@@ -689,12 +581,10 @@ async function handleScreenshot(cmd: Command, workspace: string): Promise<Result
|
|
|
689
581
|
async function handleCloseWindow(cmd: Command, workspace: string): Promise<Result> {
|
|
690
582
|
const session = automationSessions.get(workspace);
|
|
691
583
|
if (session) {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
// Window may already be closed
|
|
697
|
-
}
|
|
584
|
+
try {
|
|
585
|
+
await chrome.windows.remove(session.windowId);
|
|
586
|
+
} catch {
|
|
587
|
+
// Window may already be closed
|
|
698
588
|
}
|
|
699
589
|
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
700
590
|
automationSessions.delete(workspace);
|
|
@@ -726,49 +616,11 @@ async function handleSessions(cmd: Command): Promise<Result> {
|
|
|
726
616
|
return { id: cmd.id, ok: true, data };
|
|
727
617
|
}
|
|
728
618
|
|
|
729
|
-
async function handleBindCurrent(cmd: Command, workspace: string): Promise<Result> {
|
|
730
|
-
const activeTabs = await chrome.tabs.query({ active: true, lastFocusedWindow: true });
|
|
731
|
-
const fallbackTabs = await chrome.tabs.query({ lastFocusedWindow: true });
|
|
732
|
-
const allTabs = await chrome.tabs.query({});
|
|
733
|
-
const boundTab = activeTabs.find((tab) => matchesBindCriteria(tab, cmd))
|
|
734
|
-
?? fallbackTabs.find((tab) => matchesBindCriteria(tab, cmd))
|
|
735
|
-
?? allTabs.find((tab) => matchesBindCriteria(tab, cmd));
|
|
736
|
-
if (!boundTab?.id) {
|
|
737
|
-
return {
|
|
738
|
-
id: cmd.id,
|
|
739
|
-
ok: false,
|
|
740
|
-
error: cmd.matchDomain || cmd.matchPathPrefix
|
|
741
|
-
? `No visible tab matching ${cmd.matchDomain ?? 'domain'}${cmd.matchPathPrefix ? ` ${cmd.matchPathPrefix}` : ''}`
|
|
742
|
-
: 'No active debuggable tab found',
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
setWorkspaceSession(workspace, {
|
|
747
|
-
windowId: boundTab.windowId,
|
|
748
|
-
owned: false,
|
|
749
|
-
preferredTabId: boundTab.id,
|
|
750
|
-
});
|
|
751
|
-
resetWindowIdleTimer(workspace);
|
|
752
|
-
console.log(`[opencli] Workspace ${workspace} explicitly bound to tab ${boundTab.id} (${boundTab.url})`);
|
|
753
|
-
return {
|
|
754
|
-
id: cmd.id,
|
|
755
|
-
ok: true,
|
|
756
|
-
data: {
|
|
757
|
-
tabId: boundTab.id,
|
|
758
|
-
windowId: boundTab.windowId,
|
|
759
|
-
url: boundTab.url,
|
|
760
|
-
title: boundTab.title,
|
|
761
|
-
workspace,
|
|
762
|
-
},
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
|
|
766
619
|
export const __test__ = {
|
|
767
620
|
handleNavigate,
|
|
768
621
|
isTargetUrl,
|
|
769
622
|
handleTabs,
|
|
770
623
|
handleSessions,
|
|
771
|
-
handleBindCurrent,
|
|
772
624
|
resolveTabId,
|
|
773
625
|
resetWindowIdleTimer,
|
|
774
626
|
getSession: (workspace: string = 'default') => automationSessions.get(workspace) ?? null,
|
|
@@ -782,11 +634,9 @@ export const __test__ = {
|
|
|
782
634
|
}
|
|
783
635
|
setWorkspaceSession(workspace, {
|
|
784
636
|
windowId,
|
|
785
|
-
owned: true,
|
|
786
|
-
preferredTabId: null,
|
|
787
637
|
});
|
|
788
638
|
},
|
|
789
|
-
setSession: (workspace: string, session: { windowId: number
|
|
639
|
+
setSession: (workspace: string, session: { windowId: number }) => {
|
|
790
640
|
setWorkspaceSession(workspace, session);
|
|
791
641
|
},
|
|
792
642
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Everything else is just JS code sent via 'exec'.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export type Action = 'exec' | 'navigate' | 'tabs' | 'cookies' | 'screenshot' | 'close-window' | 'sessions' | 'set-file-input'
|
|
8
|
+
export type Action = 'exec' | 'navigate' | 'tabs' | 'cookies' | 'screenshot' | 'close-window' | 'sessions' | 'set-file-input';
|
|
9
9
|
|
|
10
10
|
export interface Command {
|
|
11
11
|
/** Unique request ID */
|
|
@@ -26,10 +26,6 @@ export interface Command {
|
|
|
26
26
|
index?: number;
|
|
27
27
|
/** Cookie domain filter */
|
|
28
28
|
domain?: string;
|
|
29
|
-
/** Optional hostname/domain to require for current-tab binding */
|
|
30
|
-
matchDomain?: string;
|
|
31
|
-
/** Optional pathname prefix to require for current-tab binding */
|
|
32
|
-
matchPathPrefix?: string;
|
|
33
29
|
/** Screenshot format: png (default) or jpeg */
|
|
34
30
|
format?: 'png' | 'jpeg';
|
|
35
31
|
/** JPEG quality (0-100), only for jpeg format */
|