/** * Playwright codegen headed 录制 Priority1 公开询价演示路径(跨平台) */ import { spawn } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; 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"); /** 公开即时询价页(Feathery 两步表单) */ export const DEFAULT_PRIORITY1_QUOTE_URL = "https://www.priority1.com/instant-freight-quotes/"; const FORBIDDEN_QUOTE_URL_PATTERNS: RegExp[] = [ /dashboard\.priority1\.com/i, /api\.priority1\.com/i, ]; export function isForbiddenPriority1QuoteUrl(url: string): boolean { return FORBIDDEN_QUOTE_URL_PATTERNS.some((pattern) => pattern.test(url)); } function printHelp(): void { console.log(` 用法: npm run record:priority1 [-- 起始URL] 启动 Playwright codegen(headed),录制 Priority1 公开询价完整路径。 默认起始页: ${DEFAULT_PRIORITY1_QUOTE_URL} 禁止入口: - dashboard.priority1.com(Cabo TMS 登录后台) - api.priority1.com(API 文档门户,非 UI) 产出: .rpa/recordings/priority1-<时间戳>.js .rpa/priority1-storage.json(若会话可持久化) 推荐演示样例(与截图一致): Step 1: LTL | 10118 → 90017 | One Time Shipment | 取货日 | 邮箱 | 电话 Step 2: 48×40×48 in | 21 lbs | Pallet×1 | Class 70 Pickup: Residence + Liftgate + Residential Delivery: Residence + Liftgate + Residential 提交后等待承运商报价列表出现再结束录制 也可从首页开始录制: npm run record:priority1 -- https://www.priority1.com/ `); } 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 rawArg = process.argv[2]; const startUrl = rawArg && rawArg !== "-h" && rawArg !== "--help" ? rawArg : DEFAULT_PRIORITY1_QUOTE_URL; if (rawArg === "-h" || rawArg === "--help") { printHelp(); process.exit(0); } if (isForbiddenPriority1QuoteUrl(startUrl)) { console.error("错误:禁止使用入口 URL"); console.error("请使用公开询价页或首页,勿使用 Cabo 后台 / API 门户"); process.exit(1); } fs.mkdirSync(OUTPUT_DIR, { recursive: true }); fs.mkdirSync(path.dirname(STORAGE_PATH), { recursive: true }); const outputFile = path.join(OUTPUT_DIR, `priority1-${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}`); } args.push(startUrl); console.log("=== Priority1 RPA 录制(headed)==="); console.log("RPA_HEADLESS=false"); console.log("禁止入口: dashboard.priority1.com / api.priority1.com"); console.log(`起始 URL: ${startUrl}`); console.log(`脚本产出: ${outputFile}`); console.log(`storageState: ${STORAGE_PATH}`); 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); }); } const isDirectRun = typeof process.argv[1] === "string" && path.resolve(process.argv[1]) === path.resolve(__filename); if (isDirectRun) { main(); }