feat(web-probe): add semantic YAML origins
This commit is contained in:
@@ -37,6 +37,7 @@ import { transPath } from "./runtime-common";
|
||||
import { assertLane, assertNodeId, compactCommandResult, compactCommandResultRedacted, compactCommandResultWithStdoutTail, nullableRecord, optionValue, parseJsonObject, positiveIntegerOption, record, requiredOption, shellQuote } from "./utils";
|
||||
import { runNodeWebProbeObserveCommand, runNodeWebProbeObserveGc, runNodeWebProbeObserveStart, runNodeWebProbeObserveStatus } from "./web-probe-observe-actions";
|
||||
import { runNodeWebProbeObserveCollect } from "./web-probe-observe-collect";
|
||||
import { parseWebProbeOriginName, resolveNodeWebProbeCliOrigin, resolveNodeWebProbeOrigin, webProbeOriginSummary } from "./web-probe-origin";
|
||||
import { nodeWebObserveResolveStateDirShell, recoverWebObserveAnalyzeTurnDetails, renderWebObserveStartResult, renderWebProbeRunResult, upsertWebObserveIndexEntry, webObserveCommandLabel, webObserveIdFromOptions, webObserveIdFromStatus, webObserveIndexEntryFromOptions, webObserveNextCommands, withWebObserveAnalyzeRendered, withWebObserveShortcuts } from "./web-observe-render";
|
||||
import { commandSummaryForOutput, isSafeWebObserveArchivePrefix, isSafeWebObserveCollectFile, isSafeWebObserveFindingId, isSafeWebObserveJobId, isSafeWebObserveStateDir, isSafeWebObserveTraceId, nodeWebObserveForceStopNodeScript, nodeWebObserveStatusNodeScript, nodeWebObserveWaitCommandShell, runNodeWebProbeScript, safeWebObserveSegment, safeWebObserveTargetSegment } from "./web-observe-scripts";
|
||||
import { displayRepoPath, readBootstrapAdminPasswordMaterial, sleepSync } from "./web-probe";
|
||||
@@ -387,6 +388,7 @@ export function parseNodeWebProbeObserveOptions(
|
||||
assertKnownOptions(args.slice(1), new Set([
|
||||
"--node",
|
||||
"--lane",
|
||||
"--origin",
|
||||
"--url",
|
||||
"--target-path",
|
||||
"--viewport",
|
||||
@@ -491,6 +493,25 @@ export function parseNodeWebProbeObserveOptions(
|
||||
if (observeActionRaw !== "start" && observeActionRaw !== "gc" && stateDir === null && jobId === null) {
|
||||
throw new Error("web-probe observe status|command|stop|collect|analyze requires --state-dir or --job-id");
|
||||
}
|
||||
const requestedOrigin = optionValue(args, "--origin");
|
||||
const customUrl = optionValue(args, "--url");
|
||||
const browserProxyModeRaw = optionValue(args, "--browser-proxy-mode");
|
||||
if (observeActionRaw !== "start" && (requestedOrigin !== undefined || customUrl !== undefined || browserProxyModeRaw !== undefined)) {
|
||||
throw new Error("web-probe observe --origin/--url/--browser-proxy-mode is only valid for start; later actions inherit the observer origin");
|
||||
}
|
||||
const requestedBrowserProxyMode = browserProxyModeRaw === undefined ? undefined : parseWebProbeBrowserProxyMode(browserProxyModeRaw);
|
||||
const selectedOrigin = observeActionRaw === "start"
|
||||
? resolveNodeWebProbeCliOrigin(spec, requestedOrigin, customUrl, requestedBrowserProxyMode)
|
||||
: {
|
||||
originName: indexed?.originName ?? "custom" as const,
|
||||
originMode: indexed?.originMode ?? "custom-url" as const,
|
||||
originConfigPath: indexed?.originConfigPath ?? null,
|
||||
url: indexed?.url || spec.publicWebUrl,
|
||||
browserProxyMode: requestedBrowserProxyMode ?? indexed?.browserProxyMode ?? spec.webProbe?.browserProxyMode ?? "auto" as const,
|
||||
browserProxyModeSource: requestedBrowserProxyMode === undefined
|
||||
? indexed?.browserProxyModeSource ?? (spec.webProbe?.browserProxyMode === undefined ? "default" as const : "yaml-web-probe" as const)
|
||||
: "cli" as const,
|
||||
};
|
||||
const confirm = args.includes("--confirm");
|
||||
const dryRun = args.includes("--dry-run") || !confirm;
|
||||
if (confirm && args.includes("--dry-run")) throw new Error("web-probe observe gc accepts only one of --confirm or --dry-run");
|
||||
@@ -585,10 +606,14 @@ export function parseNodeWebProbeObserveOptions(
|
||||
id: observeId ?? jobId,
|
||||
node,
|
||||
lane,
|
||||
url: optionValue(args, "--url") ?? (observeActionRaw === "start" ? nodeWebProbeDefaultUrl(spec) : indexed?.url ?? spec.publicWebUrl),
|
||||
url: selectedOrigin.url,
|
||||
originName: selectedOrigin.originName,
|
||||
originMode: selectedOrigin.originMode,
|
||||
originConfigPath: selectedOrigin.originConfigPath,
|
||||
targetPath: optionValue(args, "--target-path") ?? "/workbench",
|
||||
viewport: optionValue(args, "--viewport") ?? "1440x900",
|
||||
browserProxyMode: parseWebProbeBrowserProxyMode(optionValue(args, "--browser-proxy-mode") ?? spec.webProbe?.browserProxyMode),
|
||||
browserProxyMode: selectedOrigin.browserProxyMode,
|
||||
browserProxyModeSource: selectedOrigin.browserProxyModeSource,
|
||||
sampleIntervalMs: positiveIntegerOption(args, "--sample-interval-ms", 5000, 600000),
|
||||
screenshotIntervalMs: positiveIntegerOption(args, "--screenshot-interval-ms", 300000, 86_400_000),
|
||||
observerRefreshIntervalMs: positiveIntegerOption(args, "--observer-refresh-interval-ms", 180000, 86_400_000),
|
||||
@@ -715,10 +740,8 @@ export function parseWebProbeBrowserProxyMode(value: string | undefined): WebPro
|
||||
}
|
||||
|
||||
export function nodeWebProbeDefaultUrl(spec: HwlabRuntimeLaneSpec): string {
|
||||
const origin = spec.webProbe?.defaultOrigin;
|
||||
if (origin === undefined || origin.mode === "public") return origin?.baseUrl ?? spec.publicWebUrl;
|
||||
const clusterIp = resolveKubernetesServiceClusterIp(spec, origin.namespace, origin.serviceName, new Map());
|
||||
return `${origin.scheme}://${clusterIp}:${origin.port}`;
|
||||
const origin = parseWebProbeOriginName(spec.webProbe?.defaultOrigin ?? "public");
|
||||
return resolveNodeWebProbeOrigin(spec, origin).url;
|
||||
}
|
||||
|
||||
export function nodeWebProbeAutoCommandTimeoutSeconds(input: {
|
||||
@@ -790,6 +813,7 @@ export function runNodeWebProbe(options: NodeWebProbeOptions): Record<string, un
|
||||
lane: options.lane,
|
||||
workspace: spec.workspace,
|
||||
url: options.url,
|
||||
origin: webProbeOriginSummary(options),
|
||||
degradedReason: "web_login_secret_missing",
|
||||
credential,
|
||||
next: { secretStatus: `bun scripts/cli.ts hwlab nodes secret status --node ${options.node} --lane ${options.lane} --name ${secretSpec.bootstrapAdminSecret}` },
|
||||
@@ -799,7 +823,7 @@ export function runNodeWebProbe(options: NodeWebProbeOptions): Record<string, un
|
||||
if (options.action === "script") return runNodeWebProbeScript(options, spec, secretSpec, material, credential);
|
||||
if (options.commandTimeoutSeconds > 55) return runNodeWebProbeAsync(options, spec, secretSpec, material, credential);
|
||||
const probeArgs = nodeWebProbeRunArgs(options, "run");
|
||||
const webProbeProxy = nodeWebProbeHostProxyEnv(spec);
|
||||
const webProbeProxy = nodeWebProbeHostProxyEnv(spec, options.browserProxyMode);
|
||||
const script = [
|
||||
"set -eu",
|
||||
`${[...webProbeProxy.envAssignments, `HWLAB_WEB_USER=${shellQuote(material.username ?? secretSpec.bootstrapAdminUsername)}`, `HWLAB_WEB_PASS=${shellQuote(material.password)}`].join(" ")} ${probeArgs.map(shellQuote).join(" ")}`,
|
||||
@@ -821,6 +845,7 @@ export function runNodeWebProbe(options: NodeWebProbeOptions): Record<string, un
|
||||
lane: options.lane,
|
||||
workspace: spec.workspace,
|
||||
url: options.url,
|
||||
origin: webProbeOriginSummary(options),
|
||||
network: webProbeProxy.summary,
|
||||
credential,
|
||||
commandTimeout: {
|
||||
@@ -884,6 +909,7 @@ export function runNodeWebProbeScreenshot(options: NodeWebProbeScreenshotOptions
|
||||
workspace: spec.workspace,
|
||||
route,
|
||||
url: options.url,
|
||||
origin: webProbeOriginSummary(options),
|
||||
viewport: options.viewport,
|
||||
localDir: options.localDir,
|
||||
screenshot: compactScreenshot,
|
||||
@@ -1139,7 +1165,7 @@ export function runNodeWebProbeAsync(
|
||||
credential: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
const startArgs = nodeWebProbeRunArgs(options, "start");
|
||||
const webProbeProxy = nodeWebProbeHostProxyEnv(spec);
|
||||
const webProbeProxy = nodeWebProbeHostProxyEnv(spec, options.browserProxyMode);
|
||||
const startScript = [
|
||||
"set -eu",
|
||||
`${[...webProbeProxy.envAssignments, `HWLAB_WEB_USER=${shellQuote(material.username ?? secretSpec.bootstrapAdminUsername)}`, `HWLAB_WEB_PASS=${shellQuote(material.password ?? "")}`].join(" ")} ${startArgs.map(shellQuote).join(" ")}`,
|
||||
@@ -1156,6 +1182,7 @@ export function runNodeWebProbeAsync(
|
||||
lane: options.lane,
|
||||
workspace: spec.workspace,
|
||||
url: options.url,
|
||||
origin: webProbeOriginSummary(options),
|
||||
network: webProbeProxy.summary,
|
||||
credential,
|
||||
mode: "async-start",
|
||||
@@ -1194,6 +1221,7 @@ export function runNodeWebProbeAsync(
|
||||
lane: options.lane,
|
||||
workspace: spec.workspace,
|
||||
url: options.url,
|
||||
origin: webProbeOriginSummary(options),
|
||||
network: webProbeProxy.summary,
|
||||
credential,
|
||||
mode: "async",
|
||||
|
||||
Reference in New Issue
Block a user