#!/usr/bin/env node import { mkdirSync, writeFileSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; const DEFAULT_PORT = process.env.LWLT_CDP_PORT || '9223'; const DEFAULT_MARKER = 'AI-DRYRUN-NO-SUBMIT'; function parseArgs(argv) { const args = { _: [] }; for (let i = 0; i < argv.length; i += 1) { const arg = argv[i]; if (!arg.startsWith('--')) { args._.push(arg); continue; } const key = arg.slice(2); const next = argv[i + 1]; if (!next || next.startsWith('--')) args[key] = true; else { args[key] = next; i += 1; } } return args; } function usage() { return [ 'Usage:', ' node tools/verify_order_marker.mjs --marker AI-DRYRUN-NO-SUBMIT --out reports/order-marker-verification.json', '', 'Options:', ` --port Chrome DevTools port. Default: ${DEFAULT_PORT}`, ` --marker Obvious test marker to search. Default: ${DEFAULT_MARKER}`, ' --date-from Departure date range start. Default: 2026-8-1.', ' --date-to Departure date range end. Default: same as --date-from.', ' --out Write redacted verification report.', ' --help Show this help.', '', 'This tool searches the authorized independent-order list for an obvious test marker and stores no business row values.' ].join('\n'); } function writeJson(path, data) { mkdirSync(dirname(resolve(path)), { recursive: true }); writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`); } async function getJson(url) { const res = await fetch(url); if (!res.ok) throw new Error(`${res.status} ${res.statusText}: ${url}`); return res.json(); } async function getTarget(port) { const targets = await getJson(`http://127.0.0.1:${port}/json`); const target = targets.find((item) => item.type === 'page' && item.url.includes('ltjt.yunzhi.run')); if (!target) throw new Error(`No ltjt.yunzhi.run page target on port ${port}`); return target; } class CDP { constructor(wsUrl) { this.wsUrl = wsUrl; this.id = 0; this.pending = new Map(); } connect() { return new Promise((resolve, reject) => { this.ws = new WebSocket(this.wsUrl); this.ws.onopen = resolve; this.ws.onerror = (event) => reject(new Error(event.message || event.type || 'WebSocket error')); this.ws.onmessage = (event) => { const msg = JSON.parse(event.data); if (!msg.id || !this.pending.has(msg.id)) return; const { resolve: ok, reject: fail, timer } = this.pending.get(msg.id); clearTimeout(timer); this.pending.delete(msg.id); msg.error ? fail(new Error(msg.error.message)) : ok(msg.result); }; }); } send(method, params = {}, timeoutMs = 30000) { const id = ++this.id; this.ws.send(JSON.stringify({ id, method, params })); return new Promise((resolve, reject) => { const timer = setTimeout(() => { this.pending.delete(id); reject(new Error(`Timeout: ${method}`)); }, timeoutMs); this.pending.set(id, { resolve, reject, timer }); }); } close() { this.ws?.close(); } } async function evaluate(cdp, expression, timeoutMs = 30000) { const result = await cdp.send('Runtime.evaluate', { expression, returnByValue: true, awaitPromise: true }, timeoutMs); if (result.exceptionDetails) { const detail = result.exceptionDetails.exception?.description || result.exceptionDetails.exception?.value || result.exceptionDetails.text || 'Runtime.evaluate failed'; throw new Error(detail); } return result.result.value; } function verifyExpression(marker, dateFrom, dateTo) { return `(async () => { const marker = ${JSON.stringify(marker)}; const dateFrom = ${JSON.stringify(dateFrom || '2026-8-1')}; const dateTo = ${JSON.stringify(dateTo || dateFrom || '2026-8-1')}; function findSameOriginWindow() { const queue = [window]; while (queue.length) { const win = queue.shift(); let doc; try { doc = win.document; } catch (err) { continue; } if (String(doc.location.href || '').includes('ltjt.yunzhi.run')) return win; Array.from(win.frames).forEach((frame) => queue.push(frame)); } return window; } const win = findSameOriginWindow(); const body = new URLSearchParams({ Act: 'JH_OrderList', Tpage: '1', P_Size: '50', riqi: 'chufari', S_chufariqi: dateFrom, S_chufarizhi: dateTo, S_fabudanwei: '老挝联泰', S_tuanxuhao: marker, S_kehuming: '', S_chanpinming: '', S_gendanren: '', S_youkexinxi: '', S_daoyou: '', S_lingdui: '', S_querenshu: '', S_zhuanxianming: '', S_zhuangtai: '' }); const res = await win.fetch('/System/dat/orders.asp', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, body: body.toString() }); const text = await res.text(); const markerOccurrences = marker ? Math.max(0, text.split(marker).length - 1) : 0; const loginTimeout = /登陆|登录|login/i.test(text); const permissionText = /权限|permission/i.test(text); const looksEmpty = /无数据|没有|暂无|empty/i.test(text) || markerOccurrences === 0; let parsedJson = null; try { parsedJson = JSON.parse(text); } catch (err) {} return { ok: res.ok, status: markerOccurrences ? 'order_marker_found' : 'order_marker_not_found', request: { endpoint: '/System/dat/orders.asp', action: 'JH_OrderList', date_range: { from: dateFrom, to: dateTo }, marker_redacted: false, marker }, response: { http_status: res.status, byte_length: new Blob([text]).size, marker_occurrences: markerOccurrences, contains_login_timeout_text: loginTimeout, contains_permission_text: permissionText, looks_empty_or_no_match: looksEmpty, parsed_json: Boolean(parsedJson), json_top_level_keys: parsedJson && typeof parsedJson === 'object' ? Object.keys(parsedJson).slice(0, 30) : [], value_redacted: true }, submit_safety: { live_submit_attempted: false, verification_only: true } }; })()`; } async function main() { const args = parseArgs(process.argv.slice(2)); if (args.help) { console.log(usage()); return; } const marker = args.marker || DEFAULT_MARKER; const dateFrom = args['date-from'] || '2026-8-1'; const dateTo = args['date-to'] || dateFrom; const target = await getTarget(args.port || DEFAULT_PORT); const cdp = new CDP(target.webSocketDebuggerUrl); await cdp.connect(); try { await cdp.send('Runtime.enable'); const output = await evaluate(cdp, verifyExpression(marker, dateFrom, dateTo), 30000); output.generated_at = new Date().toISOString(); if (args.out) writeJson(args.out, output); console.log(JSON.stringify({ status: output.status, marker_occurrences: output.response?.marker_occurrences || 0, http_status: output.response?.http_status, byte_length: output.response?.byte_length, out: args.out || '' }, null, 2)); if (output.status !== 'order_marker_found') process.exit(2); } finally { cdp.close(); } } main().catch((error) => { console.error(error.stack || error.message); process.exit(1); });