feat(web-probe): add semantic YAML origins

This commit is contained in:
Codex
2026-07-10 12:37:13 +02:00
parent 2d4c1a5ffa
commit 2c47fd6528
18 changed files with 545 additions and 48 deletions
+44 -11
View File
@@ -36,7 +36,8 @@ import { nodeRuntimePipelineFailureSummary, nodeRuntimePipelineRunDiagnostics }
import { compactRuntimeCommand, runNodeHostScript, runNodeHostScriptAsync } from "./runtime-common";
import { assertLane, assertNodeId, keyValueLinesFromText, numericField, optionValue, optionalStringValue, positiveIntegerOption, positiveIntegerValue, record, requiredOption, shellQuote, statusText, stringValue, stripOptions } from "./utils";
import { discoverWebObserveIndexEntry, readWebObserveIndexEntry } from "./web-observe-render";
import { assertKnownOptions, nodeWebProbeAutoCommandTimeoutSeconds, nodeWebProbeDefaultUrl, normalizeNodeWebProbeObserveArgs, parseNodeWebProbeObserveOptions, parseNodeWebProbeSentinelOptions, parseWebProbeBrowserProxyMode } from "./web-probe-observe";
import { assertKnownOptions, nodeWebProbeAutoCommandTimeoutSeconds, normalizeNodeWebProbeObserveArgs, parseNodeWebProbeObserveOptions, parseNodeWebProbeSentinelOptions, parseWebProbeBrowserProxyMode } from "./web-probe-observe";
import { resolveNodeWebProbeCliOrigin } from "./web-probe-origin";
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";
import { resolveEgressProxySourceRef } from "../egress-proxy-sources";
@@ -2023,7 +2024,7 @@ export function rewriteDelegatedNodeString(value: string, scoped: ReturnType<typ
export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
const [actionRaw] = args;
if (actionRaw !== "run" && actionRaw !== "script" && actionRaw !== "screenshot" && actionRaw !== "observe" && actionRaw !== "sentinel" && actionRaw !== "opencode-smoke") throw new Error("web-probe usage: run|script|screenshot|observe|sentinel|opencode-smoke --node NODE --lane vNN [--url URL]");
if (actionRaw !== "run" && actionRaw !== "script" && actionRaw !== "screenshot" && actionRaw !== "observe" && actionRaw !== "sentinel" && actionRaw !== "opencode-smoke") throw new Error("web-probe usage: run|script|screenshot|observe|sentinel|opencode-smoke --node NODE --lane vNN [--origin internal|public]");
if (actionRaw === "sentinel") return parseNodeWebProbeSentinelOptions(args.slice(1));
if (actionRaw === "observe") {
const normalized = normalizeNodeWebProbeObserveArgs(args.slice(1));
@@ -2053,13 +2054,20 @@ 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);
const resolveOrigin = () => {
const browserProxyModeRaw = optionValue(args, "--browser-proxy-mode");
const browserProxyMode = browserProxyModeRaw === undefined ? undefined : parseWebProbeBrowserProxyMode(browserProxyModeRaw);
return resolveNodeWebProbeCliOrigin(spec, optionValue(args, "--origin"), optionValue(args, "--url"), browserProxyMode);
};
if (actionRaw === "screenshot") {
assertKnownOptions(args.slice(1), new Set([
"--node",
"--lane",
"--origin",
"--url",
"--path",
"--viewport",
"--browser-proxy-mode",
"--local-dir",
"--name",
"--timeout-ms",
@@ -2075,6 +2083,7 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
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 selectedOrigin = resolveOrigin();
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);
@@ -2082,7 +2091,12 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
action: "screenshot",
node,
lane,
url: url ?? resolveWebProbeScreenshotUrl(spec, targetPath ?? "/"),
url: url ?? resolveWebProbeScreenshotUrl(selectedOrigin.url, targetPath ?? "/"),
originName: selectedOrigin.originName,
originMode: selectedOrigin.originMode,
originConfigPath: selectedOrigin.originConfigPath,
browserProxyMode: selectedOrigin.browserProxyMode,
browserProxyModeSource: selectedOrigin.browserProxyModeSource,
path: targetPath,
viewport: parseWebProbeScreenshotViewport(optionValue(args, "--viewport") ?? "1440x900"),
localDir: optionValue(args, "--local-dir") ?? "/tmp",
@@ -2100,6 +2114,7 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
assertKnownOptions(args.slice(1), new Set([
"--node",
"--lane",
"--origin",
"--url",
"--timeout-ms",
"--viewport",
@@ -2115,14 +2130,19 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
if (scriptText.trim().length === 0) throw new Error("web-probe script received an empty script");
const timeoutMs = positiveIntegerOption(args, "--timeout-ms", 30000, 60000);
const commandTimeoutSeconds = positiveIntegerOption(args, "--command-timeout-seconds", Math.max(60, Math.ceil(timeoutMs / 1000) + 30), 3600);
const selectedOrigin = resolveOrigin();
return {
action: "script",
node,
lane,
url: optionValue(args, "--url") ?? nodeWebProbeDefaultUrl(spec),
url: selectedOrigin.url,
originName: selectedOrigin.originName,
originMode: selectedOrigin.originMode,
originConfigPath: selectedOrigin.originConfigPath,
timeoutMs,
viewport: optionValue(args, "--viewport") ?? "1440x900",
browserProxyMode: parseWebProbeBrowserProxyMode(optionValue(args, "--browser-proxy-mode") ?? spec.webProbe?.browserProxyMode),
browserProxyMode: selectedOrigin.browserProxyMode,
browserProxyModeSource: selectedOrigin.browserProxyModeSource,
commandTimeoutSeconds,
scriptText,
scriptSource: {
@@ -2137,6 +2157,7 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
assertKnownOptions(args.slice(1), new Set([
"--node",
"--lane",
"--origin",
"--url",
"--path",
"--message",
@@ -2163,15 +2184,20 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
expectText,
responseTimeoutMs,
});
const commandLabel = `web-probe opencode-smoke --node ${node} --lane ${lane}`;
const selectedOrigin = resolveOrigin();
const commandLabel = `web-probe opencode-smoke --node ${node} --lane ${lane}${selectedOrigin.originName === "custom" ? "" : ` --origin ${selectedOrigin.originName}`}`;
return {
action: "script",
node,
lane,
url: optionValue(args, "--url") ?? nodeWebProbeDefaultUrl(spec),
url: selectedOrigin.url,
originName: selectedOrigin.originName,
originMode: selectedOrigin.originMode,
originConfigPath: selectedOrigin.originConfigPath,
timeoutMs,
viewport: optionValue(args, "--viewport") ?? "1440x900",
browserProxyMode: parseWebProbeBrowserProxyMode(optionValue(args, "--browser-proxy-mode") ?? spec.webProbe?.browserProxyMode),
browserProxyMode: selectedOrigin.browserProxyMode,
browserProxyModeSource: selectedOrigin.browserProxyModeSource,
commandTimeoutSeconds,
scriptText,
commandLabel,
@@ -2194,7 +2220,9 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
assertKnownOptions(args.slice(1), new Set([
"--node",
"--lane",
"--origin",
"--url",
"--browser-proxy-mode",
"--timeout-ms",
"--wait-after-submit-ms",
"--wait-messages-ms",
@@ -2233,11 +2261,17 @@ export function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
const commandTimeoutSeconds = commandTimeoutUserProvided
? Math.max(positiveIntegerOption(args, "--command-timeout-seconds", commandTimeoutAutoSeconds, 3600), commandTimeoutAutoSeconds)
: commandTimeoutAutoSeconds;
const selectedOrigin = resolveOrigin();
return {
action: "run",
node,
lane,
url: optionValue(args, "--url") ?? nodeWebProbeDefaultUrl(spec),
url: selectedOrigin.url,
originName: selectedOrigin.originName,
originMode: selectedOrigin.originMode,
originConfigPath: selectedOrigin.originConfigPath,
browserProxyMode: selectedOrigin.browserProxyMode,
browserProxyModeSource: selectedOrigin.browserProxyModeSource,
timeoutMs,
waitAfterSubmitMs,
waitMessagesMs,
@@ -2433,8 +2467,7 @@ function normalizeButtonText(value) {
return patched;
}
function resolveWebProbeScreenshotUrl(spec: HwlabRuntimeLaneSpec, targetPath: string): string {
const origin = nodeWebProbeDefaultUrl(spec);
function resolveWebProbeScreenshotUrl(origin: string, targetPath: string): string {
try {
return new URL(targetPath || "/", origin).toString();
} catch {