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/probe-widget-la-pomona-quot...

211 lines
7.0 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.

#!/usr/bin/env tsx
/**
* Widget 路线验收LA Warehouse St → Pomona 7th St与 MotherShip 官网截图一致)
*
* 用法:
* npm run dev:start # 候选 API
* npm run probe:widget-la-pomona
*/
import { randomUUID } from "node:crypto";
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { loadDevEnv } from "@/lib/dev-env";
import { shouldKeepBrowserOpen } from "@/lib/rpa/env";
import { hostFetchMothershipCandidates } from "@/lib/frontend/api-client";
import { applyMothershipCandidate } from "@/lib/frontend/mothership-address";
import { RPAContext } from "@/workers/rpa/kernel/context";
import { runMothershipQuoteKernel } from "@/workers/rpa/kernel/orchestrator";
import {
stabilizeQuoteLandingPage,
waitForQuoterWidgetHydrated,
} from "@/workers/rpa/page-prep";
import { closeBrowser, openQuotePage } from "@/workers/rpa/session-manager";
import { formatWidgetQuoteTierLabel } from "@/workers/rpa/quote-capture/widget-truck-tab-quotes";
import { quotePageAdapter } from "@/workers/rpa/quote-page-adapter";
import type { QuoteRequest } from "@/modules/providers/quote-provider";
process.env.RPA_MOCK_MODE = "false";
process.env.RPA_ADDRESS_MODE = "real";
const FAST =
process.argv.includes("--fast") ||
process.env.RPA_FAST_QUOTE === "true" ||
process.env.PROBE_FAST === "1";
if (FAST) {
process.env.RPA_FAST_QUOTE = "true";
process.env.RPA_VISUAL_RUSH = "true";
process.env.RPA_DUAL_ADDRESS_PATCH = "false";
process.env.RPA_BLIND_ADDRESS_FLOW = "true";
process.env.RPA_HUMAN_PACING = "false";
process.env.RPA_SUGGESTION_TIMEOUT_MS =
process.env.RPA_SUGGESTION_TIMEOUT_MS ?? "1000";
process.env.RPA_HEADLESS = process.env.RPA_HEADLESS ?? "false";
} else {
process.env.RPA_VISUAL_RUSH = process.env.RPA_VISUAL_RUSH ?? "false";
process.env.RPA_DUAL_ADDRESS_PATCH =
process.env.RPA_DUAL_ADDRESS_PATCH ?? "true";
process.env.RPA_HEADLESS = process.env.RPA_HEADLESS ?? "true";
}
process.env.RPA_DISABLE_WIDGET_QUOTE_FALLBACK =
process.env.RPA_DISABLE_WIDGET_QUOTE_FALLBACK ?? "false";
loadDevEnv();
const BASE = process.env.PROVE_BASE_URL ?? "http://localhost:3000";
const TOKEN = "demo-host-token";
const CUSTOMER_ID = "CUST_001";
const PAIR = {
label: "1234 Warehouse St LA → 600 W 7th St Pomona",
pickup: { street: "1234 Warehouse Street", city: "Los Angeles", state: "CA" },
delivery: { street: "600 W 7th St", city: "Pomona", state: "CA" },
cargo: {
pallet_count: 2,
weight_lb: 500,
length_in: 48,
width_in: 40,
height_in: 48,
},
};
function draftAddress(d: { street: string; city: string; state: string }) {
return {
street: d.street.trim(),
city: d.city.trim(),
state: d.state.trim(),
zip: "",
place_id: `draft_${d.street}_${d.city}_${d.state}`.replace(/\s+/g, "_").slice(0, 64),
formatted_address: `${d.street}, ${d.city}, ${d.state}`,
selected_from_suggestions: true,
};
}
async function main(): Promise<void> {
const runId = randomUUID().slice(0, 8);
const startedAt = Date.now();
console.log(`[probe:widget-la-pomona] ${runId} ${PAIR.label}`);
console.log(
`[probe] FAST=${FAST} RPA_VISUAL_RUSH=${process.env.RPA_VISUAL_RUSH} RPA_DUAL_ADDRESS_PATCH=${process.env.RPA_DUAL_ADDRESS_PATCH}`,
);
const userPickup = draftAddress(PAIR.pickup);
const userDelivery = draftAddress(PAIR.delivery);
const cand = await hostFetchMothershipCandidates(
BASE,
TOKEN,
CUSTOMER_ID,
userPickup,
userDelivery,
);
if (
cand.code !== 0 ||
!cand.data?.pickup_candidates[0] ||
!cand.data.delivery_candidates[0]
) {
throw new Error(cand.message ?? "候选 API 失败(请先 npm run dev:start");
}
const pc = cand.data.pickup_candidates[0];
const dc = cand.data.delivery_candidates[0];
const pickupApplied = applyMothershipCandidate(userPickup, pc);
const deliveryApplied = applyMothershipCandidate(userDelivery, dc);
const req: QuoteRequest = {
cargoHash: `widget_la_pomona_${runId}`,
pickup: {
street: pickupApplied.street,
city: pickupApplied.city,
state: pickupApplied.state,
zip: pickupApplied.zip ?? "",
placeId: pc.option_id,
formattedAddress: pickupApplied.formatted_address,
selectedFromSuggestions: true,
mothershipOptionId: pc.option_id,
mothershipDisplayLabel: pc.display_label,
selectedFromMothership: true,
},
delivery: {
street: deliveryApplied.street,
city: deliveryApplied.city,
state: deliveryApplied.state,
zip: deliveryApplied.zip ?? "",
placeId: dc.option_id,
formattedAddress: deliveryApplied.formatted_address,
selectedFromSuggestions: true,
mothershipOptionId: dc.option_id,
mothershipDisplayLabel: dc.display_label,
selectedFromMothership: true,
},
palletCount: PAIR.cargo.pallet_count,
weightLb: PAIR.cargo.weight_lb,
dimsIn: {
l: PAIR.cargo.length_in,
w: PAIR.cargo.width_in,
h: PAIR.cargo.height_in,
},
cargoType: "general_freight",
};
console.log(`[probe] 提货候选:${pc.display_label}`);
console.log(`[probe] 派送候选:${dc.display_label}`);
const opened = await openQuotePage(quotePageAdapter, false, undefined, {
freshCargoContext: !FAST,
});
const ctx = RPAContext.fromSession(opened.page, opened.context);
await stabilizeQuoteLandingPage(ctx);
await waitForQuoterWidgetHydrated(ctx.page, FAST ? 30_000 : 90_000);
console.log("[probe] ▶ runMothershipQuoteKernel填址→货物→Direct→Widget fallback");
const items = await runMothershipQuoteKernel(ctx, req);
const report = {
runId,
pair: PAIR.label,
finishedAt: new Date().toISOString(),
durationMs: Date.now() - startedAt,
tierCount: items.length,
tiers: items.map((q) => ({
tier: `${q.serviceLevel}/${q.rateOption}`,
label: formatWidgetQuoteTierLabel(q),
rawTotal: q.rawTotal,
carrier: q.carrier,
})),
verdict: items.length > 0 ? "PASS" : "FAIL",
};
const outDir = join(process.cwd(), ".rpa", "probe-widget-la-pomona");
mkdirSync(outDir, { recursive: true });
const reportPath = join(outDir, `probe-${runId}.json`);
writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
console.log("\n════════ Widget 路线结果 ════════");
console.log(`档位: ${items.length}`);
for (const q of items) {
console.log(` ${formatWidgetQuoteTierLabel(q)} = $${q.rawTotal}`);
}
console.log(`报告: ${reportPath}`);
console.log(`结论: ${report.verdict}`);
if (!shouldKeepBrowserOpen()) {
await closeBrowser();
} else {
console.log("[probe] 浏览器保持打开headed 调试),关闭窗口或 Ctrl+C 结束");
}
if (items.length === 0) {
process.exitCode = 1;
}
}
main().catch(async (error) => {
console.error("[probe:widget-la-pomona] 失败:", error);
if (!shouldKeepBrowserOpen()) {
await closeBrowser().catch(() => undefined);
} else {
console.log("[probe] 失败但浏览器保持打开,便于排查页面状态");
}
process.exit(1);
});