browse
Version:
Unified Browserbase CLI for browser automation and cloud APIs.
108 lines (107 loc) • 3.75 kB
JavaScript
// Deterministic reducer for `browse cloud sessions logs --only-errors`.
// Turns the raw CDP firehose (~hundreds of events per session) into the high-signal error slice:
// console errors/warnings/asserts, uncaught exceptions, HTTP 4xx/5xx responses, and net-level load
// failures. No LLM — pure allowlist + severity/status filter + field projection + dedupe + stack trim.
function paramsOf(e) {
try {
const parsed = JSON.parse(e.request?.rawBody ?? "{}");
return parsed.params ?? {};
}
catch {
return {};
}
}
// Keep the message line + the app (`/src/`) stack frames; drop framework/vendor frames and hosts.
function trimStack(s) {
return (s || "")
.split("\n")
.filter((l, i) => i === 0 || (/\/src\//.test(l) && !/node_modules|\.vite/.test(l)))
.slice(0, 4)
.map((l) => l
.replace(/https?:\/\/[^/)]+/g, "")
.replace(/\?[^):]*/, "")
.trim())
.join("\n");
}
export function reduceLogs(raw, opts = {}) {
const out = [];
const seen = new Set();
const push = (rec) => {
const k = JSON.stringify(rec);
if (!seen.has(k)) {
seen.add(k);
out.push(rec);
}
};
for (const e of raw) {
const p = paramsOf(e);
const m = e.method;
const responseStatus = p.response?.status;
let rec = null;
if (m === "Runtime.consoleAPICalled" &&
typeof p.type === "string" &&
["error", "warning", "assert"].includes(p.type)) {
const text = (Array.isArray(p.args) ? p.args : [])
.map((a) => a && typeof a === "object" ? a.description || a.value || "" : "")
.filter(Boolean)
.join(" ");
if (text && !/^%[os]/.test(text))
rec = {
kind: `console.${p.type}`,
domain: "Runtime",
severity: p.type,
text: trimStack(text),
};
}
else if (m === "Runtime.exceptionThrown") {
rec = {
kind: "exception",
domain: "Runtime",
severity: "error",
text: trimStack(p.exceptionDetails?.exception?.description ??
p.exceptionDetails?.text ??
""),
};
}
else if (m === "Log.entryAdded" &&
typeof p.entry?.level === "string" &&
["error", "warning"].includes(p.entry?.level)) {
rec = {
kind: `log.${p.entry.level}`,
domain: "Log",
severity: p.entry.level,
text: p.entry.text,
url: p.entry.url,
};
}
else if (m === "Network.responseReceived" &&
typeof responseStatus === "number" &&
responseStatus >= 400) {
rec = {
kind: "network",
domain: "Network",
status: responseStatus,
url: p.response?.url,
type: p.type,
};
}
else if (m === "Network.loadingFailed" &&
p.errorText !== "net::ERR_ABORTED") {
rec = {
kind: "network.failed",
domain: "Network",
error: p.errorText,
type: p.type,
};
}
else {
continue; // everything else (byte-chunk / lifecycle events) is noise
}
if (!rec)
continue; // e.g. a console.error whose text was empty / formatting noise
if (opts.failedRequests && rec.domain !== "Network")
continue;
push(rec);
}
return out;
}