fix: 补齐 WebProbe 浏览器启动门禁

This commit is contained in:
AgentRun Codex
2026-07-13 07:34:31 +00:00
parent c998485d4a
commit 4b39c9ce41
9 changed files with 322 additions and 20 deletions
@@ -20,6 +20,11 @@ const viewport = parseViewport(process.env.UNIDESK_WEB_PROBE_VIEWPORT || "1440x9
const browserProxyMode = parseBrowserProxyMode(process.env.UNIDESK_WEB_PROBE_BROWSER_PROXY_MODE || "auto");
const playwrightProxy = proxyConfigFromEnv(baseUrl);
const chromiumLaunchOptions = chromiumLaunchOptionsForProxy(playwrightProxy);
const screenshotMode = requiredEnum(process.env.UNIDESK_WEB_PROBE_SCREENSHOT_MODE, ["viewport"]);
const maxNetworkRequestEntries = requiredPositiveInteger(process.env.UNIDESK_WEB_PROBE_MAX_NETWORK_REQUEST_ENTRIES, "UNIDESK_WEB_PROBE_MAX_NETWORK_REQUEST_ENTRIES");
const maxNetworkRequestBytes = requiredPositiveInteger(process.env.UNIDESK_WEB_PROBE_MAX_NETWORK_REQUEST_BYTES, "UNIDESK_WEB_PROBE_MAX_NETWORK_REQUEST_BYTES");
const maxNetworkFailureEntries = requiredPositiveInteger(process.env.UNIDESK_WEB_PROBE_MAX_NETWORK_FAILURE_ENTRIES, "UNIDESK_WEB_PROBE_MAX_NETWORK_FAILURE_ENTRIES");
const maxNetworkFailureBytes = requiredPositiveInteger(process.env.UNIDESK_WEB_PROBE_MAX_NETWORK_FAILURE_BYTES, "UNIDESK_WEB_PROBE_MAX_NETWORK_FAILURE_BYTES");
const artifactRecords = [];
const readinessRecords = [];
const stepRecords = [];
@@ -1314,36 +1319,54 @@ function createNetworkTracker(targetPage, apiPattern) {
const started = Date.now();
const requests = [];
const failures = [];
let requestBytes = 0;
let requestDropped = 0;
let failureBytes = 0;
let failureDropped = 0;
const matches = createApiMatcher(apiPattern);
const appendBounded = (items, item, kind) => {
const bytes = Buffer.byteLength(JSON.stringify(item), "utf8");
const entryLimit = kind === "request" ? maxNetworkRequestEntries : maxNetworkFailureEntries;
const byteLimit = kind === "request" ? maxNetworkRequestBytes : maxNetworkFailureBytes;
const usedBytes = kind === "request" ? requestBytes : failureBytes;
if (items.length >= entryLimit || usedBytes + bytes > byteLimit) {
if (kind === "request") requestDropped += 1;
else failureDropped += 1;
return;
}
items.push(item);
if (kind === "request") requestBytes += bytes;
else failureBytes += bytes;
};
const onRequest = (request) => {
if (!matches(request.url())) return;
requests.push({
appendBounded(requests, {
method: request.method(),
path: urlPath(request.url()),
atMs: Date.now() - started,
});
}, "request");
};
const onResponse = (response) => {
if (!matches(response.url())) return;
if (response.status() >= 400) {
failures.push({
appendBounded(failures, {
type: "response",
status: response.status(),
statusText: response.statusText(),
path: urlPath(response.url()),
atMs: Date.now() - started,
});
}, "failure");
}
};
const onRequestFailed = (request) => {
if (!matches(request.url())) return;
failures.push({
appendBounded(failures, {
type: "requestfailed",
method: request.method(),
path: urlPath(request.url()),
failureText: request.failure()?.errorText ?? null,
atMs: Date.now() - started,
});
}, "failure");
};
targetPage.on("request", onRequest);
targetPage.on("response", onResponse);
@@ -1356,6 +1379,10 @@ function createNetworkTracker(targetPage, apiPattern) {
apiFailureCount: failures.length,
apiRequests: requests.slice(0, 5),
apiFailures: failures.slice(0, 5),
apiRequestEvidenceBytes: requestBytes,
apiRequestEvidenceDropped: requestDropped,
apiFailureEvidenceBytes: failureBytes,
apiFailureEvidenceDropped: failureDropped,
}),
dispose: () => {
targetPage.off("request", onRequest);
@@ -1476,7 +1503,7 @@ async function captureFailureScreenshot(name = "failure.png") {
async function screenshotPage(targetPage, name = "screenshot.png", options = {}) {
const file = artifactPath(name);
await targetPage.screenshot({ path: file, fullPage: true, ...options });
await targetPage.screenshot({ path: file, fullPage: screenshotMode !== "viewport", ...options });
return recordArtifact(file, "screenshot");
}
@@ -1910,6 +1937,17 @@ function positiveInteger(raw, fallback) {
return value;
}
function requiredPositiveInteger(raw, name) {
const value = Number(raw);
if (!Number.isInteger(value) || value <= 0) throw new Error("missing or invalid " + name);
return value;
}
function requiredEnum(raw, allowed) {
if (!allowed.includes(raw)) throw new Error("missing or invalid screenshot mode");
return raw;
}
function artifactPath(name) {
const safe = String(name || "artifact")
.replace(/[^A-Za-z0-9._-]/gu, "_")
@@ -1920,7 +1958,7 @@ function artifactPath(name) {
async function screenshot(name = "screenshot.png", options = {}) {
const file = artifactPath(name);
await page.screenshot({ path: file, fullPage: true, ...options });
await page.screenshot({ path: file, fullPage: screenshotMode !== "viewport", ...options });
return recordArtifact(file, "screenshot");
}