// 用 CDP 给页面注入 dm_token 后截图(验证需登录的后台)。 // 用法:node scripts/cdp-shot.mjs // 前置:chrome 已带 --remote-debugging-port=9222 启动,并已打开 http://localhost:5173/login import WebSocket from 'ws'; import { writeFileSync } from 'node:fs'; const [, , TOKEN, TARGET, OUT] = process.argv; const wait = (ms) => new Promise((r) => setTimeout(r, ms)); async function listTargets() { const res = await fetch('http://localhost:9222/json/list'); return res.json(); } async function main() { // 找到 5173 的页面 target。 let ws = ''; for (let i = 0; i < 30; i++) { const ts = await listTargets(); const t = ts.find((x) => x.type === 'page' && /localhost:5173/.test(x.url)); if (t) { ws = t.webSocketDebuggerUrl; break; } await wait(300); } if (!ws) throw new Error('no 5173 page target'); const sock = new WebSocket(ws, { perMessageDeflate: false, maxPayload: 64 * 1024 * 1024 }); let id = 0; const pending = new Map(); const send = (method, params = {}) => new Promise((resolve) => { const mid = ++id; pending.set(mid, resolve); sock.send(JSON.stringify({ id: mid, method, params })); }); await new Promise((r) => sock.on('open', r)); sock.on('message', (buf) => { const msg = JSON.parse(buf.toString()); if (msg.id && pending.has(msg.id)) { pending.get(msg.id)(msg.result); pending.delete(msg.id); return; } if (msg.method === 'Network.responseReceived') { const u = msg.params?.response?.url || ''; if (/\/api\//.test(u)) console.log(' NET', msg.params.response.status, u.replace('http://localhost:3701', '')); } if (msg.method === 'Runtime.exceptionThrown') console.log(' EXC', msg.params?.exceptionDetails?.text, msg.params?.exceptionDetails?.exception?.description?.slice(0, 120)); }); await send('Page.enable'); await send('Runtime.enable'); await send('Network.enable'); // 先到根页把 token 写进 localStorage。 await send('Page.navigate', { url: 'http://localhost:5173/' }); await wait(2500); const set = await send('Runtime.evaluate', { expression: `localStorage.setItem('dm_token', ${JSON.stringify(TOKEN)}); localStorage.getItem('dm_token')?.length || 0`, returnByValue: true, }); console.log('token len in storage:', set?.result?.value); // 第一次整页跳(预热会话,冷启动可能被守卫弹走)。 await send('Runtime.evaluate', { expression: `location.href='http://localhost:5173${TARGET}'` }); await wait(5000); // 第二次跳目标(此时 store 已 authed、user 已拉取 → 稳定停在目标页)。 await send('Page.navigate', { url: `http://localhost:5173${TARGET}` }); await wait(6000); const diag = await send('Runtime.evaluate', { expression: `JSON.stringify({path: location.pathname, tok: (localStorage.getItem('dm_token')||'').length, h1: document.querySelector('h1')?.textContent?.slice(0,20), bodyHas: document.body.innerText.includes('大师墙')})`, returnByValue: true, }); console.log('after nav:', diag?.result?.value); const { data } = await send('Page.captureScreenshot', { format: 'png', captureBeyondViewport: true }); writeFileSync(OUT, Buffer.from(data, 'base64')); console.log('saved', OUT); sock.close(); process.exit(0); } main().catch((e) => { console.error(e); process.exit(1); });