vault backup: 2026-11-09 09:18:58

This commit is contained in:
admin committed 2026-11-09 09:18:58 +08:00
1 parent 856d0203da
commit 75072389db
42 files changed
+5994 -37

No files matched your search

+75
View File
@@ -0,0 +1,75 @@
// Minimal Chrome DevTools Protocol client (Node 22 global WebSocket, no npm deps).
const fs = require('fs');
const path = require('path');
class CDP {
constructor(wsUrl) {
this.wsUrl = wsUrl;
this.id = 0;
this.pending = new Map();
this.closed = false;
}
connect(timeoutMs = 10000) {
return new Promise((resolve, reject) => {
const ws = new WebSocket(this.wsUrl);
this.ws = ws;
const timer = setTimeout(() => { try { ws.close(); } catch {} reject(new Error('connect timeout')); }, timeoutMs);
ws.onopen = () => { clearTimeout(timer); resolve(this); };
ws.onerror = () => { clearTimeout(timer); reject(new Error('ws error')); };
ws.onclose = () => {
this.closed = true;
for (const [, p] of this.pending) p.rej(new Error('ws closed'));
this.pending.clear();
};
ws.onmessage = (ev) => {
let m;
try { m = JSON.parse(ev.data); } catch { return; }
if (m.id && this.pending.has(m.id)) {
const p = this.pending.get(m.id);
this.pending.delete(m.id);
if (m.error) p.rej(new Error(m.error.message || 'cdp error'));
else p.res(m.result);
}
};
});
}
send(method, params = {}, timeoutMs = 15000) {
if (this.closed) return Promise.reject(new Error('ws closed'));
const id = ++this.id;
return new Promise((res, rej) => {
this.pending.set(id, { res, rej });
this.ws.send(JSON.stringify({ id, method, params }));
setTimeout(() => {
if (this.pending.has(id)) { this.pending.delete(id); rej(new Error('cdp timeout: ' + method)); }
}, timeoutMs);
});
}
async eval(expression, timeoutMs = 15000) {
const r = await this.send('Runtime.evaluate', {
expression,
returnByValue: true,
awaitPromise: true,
userGesture: true
}, timeoutMs);
if (r.exceptionDetails) {
throw new Error(r.exceptionDetails.exception?.description || r.exceptionDetails.text || 'eval failed');
}
return r.result ? r.result.value : undefined;
}
close() { try { this.ws.close(); } catch {} this.closed = true; }
}
async function listPages(port, urlMatch) {
const res = await fetch(`http://127.0.0.1:${port}/json/list`, { signal: AbortSignal.timeout(5000) });
const all = await res.json();
return all.filter(t => t.type === 'page' && typeof t.url === 'string'
&& t.url.includes(urlMatch) && !t.url.startsWith('devtools://'));
}
const EXPR = fs.readFileSync(path.join(__dirname, 'probe-expression.js'), 'utf8');
module.exports = { CDP, listPages, EXPR };