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/diag-mothership-albany-sf-x...

223 lines
6.9 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.

/**
* 可视化Albany NY → SF 登录态查价连跑 10 次(复现卡住)
* DIAG_HEADED=true npx tsx scripts/diag-mothership-albany-sf-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 = "120";
}
if (!wantHeaded) delete process.env.RPA_SLOW_MO_MS;
import { resolveAxelMothershipSuggest } from "@/lib/axel/candidates";
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 RUNS = Math.max(1, Number(process.env.DIAG_RUNS || "10") || 10);
const PICKUP_Q = "101 South Pine Avenue, Albany, NY 12208, USA";
const DELIVERY_Q = "555 Market St ste 1001, San Francisco, CA, USA";
const READY_DATE = process.env.DIAG_READY_DATE?.trim() || "2026-07-20";
const READY_TIME = process.env.DIAG_READY_TIME?.trim() || "12:00 PM";
type RunRow = {
n: number;
ok: boolean;
ms: number;
lastStep: string;
carriers: string;
error?: string;
failShot?: 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("[rpa] logged-in: still no rate-card") ||
line.includes("[rpa] logged-in: patched") ||
line.includes("[rpa] logged-in: rate-card ready") ||
line.includes("[rpa] logged-in FAILED")
) {
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<ReturnType<typeof pickFirst>>) {
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 lastStepFromBuf(): 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 (stuck)";
if (timingBuf[i]!.includes("FAILED")) return "FAILED";
}
return "(none)";
}
async function main(): Promise<void> {
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("=== Albany→SF 可视化 ×" + RUNS + " ===");
console.log(
`headed=${wantHeaded} slowMo=${process.env.RPA_SLOW_MO_MS ?? 0} ready=${READY_DATE} ${READY_TIME}`,
);
const [pickupC, deliveryC] = await Promise.all([
pickFirst(PICKUP_Q),
pickFirst(DELIVERY_Q),
]);
console.log(
`[diag] pickup=${buildLoggedInAddressSearchQuery(toAddr(pickupC))}`,
);
console.log(
`[diag] delivery=${buildLoggedInAddressSearchQuery(toAddr(deliveryC))}`,
);
const rows: RunRow[] = [];
for (let n = 1; n <= RUNS; n += 1) {
timingBuf.length = 0;
const t0 = Date.now();
console.log(`\n---------- RUN ${n}/${RUNS} ----------`);
const req: QuoteRequest = {
cargoHash: `diag-albany-sf-${n}-${Date.now()}`,
pickup: toAddr(pickupC),
delivery: toAddr(deliveryC),
weightLb: 120,
dimsIn: { l: 48, w: 40, h: 48 },
palletCount: 2,
cargoType: "box",
readyDate: READY_DATE,
readyTime: READY_TIME,
pickupAccessorials: ["residential", "liftgate"],
deliveryAccessorials: ["cfs", "fbaAppointment"],
cargoLines: [
{
cargoType: "box",
quantity: 2,
weightLb: 120,
lengthIn: 48,
widthIn: 40,
heightIn: 48,
},
],
};
try {
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,
ok: true,
ms,
lastStep: lastStepFromBuf(),
carriers,
});
console.log(`[diag] RUN ${n} OK ${ms}ms ${carriers}`);
} catch (err) {
const ms = Date.now() - t0;
const msg = err instanceof Error ? err.message : String(err);
const diagDir = path.join(process.cwd(), ".rpa", "diag");
const shots = fs.existsSync(diagDir)
? fs
.readdirSync(diagDir)
.filter((f) => f.startsWith("logged-in-fail-") && f.endsWith(".png"))
.sort()
: [];
rows.push({
n,
ok: false,
ms,
lastStep: lastStepFromBuf(),
carriers: "",
error: msg.slice(0, 200),
failShot: shots.at(-1),
});
console.log(`[diag] RUN ${n} FAIL ${ms}ms step=${lastStepFromBuf()} err=${msg.slice(0, 160)}`);
}
}
const okN = rows.filter((r) => r.ok).length;
const reportPath = path.join(
process.cwd(),
".rpa",
"diag",
`albany-sf-x10-${new Date().toISOString().replace(/[:.]/g, "-")}.md`,
);
const md = [
`# Albany→SF ×${RUNS} headed`,
"",
`- ok=${okN}/${RUNS}`,
`- pickup=${PICKUP_Q}`,
`- delivery=${DELIVERY_Q}`,
"",
"| # | ok | ms | lastStep | carriers/error |",
"|---|----|----|----------|----------------|",
...rows.map((r) => {
const detail = r.ok
? r.carriers
: `${r.error ?? ""}${r.failShot ? ` shot=${r.failShot}` : ""}`;
return `| ${r.n} | ${r.ok ? "Y" : "N"} | ${r.ms} | ${r.lastStep} | ${detail.replace(/\|/g, "/")} |`;
}),
"",
].join("\n");
fs.mkdirSync(path.dirname(reportPath), { recursive: true });
fs.writeFileSync(reportPath, md, "utf8");
console.log("\n=== SUMMARY ===");
console.log(`ok=${okN}/${RUNS} report=${reportPath}`);
for (const r of rows) {
console.log(
` #${r.n} ${r.ok ? "OK" : "FAIL"} ${r.ms}ms last=${r.lastStep}${r.error ? " " + r.error.slice(0, 80) : ""}`,
);
}
process.exit(okN === RUNS ? 0 : 1);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});