|
|
#!/usr/bin/env tsx
|
|
|
/**
|
|
|
* 按 canonical 路径 id 启动 Playwright codegen 录制
|
|
|
*
|
|
|
* npm run record:priority1-path -- F04-ftl-open-deck-flatbed
|
|
|
* npm run record:priority1-path -- list
|
|
|
*/
|
|
|
import { spawn } from "node:child_process";
|
|
|
import fs from "node:fs";
|
|
|
import path from "node:path";
|
|
|
import {
|
|
|
PRIORITY1_CANONICAL_PATHS,
|
|
|
formatPriority1InputSummary,
|
|
|
} from "@/workers/rpa/priority1/demo-input";
|
|
|
import { buildPriority1FormPlan } from "@/workers/rpa/priority1/form-logic-map";
|
|
|
import {
|
|
|
DEFAULT_PRIORITY1_QUOTE_URL,
|
|
|
isForbiddenPriority1QuoteUrl,
|
|
|
} from "./record-priority1";
|
|
|
|
|
|
const ROOT = process.cwd();
|
|
|
const OUTPUT_DIR = path.join(ROOT, ".rpa", "recordings");
|
|
|
const STORAGE_PATH =
|
|
|
process.env.PRIORITY1_STORAGE_STATE_PATH?.trim() ||
|
|
|
path.join(ROOT, ".rpa", "priority1-storage.json");
|
|
|
|
|
|
function formatTimestamp(): string {
|
|
|
const d = new Date();
|
|
|
const pad = (n: number) => String(n).padStart(2, "0");
|
|
|
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
|
}
|
|
|
|
|
|
function listPaths(): void {
|
|
|
console.log("\nPriority1 canonical 录制清单(24 条):\n");
|
|
|
for (const p of PRIORITY1_CANONICAL_PATHS) {
|
|
|
console.log(` ${p.id.padEnd(32)} ${p.title} [${p.verified}]`);
|
|
|
}
|
|
|
console.log("\n用法: npm run record:priority1-path -- <path-id>\n");
|
|
|
}
|
|
|
|
|
|
function main(): void {
|
|
|
const pathId = process.argv[2]?.trim();
|
|
|
if (!pathId || pathId === "-h" || pathId === "--help") {
|
|
|
console.log("用法: npm run record:priority1-path -- <path-id|list>");
|
|
|
listPaths();
|
|
|
process.exit(0);
|
|
|
}
|
|
|
if (pathId === "list") {
|
|
|
listPaths();
|
|
|
process.exit(0);
|
|
|
}
|
|
|
|
|
|
const canonical = PRIORITY1_CANONICAL_PATHS.find(
|
|
|
(p) => p.id === pathId || p.id.startsWith(pathId),
|
|
|
);
|
|
|
if (!canonical) {
|
|
|
console.error(`未找到路径: ${pathId}`);
|
|
|
listPaths();
|
|
|
process.exit(1);
|
|
|
}
|
|
|
|
|
|
const startUrl = DEFAULT_PRIORITY1_QUOTE_URL;
|
|
|
if (isForbiddenPriority1QuoteUrl(startUrl)) {
|
|
|
console.error("禁止入口 URL");
|
|
|
process.exit(1);
|
|
|
}
|
|
|
|
|
|
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
|
fs.mkdirSync(path.dirname(STORAGE_PATH), { recursive: true });
|
|
|
|
|
|
const outputFile = path.join(
|
|
|
OUTPUT_DIR,
|
|
|
`priority1-${canonical.id}-${formatTimestamp()}.js`,
|
|
|
);
|
|
|
|
|
|
console.log("\n=== Priority1 单路径录制 ===");
|
|
|
console.log(`路径: ${canonical.id} — ${canonical.title}`);
|
|
|
console.log(`画像: ${canonical.persona}`);
|
|
|
console.log("─".repeat(50));
|
|
|
console.log(formatPriority1InputSummary(canonical.input));
|
|
|
console.log("─".repeat(50));
|
|
|
console.log("[form-logic-map] Step 2 关键步骤:");
|
|
|
for (const row of buildPriority1FormPlan(canonical.input).filter((p) => p.step === "step2")) {
|
|
|
console.log(` ${row.order}. ${row.field.label} ← ${row.value}`);
|
|
|
}
|
|
|
console.log("─".repeat(50));
|
|
|
console.log(`产出: ${outputFile}`);
|
|
|
console.log("录到报价页出现后再关闭 codegen\n");
|
|
|
|
|
|
const args = [
|
|
|
"playwright",
|
|
|
"codegen",
|
|
|
"--target=javascript",
|
|
|
"-o",
|
|
|
outputFile,
|
|
|
`--save-storage=${STORAGE_PATH}`,
|
|
|
];
|
|
|
if (fs.existsSync(STORAGE_PATH)) {
|
|
|
args.push(`--load-storage=${STORAGE_PATH}`);
|
|
|
}
|
|
|
args.push(startUrl);
|
|
|
|
|
|
const child = spawn("npx", args, {
|
|
|
stdio: "inherit",
|
|
|
shell: true,
|
|
|
cwd: ROOT,
|
|
|
env: { ...process.env, RPA_HEADLESS: "false" },
|
|
|
});
|
|
|
|
|
|
child.on("exit", (code) => process.exit(code ?? 0));
|
|
|
}
|
|
|
|
|
|
main();
|