/** * Playwright codegen headed 录制(跨平台,Windows 无需 bash) * task-121 / GD-15 */ import { spawn } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; import { isForbiddenQuoteUrl } from "@/lib/rpa/env"; const ROOT = process.cwd(); const OUTPUT_DIR = path.join(ROOT, ".rpa", "recordings"); const STORAGE_PATH = process.env.RPA_STORAGE_STATE_PATH?.trim() || path.join(ROOT, ".rpa", "mothership-storage.json"); function printHelp(): void { console.log(` 用法: npm run record:mothership [-- 起始URL] 启动 Playwright codegen(headed),录制 Mothership 匿名查价完整路径。 禁止入口(GD-15): - dashboard.mothership.com - www.mothership.com/quote 产出: .rpa/recordings/mothership-<时间戳>.js .rpa/mothership-storage.json(若会话可持久化) 标准样例: LA 90001 → Dallas 75201,见 docs/rpa-recording-sop.md `); } 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 main(): void { const startUrl = process.argv[2]; if (startUrl === "-h" || startUrl === "--help") { printHelp(); process.exit(0); } if (startUrl && isForbiddenQuoteUrl(startUrl)) { console.error("错误:禁止使用入口 URL(GD-15)"); console.error("请使用 headed 录制验证的匿名公开报价 URL,见 docs/rpa-recording-sop.md"); process.exit(1); } fs.mkdirSync(OUTPUT_DIR, { recursive: true }); fs.mkdirSync(path.dirname(STORAGE_PATH), { recursive: true }); const outputFile = path.join(OUTPUT_DIR, `mothership-${formatTimestamp()}.js`); const args = [ "playwright", "codegen", "--target=javascript", "-o", outputFile, `--save-storage=${STORAGE_PATH}`, ]; if (fs.existsSync(STORAGE_PATH)) { args.push(`--load-storage=${STORAGE_PATH}`); console.log(`已加载已有 storageState: ${STORAGE_PATH}`); } if (startUrl) { args.push(startUrl); } else { console.log("未指定起始 URL:请在 codegen 浏览器中手动导航至匿名报价页"); } console.log("=== Mothership RPA 录制(headed)==="); console.log("RPA_HEADLESS=false"); console.log("禁止入口: dashboard.mothership.com / www.mothership.com/quote"); console.log(`脚本产出: ${outputFile}`); console.log(`storageState: ${STORAGE_PATH}`); console.log("SOP: docs/rpa-recording-sop.md"); console.log(""); const child = spawn("npx", args, { stdio: "inherit", shell: true, cwd: ROOT, env: { ...process.env, RPA_HEADLESS: "false" }, }); child.on("error", (error) => { console.error("启动 playwright codegen 失败:", error); process.exit(1); }); child.on("exit", (code) => { process.exit(code ?? 0); }); } main();