// Dev-only: CDP screencast while the scout-dest dialog is open — checks // whether ANY live frame paints the dialog plate (vs. the stale-capture // hypothesis). Usage: // node dev/cdp-cast.mjs '' [ms] import { mkdir, writeFile } from 'node:fs/promises'; const [url, dir, readyExpr, ms] = process.argv.slice(2); const CDP = process.env.CDP_PORT ? `127.0.0.1:${CDP_PORT}` : '127.0.0.1:9333'; const dur = Number(ms ?? 4000); let target = await (await fetch(`http://${CDP}/json/list`)).json() .then((ts) => ts.find((t) => t.type === 'page')); if (!target) { target = await (await fetch(`http://${CDP}/json/new?${encodeURIComponent(url)}`, { method: 'PUT' })).json(); } const ws = new WebSocket(target.webSocketDebuggerUrl); let id = 0; const pending = new Map(); const send = (method, params = {}) => new Promise((res, rej) => { const mid = ++id; pending.set(mid, { res, rej }); ws.send(JSON.stringify({ id: mid, method, params })); }); const frames = []; await new Promise((res, rej) => { ws.onopen = res; ws.onerror = (e) => rej(new Error('ws')); }); ws.onmessage = (m) => { const msg = JSON.parse(m.data); if (msg.id && pending.has(msg.id)) { const { res, rej } = pending.get(msg.id); pending.delete(msg.id); msg.error ? rej(new Error(msg.error.message)) : res(msg.result); } else if (msg.method === 'Page.screencastFrame') { frames.push(msg.params.data); send('Page.screencastFrameAck', { sessionId: msg.params.sessionId }); } }; await send('Page.enable'); await send('Runtime.enable'); await send('Network.enable'); await send('Network.setCacheDisabled', { cacheDisabled: true }); await send('Page.navigate', { url: 'about:blank' }); await new Promise((r) => setTimeout(r, 300)); await send('Page.navigate', { url }); const evaluate = async (expr) => { const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true, awaitPromise: true }); if (r.exceptionDetails) throw new Error('page: ' + (r.exceptionDetails.exception?.description ?? r.exceptionDetails.text)); return r.result?.value; }; const deadline = Date.now() + 120000; while (Date.now() < deadline) { const v = await evaluate(readyExpr).catch(() => null); if (v) break; await new Promise((r) => setTimeout(r, 200)); } await send('Page.startScreencast', { format: 'png', everyNthFrame: 1 }); await new Promise((r) => setTimeout(r, dur)); await send('Page.stopScreencast'); await mkdir(dir, { recursive: true }); let i = 0; for (const data of frames) { await writeFile(`${dir}/frame-${String(i).padStart(3, '0')}.png`, Buffer.from(data, 'base64')); i++; } console.log(`captured ${i} frames in ${dir}`); ws.close();