|
|
/**
|
|
|
* 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<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 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<void> {
|
|
|
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);
|
|
|
});
|