fix: add web-probe screenshot and tighten sentinel dashboard

This commit is contained in:
Codex
2026-06-26 11:06:09 +00:00
parent 3e09444488
commit 49ce6c41be
7 changed files with 526 additions and 30 deletions
+72 -1
View File
@@ -1312,7 +1312,7 @@ export function rewriteDelegatedNodeString(value: string, scoped: ReturnType<typ
export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
const [actionRaw] = args;
if (actionRaw !== "run" && actionRaw !== "script" && actionRaw !== "observe" && actionRaw !== "sentinel") throw new Error("web-probe usage: run|script|observe|sentinel --node NODE --lane vNN [--url URL]");
if (actionRaw !== "run" && actionRaw !== "script" && actionRaw !== "screenshot" && actionRaw !== "observe" && actionRaw !== "sentinel") throw new Error("web-probe usage: run|script|screenshot|observe|sentinel --node NODE --lane vNN [--url URL]");
if (actionRaw === "sentinel") return parseNodeWebProbeSentinelOptions(args.slice(1));
if (actionRaw === "observe") {
const normalized = normalizeNodeWebProbeObserveArgs(args.slice(1));
@@ -1342,6 +1342,49 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
assertLane(lane);
if (!isHwlabRuntimeLane(lane)) throw new Error(`web-probe only supports HWLAB runtime lanes, got ${lane}`);
const spec = hwlabRuntimeLaneSpecForNode(lane, node);
if (actionRaw === "screenshot") {
assertKnownOptions(args.slice(1), new Set([
"--node",
"--lane",
"--url",
"--path",
"--viewport",
"--local-dir",
"--name",
"--timeout-ms",
"--wait-until",
"--selector",
"--wait-timeout-ms",
"--command-timeout-seconds",
]), new Set([
"--full-page",
"--no-full-page",
"--keep-remote",
]));
const url = optionValue(args, "--url");
const targetPath = optionValue(args, "--path") ?? null;
if (url !== undefined && targetPath !== null) throw new Error("web-probe screenshot accepts --url or --path, not both");
const timeoutMs = positiveIntegerOption(args, "--timeout-ms", 30000, 120000);
const waitTimeoutMs = positiveIntegerOption(args, "--wait-timeout-ms", Math.max(90000, timeoutMs + 30000), 600000);
const commandTimeoutSeconds = positiveIntegerOption(args, "--command-timeout-seconds", Math.ceil(waitTimeoutMs / 1000) + 45, 900);
return {
action: "screenshot",
node,
lane,
url: url ?? resolveWebProbeScreenshotUrl(spec, targetPath ?? "/"),
path: targetPath,
viewport: parseWebProbeScreenshotViewport(optionValue(args, "--viewport") ?? "1440x900"),
localDir: optionValue(args, "--local-dir") ?? "/tmp",
name: parseWebProbeScreenshotName(optionValue(args, "--name") ?? `web-probe-${node.toLowerCase()}-${lane}.png`),
timeoutMs,
waitUntil: parseWebProbeScreenshotWaitUntil(optionValue(args, "--wait-until") ?? "networkidle"),
fullPage: !args.includes("--no-full-page"),
selector: optionValue(args, "--selector") ?? null,
keepRemote: args.includes("--keep-remote"),
waitTimeoutMs,
commandTimeoutSeconds,
};
}
if (actionRaw === "script") {
assertKnownOptions(args.slice(1), new Set([
"--node",
@@ -1441,3 +1484,31 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
commandTimeoutUserProvided,
};
}
function resolveWebProbeScreenshotUrl(spec: HwlabRuntimeLaneSpec, targetPath: string): string {
const origin = nodeWebProbeDefaultUrl(spec);
try {
return new URL(targetPath || "/", origin).toString();
} catch {
throw new Error(`web-probe screenshot --path cannot be resolved against ${origin}: ${targetPath}`);
}
}
function parseWebProbeScreenshotViewport(value: string): string {
if (!/^[1-9][0-9]{1,4}x[1-9][0-9]{1,4}$/u.test(value)) throw new Error(`web-probe screenshot --viewport must look like 1440x900, got ${value}`);
const [widthRaw, heightRaw] = value.split("x");
const width = Number(widthRaw);
const height = Number(heightRaw);
if (width < 240 || width > 7680 || height < 240 || height > 4320) throw new Error(`web-probe screenshot --viewport out of range: ${value}`);
return value;
}
function parseWebProbeScreenshotName(value: string): string {
if (!/^[A-Za-z0-9._-]{1,120}$/u.test(value)) throw new Error("web-probe screenshot --name must contain only letters, numbers, dot, underscore, or dash, max 120 chars");
return value.endsWith(".png") ? value : `${value}.png`;
}
function parseWebProbeScreenshotWaitUntil(value: string): "load" | "domcontentloaded" | "networkidle" | "commit" {
if (value === "load" || value === "domcontentloaded" || value === "networkidle" || value === "commit") return value;
throw new Error(`web-probe screenshot --wait-until must be load, domcontentloaded, networkidle, or commit; got ${value}`);
}