#!/usr/bin/env tsx /** * 有头可视化补录 — 逐条跑 canonical 路径,失败时保持浏览器供人工观测中断点 * * npm run visual:priority1-canonical-inspect * $env:PRIORITY1_INSPECT_FILTER="P01"; npm run visual:priority1-canonical-inspect * $env:PRIORITY1_INSPECT_START="9"; npm run visual:priority1-canonical-inspect # 从 F01 起 * $env:PRIORITY1_INSPECT_STEP2_ONLY="true"; ... # 仅填 Step2,不等待报价 */ process.env.RPA_HEADLESS = "false"; process.env.RPA_KEEP_BROWSER_OPEN = "true"; process.env.RPA_SLOW_MO_MS = process.env.RPA_SLOW_MO_MS ?? "200"; process.env.RPA_DWELL_MS = process.env.RPA_DWELL_MS ?? "3500"; process.env.RPA_QUOTE_WAIT_MS = process.env.RPA_QUOTE_WAIT_MS ?? "120000"; import { createInterface } from "node:readline/promises"; import { stdin as input, stdout as output } from "node:process"; import { mkdirSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { formatPriority1InputSummary, PRIORITY1_CANONICAL_PATHS, } from "@/workers/rpa/priority1/demo-input"; import { buildPriority1FormPlan } from "@/workers/rpa/priority1/form-logic-map"; const outRoot = join(process.cwd(), ".rpa", "visual-priority1-canonical-inspect"); const FILTER = process.env.PRIORITY1_INSPECT_FILTER?.trim(); const START = Number(process.env.PRIORITY1_INSPECT_START ?? 0); const LIMIT = Number(process.env.PRIORITY1_INSPECT_LIMIT ?? 0); const STEP2_ONLY = process.env.PRIORITY1_INSPECT_STEP2_ONLY === "true"; function filterPaths() { let paths = [...PRIORITY1_CANONICAL_PATHS]; if (FILTER) { const re = new RegExp(FILTER, "i"); paths = paths.filter((p) => re.test(p.id) || re.test(p.title)); } if (START > 0) paths = paths.slice(START); if (LIMIT > 0) paths = paths.slice(0, LIMIT); return paths; } async function waitContinue(rl: ReturnType, msg: string) { console.log(`\n${msg}`); await rl.question("按 Enter 继续下一条路径(Ctrl+C 退出)… "); } async function main(): Promise { const paths = filterPaths(); if (paths.length === 0) { console.error("[inspect-canonical] 无匹配路径"); process.exit(1); } mkdirSync(outRoot, { recursive: true }); const { runPriority1VisualChain } = await import("./visual-priority1-full-chain"); const rl = createInterface({ input, output }); console.log("\n[inspect-canonical] 有头可视化补录 — 人工观测链路中断点"); console.log(`[inspect-canonical] 共 ${paths.length} 条 / ${PRIORITY1_CANONICAL_PATHS.length} canonical`); console.log(`[inspect-canonical] Step2 后${STEP2_ONLY ? "停止(不提交报价)" : "提交并等待报价"}`); console.log(`[inspect-canonical] 截图目录: ${outRoot}\n`); const sessionLog: Array<{ id: string; ok: boolean; error?: string; quoteCount: number; outDir: string; }> = []; for (let i = 0; i < paths.length; i += 1) { const path = paths[i]!; console.log(`\n${"═".repeat(64)}`); console.log(`[inspect-canonical] ▶ ${i + 1}/${paths.length} ${path.id}`); console.log(` ${path.title} — ${path.persona}`); console.log(` 原验证状态: ${path.verified === "recorded" ? "已录制" : "待录制"}`); console.log("─".repeat(64)); console.log(formatPriority1InputSummary(path.input)); console.log("─".repeat(64)); console.log("[form-logic-map] 填写计划:"); for (const row of buildPriority1FormPlan(path.input)) { console.log(` ${row.order}. [${row.step}] ${row.field.label} ← ${row.value}`); } console.log("─".repeat(64)); const result = await runPriority1VisualChain({ demo: path.input, caseId: path.id, outDir: join(outRoot, path.id), keepBrowserOpen: true, stopAfterStep2: STEP2_ONLY, }); sessionLog.push({ id: path.id, ok: result.ok, error: result.error, quoteCount: result.quoteCount, outDir: result.outDir, }); if (result.ok && result.quoteCount > 0) { console.log(`\n[inspect-canonical] ✓ ${path.id} 报价 ${result.quoteCount} 条`); } else if (result.ok && STEP2_ONLY) { console.log(`\n[inspect-canonical] ✓ ${path.id} Step2 填写观测通过`); } else { console.log(`\n[inspect-canonical] ✗ ${path.id} 中断: ${result.error ?? "未知"}`); } if (i < paths.length - 1) { await waitContinue(rl, "[inspect-canonical] 请核对浏览器与截图后"); } } rl.close(); const logPath = join(outRoot, "inspect-session.json"); writeFileSync( logPath, `${JSON.stringify({ finishedAt: new Date().toISOString(), paths: sessionLog }, null, 2)}\n`, "utf8", ); const pass = sessionLog.filter((r) => r.ok).length; console.log(`\n[inspect-canonical] 会话结束 ${pass}/${sessionLog.length} 通过`); console.log(`[inspect-canonical] 日志: ${logPath}`); } main().catch((e) => { console.error(e); process.exit(1); });