Files
pikasTech-HWLAB/tools/capture-issue-803-noise.mjs
T
2026-06-04 14:51:54 +08:00

133 lines
6.1 KiB
JavaScript

#!/usr/bin/env node
// HWLAB #803 noise-capture CLI.
//
// Captures the rendered Agent workspace (#code-agent-summary, per-message
// meta/pending/runtime/session/trace cards) from a live or local Cloud Web
// build and prints a structured JSON dump that can be diffed before/after a
// Web UI refactor.
//
// Usage:
// CHROMIUM_PATH=/usr/bin/chromium-browser \
// node tools/capture-issue-803-noise.mjs \
// --url http://74.48.78.17:17666/ --user admin --pass hwlab2026 \
// --png /tmp/hwlab-noise.png
// CHROMIUM_PATH=/usr/bin/chromium-browser \
// NOISE_USER=admin NOISE_PASS=hwlab2026 \
// node tools/capture-issue-803-noise.mjs
import { setTimeout as sleep } from "node:timers/promises";
import { importPlaywright, launchChromium } from "../scripts/src/browser-launcher.mjs";
function parseArgs(argv) {
const args = {
url: process.env.NOISE_URL ?? "http://74.48.78.17:17666/",
user: process.env.NOISE_USER ?? "admin",
pass: process.env.NOISE_PASS ?? "hwlab2026",
message: process.env.NOISE_MESSAGE ?? null,
viewport: process.env.NOISE_VIEWPORT ?? "1440x900",
png: process.env.NOISE_PNG ?? "/tmp/hwlab-noise.png"
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === "--url") args.url = argv[++index];
else if (arg === "--user") args.user = argv[++index];
else if (arg === "--pass") args.pass = argv[++index];
else if (arg === "--message") args.message = argv[++index];
else if (arg === "--png") args.png = argv[++index];
else if (arg === "--viewport") args.viewport = argv[++index];
}
const [w, h] = args.viewport.split("x").map(Number);
args.viewportWidth = Number.isFinite(w) ? w : 1440;
args.viewportHeight = Number.isFinite(h) ? h : 900;
return args;
}
const args = parseArgs(process.argv.slice(2));
const { chromium } = await importPlaywright();
const browser = await launchChromium(chromium);
console.error(`[noise] browser ${JSON.stringify(browser.hwlabLaunchPlan ?? {})}`);
const ctx = await browser.newContext({ viewport: { width: args.viewportWidth, height: args.viewportHeight } });
const page = await ctx.newPage();
await page.goto(args.url, { waitUntil: "networkidle", timeout: 30000 });
try {
await page.locator("#login-username").fill(args.user, { timeout: 5000 });
await page.locator("#login-password").fill(args.pass);
await page.locator("#login-submit").click();
await page.locator("#command-input").waitFor({ state: "visible", timeout: 12000 });
} catch (error) {
console.error("[noise] login failed:", error instanceof Error ? error.message : String(error));
}
await page.waitForTimeout(1500);
if (args.message) {
try {
await page.locator("#command-input").fill(args.message, { timeout: 5000 });
await page.locator("#command-send").click();
await sleep(8000);
} catch (error) {
console.error("[noise] submit failed:", error instanceof Error ? error.message : String(error));
}
}
const data = await page.evaluate(() => {
const summary = document.querySelector("#code-agent-summary");
const summaryRect = summary?.getBoundingClientRect();
const summaryText = summary?.textContent?.replace(/\s+/gu, " ").trim().slice(0, 800) ?? null;
const summaryDetail = document.querySelector("#code-agent-summary-detail");
const summaryDetailRect = summaryDetail?.getBoundingClientRect();
const summaryDetailText = summaryDetail?.textContent?.replace(/\s+/gu, " ").trim().slice(0, 1500) ?? null;
const summaryIsOpen = summary ? summary.open === true : null;
const summaryDebugBtn = document.querySelector("#code-agent-debug-trigger");
const convList = document.querySelector("#conversation-list");
const convListRect = convList?.getBoundingClientRect();
const messages = [...document.querySelectorAll(".message-card")].map((card) => {
const rect = card.getBoundingClientRect();
const role = card.getAttribute("data-message-role")
?? [...card.classList].find((cls) => cls.startsWith("message-"))?.replace(/^message-/, "")
?? null;
const meta = card.querySelector(".message-meta")?.textContent?.replace(/\s+/gu, " ").trim() ?? null;
const pending = card.querySelector(".message-pending-context")?.textContent?.replace(/\s+/gu, " ").trim().slice(0, 600) ?? null;
const session = card.querySelector(".message-session-context")?.textContent?.replace(/\s+/gu, " ").trim().slice(0, 600) ?? null;
const runtime = card.querySelector(".message-runtime-path")?.textContent?.replace(/\s+/gu, " ").trim().slice(0, 600) ?? null;
const trace = card.querySelector(".message-trace")?.textContent?.replace(/\s+/gu, " ").trim().slice(0, 400) ?? null;
const traceEventsRect = card.querySelector(".message-trace-events")?.getBoundingClientRect();
const debugTrigger = card.querySelector(".debug-dialog-trigger");
const cardWidth = rect.width;
const tracePct = cardWidth > 0 && traceEventsRect ? Math.round((traceEventsRect.width / cardWidth) * 100) : null;
return {
role,
meta,
pending,
session,
runtime,
trace,
debugTriggerText: debugTrigger?.textContent ?? null,
cardWidth: Math.round(cardWidth),
traceEventsWidth: traceEventsRect ? Math.round(traceEventsRect.width) : null,
traceEventsPct: tracePct
};
});
const systemCount = messages.filter((m) => m.role === "system").length;
const roleCounts = messages.reduce((acc, m) => {
const key = m.role ?? "unknown";
acc[key] = (acc[key] ?? 0) + 1;
return acc;
}, {});
return {
summaryText,
summaryDetailText,
summaryIsOpen,
summaryDebugBtnExists: !!summaryDebugBtn,
summaryRect: summaryRect ? { width: Math.round(summaryRect.width), height: Math.round(summaryRect.height) } : null,
summaryDetailRect: summaryDetailRect ? { width: Math.round(summaryDetailRect.width), height: Math.round(summaryDetailRect.height) } : null,
convListRect: convListRect ? { width: Math.round(convListRect.width), height: Math.round(convListRect.height) } : null,
messageCount: messages.length,
systemMessageCount: systemCount,
roleCounts,
messages
};
});
console.log(JSON.stringify(data, null, 2));
await page.screenshot({ path: args.png, fullPage: false });
await browser.close();