From 0d29b7978b86088d13b098937ce580382cf68ea1 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Thu, 17 Sep 2026 17:40:56 -0700 Subject: [PATCH] Add read-only single-tab recording gate --- slab/captutor/lib/single-tab.mjs | 14 ++++++++++++++ slab/captutor/test/single-tab.test.mjs | 5 +++++ 2 files changed, 19 insertions(+) create mode 100644 slab/captutor/lib/single-tab.mjs create mode 100644 slab/captutor/test/single-tab.test.mjs diff --git a/slab/captutor/lib/single-tab.mjs b/slab/captutor/lib/single-tab.mjs new file mode 100644 index 000000000..5d8cf042d --- /dev/null +++ b/slab/captutor/lib/single-tab.mjs @@ -0,0 +1,14 @@ +// Read-only recording gate: never close or navigate unrelated tabs. +export async function assertSingleRecordingTab(cdp){ + const current=await cdp.send('Browser.getWindowForTarget'); + const {targetInfos}=await cdp.send('Target.getTargets'); + if(!Array.isArray(targetInfos))throw Error('Cannot inspect recording-window tabs'); + let count=0; + for(const t of targetInfos){ + if(t.type!=='page')continue; + const {windowId}=await cdp.send('Browser.getWindowForTarget',{targetId:t.targetId}); + if(windowId===current.windowId)count++; + } + if(count!==1)throw Error(`Recording window has ${count} tabs; move the project tab into its own window before recording. Preserve all other tabs.`); + return {windowId:current.windowId,tabCount:count}; +} diff --git a/slab/captutor/test/single-tab.test.mjs b/slab/captutor/test/single-tab.test.mjs new file mode 100644 index 000000000..43547194f --- /dev/null +++ b/slab/captutor/test/single-tab.test.mjs @@ -0,0 +1,5 @@ +import test from 'node:test';import assert from 'node:assert/strict';import {assertSingleRecordingTab} from '../lib/single-tab.mjs'; +const mock=windows=>({send:async(method,p)=>{if(method==='Target.getTargets')return {targetInfos:windows.map((w,i)=>({type:'page',targetId:String(i)}))};if(method==='Browser.getWindowForTarget')return {windowId:p?.targetId===undefined?1:windows[Number(p.targetId)]};throw Error('Unexpected mutation '+method);}}); +test('one recording tab accepts other windows without mutation',async()=>assert.equal((await assertSingleRecordingTab(mock([1,2,2]))).tabCount,1)); +test('extra recording tab fails without closing it',async()=>assert.rejects(assertSingleRecordingTab(mock([1,1])),/2 tabs/)); +test('missing recording target fails closed',async()=>assert.rejects(assertSingleRecordingTab(mock([2])),/0 tabs/)); -- 2.51.2