fix(web-probe): make observe control commands short-lived (#707)
Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
@@ -72,6 +72,7 @@ interface NodeWebProbeObserveOptions {
|
|||||||
viewport: string;
|
viewport: string;
|
||||||
sampleIntervalMs: number;
|
sampleIntervalMs: number;
|
||||||
screenshotIntervalMs: number;
|
screenshotIntervalMs: number;
|
||||||
|
observerRefreshIntervalMs: number;
|
||||||
maxSamples: number;
|
maxSamples: number;
|
||||||
commandTimeoutSeconds: number;
|
commandTimeoutSeconds: number;
|
||||||
waitMs: number;
|
waitMs: number;
|
||||||
@@ -6900,6 +6901,7 @@ function parseNodeWebProbeObserveOptions(
|
|||||||
"--viewport",
|
"--viewport",
|
||||||
"--sample-interval-ms",
|
"--sample-interval-ms",
|
||||||
"--screenshot-interval-ms",
|
"--screenshot-interval-ms",
|
||||||
|
"--observer-refresh-interval-ms",
|
||||||
"--max-samples",
|
"--max-samples",
|
||||||
"--command-timeout-seconds",
|
"--command-timeout-seconds",
|
||||||
"--wait-ms",
|
"--wait-ms",
|
||||||
@@ -6940,9 +6942,10 @@ function parseNodeWebProbeObserveOptions(
|
|||||||
viewport: optionValue(args, "--viewport") ?? "1440x900",
|
viewport: optionValue(args, "--viewport") ?? "1440x900",
|
||||||
sampleIntervalMs: positiveIntegerOption(args, "--sample-interval-ms", 5000, 600000),
|
sampleIntervalMs: positiveIntegerOption(args, "--sample-interval-ms", 5000, 600000),
|
||||||
screenshotIntervalMs: positiveIntegerOption(args, "--screenshot-interval-ms", 300000, 86_400_000),
|
screenshotIntervalMs: positiveIntegerOption(args, "--screenshot-interval-ms", 300000, 86_400_000),
|
||||||
|
observerRefreshIntervalMs: positiveIntegerOption(args, "--observer-refresh-interval-ms", 180000, 86_400_000),
|
||||||
maxSamples: positiveIntegerOption(args, "--max-samples", 0, 10_000_000),
|
maxSamples: positiveIntegerOption(args, "--max-samples", 0, 10_000_000),
|
||||||
commandTimeoutSeconds: positiveIntegerOption(args, "--command-timeout-seconds", 55, 3600),
|
commandTimeoutSeconds: positiveIntegerOption(args, "--command-timeout-seconds", 55, 3600),
|
||||||
waitMs: positiveIntegerOption(args, "--wait-ms", observeActionRaw === "command" || observeActionRaw === "stop" ? 30000 : 0, 600000),
|
waitMs: positiveIntegerOption(args, "--wait-ms", 0, 600000),
|
||||||
tailLines: positiveIntegerOption(args, "--tail-lines", 5, 200),
|
tailLines: positiveIntegerOption(args, "--tail-lines", 5, 200),
|
||||||
maxFiles: positiveIntegerOption(args, "--max-files", 80, 5000),
|
maxFiles: positiveIntegerOption(args, "--max-files", 80, 5000),
|
||||||
collectFile,
|
collectFile,
|
||||||
@@ -7405,6 +7408,20 @@ function runNodeWebProbeObserveStart(
|
|||||||
const stateDir = options.stateDir ?? defaultStateDir;
|
const stateDir = options.stateDir ?? defaultStateDir;
|
||||||
const runnerB64 = Buffer.from(nodeWebObserveRunnerSource(), "utf8").toString("base64");
|
const runnerB64 = Buffer.from(nodeWebObserveRunnerSource(), "utf8").toString("base64");
|
||||||
const webProbeProxy = nodeWebProbeHostProxyEnv(spec);
|
const webProbeProxy = nodeWebProbeHostProxyEnv(spec);
|
||||||
|
const runnerEnvAssignments = [
|
||||||
|
...webProbeProxy.envAssignments,
|
||||||
|
`HWLAB_WEB_BASE_URL=${shellQuote(options.url)}`,
|
||||||
|
`HWLAB_WEB_USER=${shellQuote(secretSpec.bootstrapAdminUsername)}`,
|
||||||
|
`HWLAB_WEB_PASS=${shellQuote(material.password)}`,
|
||||||
|
`UNIDESK_WEB_OBSERVE_STATE_DIR=${shellQuote(stateDir)}`,
|
||||||
|
`UNIDESK_WEB_OBSERVE_JOB_ID=${shellQuote(jobId)}`,
|
||||||
|
`UNIDESK_WEB_OBSERVE_TARGET_PATH=${shellQuote(options.targetPath)}`,
|
||||||
|
`UNIDESK_WEB_OBSERVE_SAMPLE_INTERVAL_MS=${shellQuote(String(options.sampleIntervalMs))}`,
|
||||||
|
`UNIDESK_WEB_OBSERVE_SCREENSHOT_INTERVAL_MS=${shellQuote(String(options.screenshotIntervalMs))}`,
|
||||||
|
`UNIDESK_WEB_OBSERVE_OBSERVER_REFRESH_INTERVAL_MS=${shellQuote(String(options.observerRefreshIntervalMs))}`,
|
||||||
|
`UNIDESK_WEB_OBSERVE_MAX_SAMPLES=${shellQuote(String(options.maxSamples))}`,
|
||||||
|
`UNIDESK_WEB_OBSERVE_VIEWPORT=${shellQuote(options.viewport)}`,
|
||||||
|
].join(" ");
|
||||||
const script = [
|
const script = [
|
||||||
"set -eu",
|
"set -eu",
|
||||||
`state_dir=${shellQuote(stateDir)}`,
|
`state_dir=${shellQuote(stateDir)}`,
|
||||||
@@ -7413,20 +7430,7 @@ function runNodeWebProbeObserveStart(
|
|||||||
"runner=\"$state_dir/observer-runner.mjs\"",
|
"runner=\"$state_dir/observer-runner.mjs\"",
|
||||||
`node -e "require('fs').writeFileSync(process.argv[1], Buffer.from(process.argv[2], 'base64'))" "$runner" ${shellQuote(runnerB64)}`,
|
`node -e "require('fs').writeFileSync(process.argv[1], Buffer.from(process.argv[2], 'base64'))" "$runner" ${shellQuote(runnerB64)}`,
|
||||||
"chmod 700 \"$runner\"",
|
"chmod 700 \"$runner\"",
|
||||||
[
|
`if command -v setsid >/dev/null 2>&1; then setsid env ${runnerEnvAssignments} node "$runner" >"$state_dir/stdout.log" 2>"$state_dir/stderr.log" </dev/null & else nohup env ${runnerEnvAssignments} node "$runner" >"$state_dir/stdout.log" 2>"$state_dir/stderr.log" </dev/null & fi`,
|
||||||
...webProbeProxy.envAssignments,
|
|
||||||
`HWLAB_WEB_BASE_URL=${shellQuote(options.url)}`,
|
|
||||||
`HWLAB_WEB_USER=${shellQuote(secretSpec.bootstrapAdminUsername)}`,
|
|
||||||
`HWLAB_WEB_PASS=${shellQuote(material.password)}`,
|
|
||||||
`UNIDESK_WEB_OBSERVE_STATE_DIR=${shellQuote(stateDir)}`,
|
|
||||||
`UNIDESK_WEB_OBSERVE_JOB_ID=${shellQuote(jobId)}`,
|
|
||||||
`UNIDESK_WEB_OBSERVE_TARGET_PATH=${shellQuote(options.targetPath)}`,
|
|
||||||
`UNIDESK_WEB_OBSERVE_SAMPLE_INTERVAL_MS=${shellQuote(String(options.sampleIntervalMs))}`,
|
|
||||||
`UNIDESK_WEB_OBSERVE_SCREENSHOT_INTERVAL_MS=${shellQuote(String(options.screenshotIntervalMs))}`,
|
|
||||||
`UNIDESK_WEB_OBSERVE_MAX_SAMPLES=${shellQuote(String(options.maxSamples))}`,
|
|
||||||
`UNIDESK_WEB_OBSERVE_VIEWPORT=${shellQuote(options.viewport)}`,
|
|
||||||
"nohup node \"$runner\" >\"$state_dir/stdout.log\" 2>\"$state_dir/stderr.log\" </dev/null &",
|
|
||||||
].join(" "),
|
|
||||||
"pid=$!",
|
"pid=$!",
|
||||||
"printf '%s\\n' \"$pid\" >\"$state_dir/pid\"",
|
"printf '%s\\n' \"$pid\" >\"$state_dir/pid\"",
|
||||||
"sleep 1",
|
"sleep 1",
|
||||||
@@ -7527,6 +7531,12 @@ function renderWebObserveStatusTable(value: Record<string, unknown>): string {
|
|||||||
const result = record(value.result);
|
const result = record(value.result);
|
||||||
const samples = Array.isArray(tails?.samples) ? tails.samples.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(-5) : [];
|
const samples = Array.isArray(tails?.samples) ? tails.samples.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(-5) : [];
|
||||||
const controls = Array.isArray(tails?.control) ? tails.control.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(-5) : [];
|
const controls = Array.isArray(tails?.control) ? tails.control.map(record).filter((item): item is Record<string, unknown> => item !== null).slice(-5) : [];
|
||||||
|
const completedControlIds = new Set(controls
|
||||||
|
.filter((item) => item.phase === "completed" || item.phase === "failed")
|
||||||
|
.map((item) => webObserveText(item.commandId))
|
||||||
|
.filter((item) => item !== "-"));
|
||||||
|
const activeControl = controls.slice().reverse().find((item) => item.phase === "started" && !completedControlIds.has(webObserveText(item.commandId))) ?? null;
|
||||||
|
const activeControlAgeSeconds = activeControl === null ? null : Math.max(0, Math.round((Date.now() - Date.parse(webObserveText(activeControl.ts))) / 1000));
|
||||||
const network = Array.isArray(tails?.network)
|
const network = Array.isArray(tails?.network)
|
||||||
? tails.network.map(record).filter((item): item is Record<string, unknown> => item !== null).filter((item) => {
|
? tails.network.map(record).filter((item): item is Record<string, unknown> => item !== null).filter((item) => {
|
||||||
const status = typeof item.status === "number" ? item.status : null;
|
const status = typeof item.status === "number" ? item.status : null;
|
||||||
@@ -7575,6 +7585,17 @@ function renderWebObserveStatusTable(value: Record<string, unknown>): string {
|
|||||||
]]),
|
]]),
|
||||||
"",
|
"",
|
||||||
] : []),
|
] : []),
|
||||||
|
...(activeControl !== null ? [
|
||||||
|
"Active command:",
|
||||||
|
webObserveTable(["TYPE", "COMMAND", "AGE_S", "STARTED_AT", "DETAIL"], [[
|
||||||
|
webObserveText(activeControl.type),
|
||||||
|
webObserveShort(webObserveText(activeControl.commandId), 28),
|
||||||
|
webObserveText(activeControlAgeSeconds),
|
||||||
|
webObserveShort(webObserveText(activeControl.ts), 24),
|
||||||
|
webObserveShort(webObserveText(activeControl.detail), 80),
|
||||||
|
]]),
|
||||||
|
"",
|
||||||
|
] : []),
|
||||||
"Recent samples:",
|
"Recent samples:",
|
||||||
webObserveTable(["SEQ", "TS", "PATH", "ROUTE_SESSION", "ACTIVE_SESSION", "MSG", "TRACE"], samples.length > 0 ? samples.map((sample) => [
|
webObserveTable(["SEQ", "TS", "PATH", "ROUTE_SESSION", "ACTIVE_SESSION", "MSG", "TRACE"], samples.length > 0 ? samples.map((sample) => [
|
||||||
webObserveText(sample.seq),
|
webObserveText(sample.seq),
|
||||||
|
|||||||
Reference in New Issue
Block a user