You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
chajia/scripts/visual-priority1-canonical-...

134 lines
4.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/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<typeof createInterface>, msg: string) {
console.log(`\n${msg}`);
await rl.question("按 Enter 继续下一条路径Ctrl+C 退出)… ");
}
async function main(): Promise<void> {
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);
});