fix: add web-probe screenshot and tighten sentinel dashboard
This commit is contained in:
@@ -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}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user