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

133 lines
5.3 KiB
JavaScript

#!/usr/bin/env node
import { readFile } from 'node:fs/promises';
const DEFAULT_PORT_FILE = '/Users/inmanx/Library/Application Support/Google/Chrome/DevToolsActivePort';
const EXTENSION_ID = 'kafggjjlhebccdgkechmbgflkaifkccf';
function argument(name, fallback = '') {
const index = process.argv.indexOf(name);
return index >= 0 && process.argv[index + 1] ? process.argv[index + 1] : fallback;
}
function connect(url) {
return new Promise((resolve, reject) => {
const socket = new WebSocket(url);
const timer = setTimeout(() => reject(new Error('browser websocket connection timed out')), 15000);
socket.addEventListener('open', () => {
clearTimeout(timer);
resolve(socket);
}, { once: true });
socket.addEventListener('error', () => {
clearTimeout(timer);
reject(new Error('browser websocket connection failed'));
}, { once: true });
});
}
function messenger(socket) {
let sequence = 0;
const pending = new Map();
socket.addEventListener('message', (event) => {
const message = JSON.parse(String(event.data || '{}'));
const handler = pending.get(message.id);
if (!handler) return;
pending.delete(message.id);
if (message.error) handler.reject(new Error(message.error.message || JSON.stringify(message.error)));
else handler.resolve(message.result || {});
});
return (method, params = {}, sessionId = '', timeoutMs = 8000) => new Promise((resolve, reject) => {
const id = ++sequence;
const timer = setTimeout(() => {
pending.delete(id);
reject(new Error(`${method} timed out`));
}, timeoutMs);
pending.set(id, {
resolve: (value) => {
clearTimeout(timer);
resolve(value);
},
reject: (error) => {
clearTimeout(timer);
reject(error);
}
});
socket.send(JSON.stringify({ id, method, params, ...(sessionId ? { sessionId } : {}) }));
});
}
const taskId = argument('--task');
const resumePrewrite = process.argv.includes('--resume-prewrite');
const reloadExtension = process.argv.includes('--reload-extension');
const cleanupRuntime = process.argv.includes('--cleanup-runtime');
const portFile = argument('--port-file', process.env.CDP_PORT_FILE || DEFAULT_PORT_FILE);
const [port, browserId] = (await readFile(portFile, 'utf8')).trim().split(/\r?\n/);
if (!port || !browserId) throw new Error(`invalid DevToolsActivePort file: ${portFile}`);
const browserPath = browserId.startsWith('/') ? browserId : `/devtools/browser/${browserId}`;
const socket = await connect(`ws://127.0.0.1:${port}${browserPath}`);
const send = messenger(socket);
try {
const targets = await send('Target.getTargets');
const worker = (targets.targetInfos || []).find((target) => (
target.type === 'service_worker'
&& target.url === `chrome-extension://${EXTENSION_ID}/background.js`
));
if (!worker) throw new Error(`extension service worker not found: ${EXTENSION_ID}`);
const attached = await send('Target.attachToTarget', { targetId: worker.targetId, flatten: true });
const taskLiteral = JSON.stringify(taskId);
const inspectionExpression = `Promise.all([
chrome.storage.local.get(null),
Promise.resolve(typeof runningTasks === 'undefined' ? [] : [...runningTasks])
]).then(([saved, running]) => {
const taskId = ${taskLiteral};
const executions = saved.businessTaskExecutions || {};
const results = saved.businessTaskResults || {};
const result = results[taskId] || null;
const task = saved.currentBusinessTask || null;
const recent = Object.fromEntries(Object.entries(saved).filter(([key]) => /^last/i.test(key)));
return {
worker: { target_id: ${JSON.stringify(worker.targetId)}, url: ${JSON.stringify(worker.url)} },
running_tasks: running,
execution: executions[taskId] || null,
current_task: task ? {
task_id: task.task_id,
status: task.status,
execution_id: task.execution_id,
received_at: task.received_at,
action: task.operation?.action || ''
} : null,
result: result ? {
status: result.status,
stage: result.stage,
message: result.message,
execution_id: result.execution_id,
write_attempted: result.write_attempted,
erp_write_started: result.erp_write_started,
blockers: result.blockers || [],
report_status: result.report?.status || '',
erp_receipt: result.erp_receipt || result.report?.erp_receipt || null,
verification: result.verification || result.report?.verification || null
} : null,
recent_storage: recent
};
})`;
const expression = cleanupRuntime
? 'cleanupStaleRuntimeState()'
: reloadExtension
? `(() => { const version = chrome.runtime.getManifest().version; chrome.runtime.reload(); return { reload_requested: true, version }; })()`
: resumePrewrite
? `resumePrewriteTask(${taskLiteral})`
: inspectionExpression;
const evaluated = await send('Runtime.evaluate', {
expression,
awaitPromise: true,
returnByValue: true
}, attached.sessionId, resumePrewrite ? 240_000 : 8_000);
if (evaluated.exceptionDetails) throw new Error(evaluated.exceptionDetails.text || 'worker evaluation failed');
process.stdout.write(`${JSON.stringify(evaluated.result?.value || null, null, 2)}\n`);
await send('Target.detachFromTarget', { sessionId: attached.sessionId }).catch(() => {});
} finally {
socket.close();
}