Files
LWLT-AIBOT/tools/persist_extension_result_via_browser_session.mjs
2026-08-25 10:33:29 +08:00

111 lines
4.2 KiB
JavaScript

#!/usr/bin/env node
import { readFile } from 'node:fs/promises';
import net from 'node:net';
const PAGES_FILE = '/Users/inmanx/.cache/cdp/pages.json';
const SOCKET_ROOT = '/Users/inmanx/.cache/cdp';
const ORIGIN = 'http://127.0.0.1:8786';
function argument(name, fallback = '') {
const index = process.argv.indexOf(name);
return index >= 0 && process.argv[index + 1] ? process.argv[index + 1] : fallback;
}
function daemonRequest(socketPath, cmd, args = []) {
return new Promise((resolve, reject) => {
const socket = net.createConnection(socketPath);
let buffer = '';
const timer = setTimeout(() => {
socket.destroy();
reject(new Error(`CDP daemon ${cmd} timed out`));
}, 15_000);
socket.on('connect', () => {
socket.write(`${JSON.stringify({ id: 1, cmd, args })}\n`);
});
socket.on('data', (chunk) => {
buffer += chunk.toString();
const newline = buffer.indexOf('\n');
if (newline < 0) return;
try {
const response = JSON.parse(buffer.slice(0, newline));
clearTimeout(timer);
socket.end();
if (!response.ok) reject(new Error(response.error || `CDP daemon ${cmd} failed`));
else resolve(response.result || '');
} catch (error) {
clearTimeout(timer);
socket.destroy();
reject(error);
}
});
socket.on('error', (error) => {
clearTimeout(timer);
reject(error);
});
});
}
const taskId = argument('--task-id');
const storageKey = argument('--storage-key');
const targetPrefix = argument('--target', 'DA600CC0');
if (!/^TASK-[A-Za-z0-9_-]+$/.test(taskId)) throw new Error('--task-id is required');
if (!/^ltjt_[A-Za-z0-9_-]+$/.test(storageKey)) throw new Error('--storage-key is required');
const pages = JSON.parse(await readFile(PAGES_FILE, 'utf8'));
const page = pages.find((target) => target.targetId.startsWith(targetPrefix));
if (!page) throw new Error(`page target not found: ${targetPrefix}`);
const socketPath = `${SOCKET_ROOT}/cdp-${page.targetId}.sock`;
const evaluated = JSON.parse(await daemonRequest(socketPath, 'evalraw', [
'Runtime.evaluate',
JSON.stringify({
expression: `sessionStorage.getItem(${JSON.stringify(storageKey)})`,
returnByValue: true
})
]));
const rawResult = evaluated.result?.value;
if (!rawResult) throw new Error('stored extension result is missing');
const result = JSON.parse(rawResult);
if (!result.execution_id || result.status !== 'completed') throw new Error('stored extension result is not a completed durable result');
const cookieResult = JSON.parse(await daemonRequest(socketPath, 'evalraw', [
'Network.getCookies',
JSON.stringify({ urls: [`${ORIGIN}/`] })
]));
const cookies = (cookieResult.cookies || []).filter((cookie) => cookie.domain === '127.0.0.1' || cookie.domain === 'localhost');
if (!cookies.length) throw new Error('authenticated browser session cookie is missing');
const cookieHeader = cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join('; ');
const csrfResponse = await fetch(`${ORIGIN}/api/auth/csrf`, {
headers: { Cookie: cookieHeader },
cache: 'no-store'
});
const csrfBody = await csrfResponse.json();
if (!csrfResponse.ok || !csrfBody.csrf_token) throw new Error(`CSRF request failed: HTTP ${csrfResponse.status}`);
const response = await fetch(`${ORIGIN}/api/tasks/${encodeURIComponent(taskId)}/result`, {
method: 'POST',
headers: {
Cookie: cookieHeader,
'Content-Type': 'application/json',
'X-CSRF-Token': csrfBody.csrf_token
},
body: JSON.stringify({
connection_id: `administrator-browser:${ORIGIN}`,
execution_id: result.execution_id,
result
})
});
const body = await response.json();
if (!response.ok) throw new Error(body.message || body.error || `result persistence failed: HTTP ${response.status}`);
process.stdout.write(`${JSON.stringify({
status: 'extension_result_persisted',
http_status: response.status,
task_id: body.task?.task_id || taskId,
task_status: body.task?.status || '',
task_stage: body.task?.stage || '',
execution_id: result.execution_id,
report_status: result.report?.status || '',
requery_matched: result.report?.requery?.matched === true
}, null, 2)}\n`);