/** * MotherShip 登录态 ×10:遵守官网硬限(禁周末 / 单件≤5000lb) * DIAG_HEADED=true npx tsx scripts/diag-mothership-rules-x10.ts */ import fs from "node:fs"; import path from "node:path"; import { loadDevEnv } from "@/lib/dev-env"; loadDevEnv(); const wantHeaded = process.env.DIAG_HEADED !== "false"; process.env.RPA_HEADED = wantHeaded ? "true" : "false"; process.env.RPA_HEADLESS = wantHeaded ? "false" : "true"; if (wantHeaded && !process.env.RPA_SLOW_MO_MS?.trim()) { process.env.RPA_SLOW_MO_MS = "80"; } if (!wantHeaded) delete process.env.RPA_SLOW_MO_MS; import { resolveAxelMothershipSuggest } from "@/lib/axel/candidates"; import { snapMothershipReadyDateToWeekday, MOTHERSHIP_MAX_WEIGHT_EACH_LB, } from "@/lib/mothership/logged-in-constraints"; import { runWithMothershipLoginContext } from "@/lib/rpa/mothership-login-context"; import { getCustomerProviderLogin } from "@/modules/customer/provider-credentials"; import type { QuoteRequest } from "@/modules/providers/quote-provider"; import { buildLoggedInAddressSearchQuery, runMothershipLoggedInDashboardQuote, } from "@/workers/rpa/mothership-logged-in-quote"; const READY_DATE = snapMothershipReadyDateToWeekday( process.env.DIAG_READY_DATE?.trim() || "2026-07-20", ); const READY_TIME = process.env.DIAG_READY_TIME?.trim() || "12:00 PM"; /** 10 组线路;单件重量均 ≤5000 */ const LANES = [ { name: "Brea→Fairhope", pickup: "789 East Elm Street suite 301, Brea, CA, USA", delivery: "456 Oak Avenue unit 207, Fairhope, AL, USA", weightEach: 250, qty: 2, dims: [60, 60, 60] as const, }, { name: "LA→SF", pickup: "2020 West Washington Boulevard floor 5 rm 508, Los Angeles, CA, USA", delivery: "555 Market St ste 1001, San Francisco, CA, USA", weightEach: 480, qty: 2, dims: [48, 40, 48] as const, }, { name: "Albany→SF", pickup: "101 South Pine Avenue, Albany, NY 12208, USA", delivery: "555 Market St ste 1001, San Francisco, CA, USA", weightEach: 120, qty: 2, dims: [48, 40, 48] as const, }, { name: "MA→OR", pickup: "789 Elm Street, East Bridgewater, MA, USA", delivery: "888 Liberty Street Northeast, Salem, OR, USA", weightEach: 200, qty: 1, dims: [48, 40, 48] as const, }, { name: "Dallas→Chicago", pickup: "1234 Main Street, Dallas, TX, USA", delivery: "200 East Randolph Street, Chicago, IL, USA", weightEach: 500, qty: 2, dims: [48, 40, 40] as const, }, { name: "Atlanta→Denver", pickup: "100 Peachtree Street NW, Atlanta, GA, USA", delivery: "1701 California Street, Denver, CO, USA", weightEach: 300, qty: 2, dims: [48, 40, 48] as const, }, { name: "Brea→LA", pickup: "789 East Elm Street, Brea, CA, USA", delivery: "2020 West Washington Boulevard floor 5 rm 508, Los Angeles, CA, USA", weightEach: 50, qty: 1, dims: [40, 40, 40] as const, }, { name: "SF→Albany", pickup: "555 Market St ste 1001, San Francisco, CA, USA", delivery: "101 South Pine Avenue, Albany, NY 12208, USA", weightEach: 150, qty: 2, dims: [48, 40, 48] as const, }, { name: "Chicago→Dallas", pickup: "200 East Randolph Street, Chicago, IL, USA", delivery: "1234 Main Street, Dallas, TX, USA", weightEach: 400, qty: 1, dims: [48, 40, 48] as const, }, { name: "Denver→Atlanta", pickup: "1701 California Street, Denver, CO, USA", delivery: "100 Peachtree Street NW, Atlanta, GA, USA", weightEach: 220, qty: 2, dims: [48, 40, 40] as const, }, ] as const; type Row = { n: number; name: string; ok: boolean; ms: number; lastStep: string; detail: string; }; const timingBuf: string[] = []; const origLog = console.log.bind(console); console.log = (...args: unknown[]) => { const line = args.map(String).join(" "); if ( line.includes("[rpa] logged-in timing:") || line.includes("[rpa] logged-in step:") || line.includes("still no rate-card") || line.includes("patched details") || line.includes("rate-card ready") || line.includes("FAILED") || line.includes("weekend") ) { timingBuf.push(line); } origLog(...args); }; async function pickFirst(query: string) { const list = await resolveAxelMothershipSuggest(query); const hit = list.find((c) => c.selectable !== false && c.option_id.trim()); if (!hit) throw new Error(`无联想: ${query.slice(0, 50)}`); return hit; } function toAddr(c: Awaited>) { return { street: c.street, city: c.city, state: c.state, zip: c.zip || "", placeId: c.option_id, formattedAddress: c.formatted_address || c.display_label, selectedFromSuggestions: true, mothershipOptionId: c.option_id, mothershipDisplayLabel: c.display_label, selectedFromMothership: true, }; } function lastStep(): string { for (let i = timingBuf.length - 1; i >= 0; i -= 1) { const m = timingBuf[i]!.match(/step="([^"]+)"/); if (m) return m[1]!; if (timingBuf[i]!.includes("still no rate-card")) return "wait rate-card"; } return "(none)"; } async function main(): Promise { for (const lane of LANES) { if (lane.weightEach > MOTHERSHIP_MAX_WEIGHT_EACH_LB) { throw new Error(`lane ${lane.name} weightEach 超限`); } } const customerId = process.env.DIAG_CUSTOMER_ID?.trim() || "CUST_001"; const login = await getCustomerProviderLogin(customerId, "mothership"); if (!login) { console.error(`[diag] 无 MotherShip 账密 customer=${customerId}`); process.exit(1); } console.log("=== MotherShip rules ×10(禁周末 / 单件≤5000)==="); console.log( `headed=${wantHeaded} ready=${READY_DATE} ${READY_TIME} maxWeightEach=${MOTHERSHIP_MAX_WEIGHT_EACH_LB}`, ); const rows: Row[] = []; for (let n = 0; n < LANES.length; n += 1) { const lane = LANES[n]!; timingBuf.length = 0; const t0 = Date.now(); console.log(`\n---------- ${n + 1}/10 ${lane.name} wt=${lane.weightEach} ----------`); try { const [p, d] = await Promise.all([ pickFirst(lane.pickup), pickFirst(lane.delivery), ]); const req: QuoteRequest = { cargoHash: `diag-rules-${n}-${Date.now()}`, pickup: toAddr(p), delivery: toAddr(d), weightLb: lane.weightEach, dimsIn: { l: lane.dims[0], w: lane.dims[1], h: lane.dims[2] }, palletCount: lane.qty, cargoType: "general_freight", readyDate: READY_DATE, readyTime: READY_TIME, cargoLines: [ { cargoType: "pallet", quantity: lane.qty, weightLb: lane.weightEach, lengthIn: lane.dims[0], widthIn: lane.dims[1], heightIn: lane.dims[2], }, ], }; console.log( `[diag] ${buildLoggedInAddressSearchQuery(req.pickup)} → ${buildLoggedInAddressSearchQuery(req.delivery)}`, ); const items = await runWithMothershipLoginContext(login, "customer", () => runMothershipLoggedInDashboardQuote(req), ); const ms = Date.now() - t0; const carriers = items.map((i) => `${i.carrier}=$${i.rawTotal}`).join(", "); rows.push({ n: n + 1, name: lane.name, ok: true, ms, lastStep: lastStep(), detail: carriers, }); console.log(`[diag] OK ${ms}ms ${carriers}`); } catch (err) { const ms = Date.now() - t0; const msg = err instanceof Error ? err.message : String(err); rows.push({ n: n + 1, name: lane.name, ok: false, ms, lastStep: lastStep(), detail: msg.slice(0, 220), }); console.log(`[diag] FAIL ${ms}ms step=${lastStep()} ${msg.slice(0, 160)}`); } } const okN = rows.filter((r) => r.ok).length; const reportPath = path.join( process.cwd(), ".rpa", "diag", `rules-x10-${new Date().toISOString().replace(/[:.]/g, "-")}.md`, ); const md = [ `# MotherShip rules ×10`, "", `- ready=${READY_DATE}(已 snap 工作日)`, `- maxWeightEach=${MOTHERSHIP_MAX_WEIGHT_EACH_LB}`, `- ok=${okN}/10`, "", "| # | lane | ok | ms | lastStep | detail |", "|---|------|----|----|----------|--------|", ...rows.map( (r) => `| ${r.n} | ${r.name} | ${r.ok ? "Y" : "N"} | ${r.ms} | ${r.lastStep} | ${r.detail.replace(/\|/g, "/")} |`, ), "", ].join("\n"); fs.mkdirSync(path.dirname(reportPath), { recursive: true }); fs.writeFileSync(reportPath, md, "utf8"); console.log(`\n=== SUMMARY ok=${okN}/10 report=${reportPath} ===`); for (const r of rows) { console.log( ` #${r.n} ${r.ok ? "OK" : "FAIL"} ${r.name} ${r.ms}ms ${r.lastStep}`, ); } process.exit(okN === 10 ? 0 : 1); } main().catch((e) => { console.error(e); process.exit(1); });