fix: warn on ad hoc web-probe scripts
This commit is contained in:
@@ -63,6 +63,7 @@ export function hwlabNodeWebProbeHelp(): Record<string, unknown> {
|
||||
},
|
||||
notes: [
|
||||
"Default URL, browser proxy mode, observe/analyze thresholds, and project-management command allowlist come from config/hwlab-node-lanes.yaml webProbe.",
|
||||
"`web-probe script` is an ad-hoc exploration escape hatch; repeated/high-frequency workflows must become `web-probe observe command` types or repo-owned web-probe commands.",
|
||||
"observe is passive by default; user actions must be explicit observe command entries in control.jsonl.",
|
||||
"After observe start, prefer observe status|command|stop|collect|analyze <id> instead of repeating --node/--lane/--state-dir.",
|
||||
"collect views render bounded summaries from existing artifacts and do not create a second source of truth.",
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "bun:test";
|
||||
|
||||
import { renderWebProbeScriptResult } from "./web-observe-render";
|
||||
|
||||
test("web-probe script render warns to promote repeated scripts into commands", () => {
|
||||
const rendered = renderWebProbeScriptResult({
|
||||
ok: true,
|
||||
status: "pass",
|
||||
command: "web-probe script --node D601 --lane v03",
|
||||
node: "D601",
|
||||
lane: "v03",
|
||||
url: "https://hwlab.example.test",
|
||||
warnings: [{
|
||||
code: "web_probe_script_ad_hoc_only",
|
||||
severity: "warning",
|
||||
message: "web-probe script is for one-off exploration; repeated or high-frequency checks must be promoted to a typed command instead of rerunning temporary scripts.",
|
||||
}],
|
||||
hints: [
|
||||
"Prefer `web-probe observe start` plus `web-probe observe command` for interactive flows.",
|
||||
],
|
||||
preferredCommands: {
|
||||
startObserver: "bun scripts/cli.ts web-probe observe start --node D601 --lane v03 --target-path /projects/mdtodo",
|
||||
mdtodoSummary: "bun scripts/cli.ts web-probe observe collect <observerId> --view project-mdtodo-summary",
|
||||
},
|
||||
summary: { ok: true, status: "pass" },
|
||||
issueEvidence: {},
|
||||
probe: { steps: [], script: { result: { ok: true } } },
|
||||
reportLoad: { source: "stdout", path: ".state/web-probe-script/run.demo/web-probe-script-report.json" },
|
||||
result: { exitCode: 0, timedOut: false },
|
||||
});
|
||||
|
||||
const text = String(rendered.renderedText ?? "");
|
||||
assert.match(text, /WARNINGS/u);
|
||||
assert.match(text, /web_probe_script_ad_hoc_only/u);
|
||||
assert.match(text, /HINTS/u);
|
||||
assert.match(text, /web-probe observe command/u);
|
||||
assert.match(text, /startObserver: bun scripts\/cli\.ts web-probe observe start/u);
|
||||
assert.match(text, /mdtodoSummary: bun scripts\/cli\.ts web-probe observe collect/u);
|
||||
});
|
||||
@@ -1413,6 +1413,9 @@ export function renderWebProbeScriptResult(result: Record<string, unknown>): Rec
|
||||
const recoveredArtifactSummary = record(recoveredArtifacts.artifacts);
|
||||
const recoveredItems = webObserveArray(recoveredArtifactSummary.items).slice(-8).map((item) => record(item));
|
||||
const steps = webObserveArray(probe.steps).slice(-5).map((item) => record(item));
|
||||
const warnings = webObserveArray(result.warnings).slice(0, 6);
|
||||
const hints = webObserveArray(result.hints).slice(0, 6).map((item) => String(item ?? "").trim()).filter(Boolean);
|
||||
const preferredCommands = record(result.preferredCommands);
|
||||
const resultRows = webProbeScriptRecordRows(scriptResult, 12);
|
||||
const evidenceRows = webProbeScriptRecordRows(issueEvidence, 12);
|
||||
const summaryRows = [
|
||||
@@ -1462,9 +1465,27 @@ export function renderWebProbeScriptResult(result: Record<string, unknown>): Rec
|
||||
recoveredItems.map((item) => [item.kind, item.byteCount, item.path]),
|
||||
),
|
||||
"",
|
||||
warnings.length === 0
|
||||
? "WARNINGS\n-"
|
||||
: [
|
||||
"WARNINGS",
|
||||
webObserveTable(
|
||||
["CODE", "SEVERITY", "MESSAGE"],
|
||||
warnings.map((item) => {
|
||||
const warning = record(item);
|
||||
return [warning.code ?? "warning", warning.severity ?? warning.level ?? "warning", warning.message ?? warning.summary ?? webProbeScriptPreview(warning)];
|
||||
}),
|
||||
),
|
||||
].join("\n"),
|
||||
"",
|
||||
hints.length === 0
|
||||
? "HINTS\n-"
|
||||
: ["HINTS", ...hints.map((hint) => `- ${hint}`)].join("\n"),
|
||||
"",
|
||||
"NEXT",
|
||||
` report: ${reportLoad.path ?? probe.reportPath ?? "-"}`,
|
||||
` rerun: ${result.command ?? `web-probe script --node ${result.node ?? "-"} --lane ${result.lane ?? "-"}`}`,
|
||||
...Object.entries(preferredCommands).map(([name, command]) => ` ${name}: ${String(command)}`),
|
||||
].join("\n");
|
||||
return withWebObserveRendered(result, renderedText);
|
||||
}
|
||||
|
||||
@@ -558,6 +558,9 @@ export function runNodeWebProbeScript(
|
||||
degradedReason: recoveredReport.degradedReason,
|
||||
result: recoveredReport.result === null ? null : compactCommandResultRedacted(recoveredReport.result, [material.password ?? ""]),
|
||||
},
|
||||
warnings: webProbeScriptGovernanceWarnings(options),
|
||||
hints: webProbeScriptGovernanceHints(options),
|
||||
preferredCommands: webProbeScriptPreferredCommands(options),
|
||||
recoveredArtifacts: recoveredArtifacts === null ? null : {
|
||||
source: recoveredArtifacts.source,
|
||||
degradedReason: recoveredArtifacts.degradedReason,
|
||||
@@ -569,6 +572,34 @@ export function runNodeWebProbeScript(
|
||||
});
|
||||
}
|
||||
|
||||
function webProbeScriptGovernanceWarnings(options: NodeWebProbeScriptOptions): Record<string, unknown>[] {
|
||||
return [{
|
||||
code: "web_probe_script_ad_hoc_only",
|
||||
severity: "warning",
|
||||
message: "web-probe script is for one-off exploration; repeated or high-frequency checks must be promoted to a typed command instead of rerunning temporary scripts.",
|
||||
node: options.node,
|
||||
lane: options.lane,
|
||||
valuesRedacted: true,
|
||||
}];
|
||||
}
|
||||
|
||||
function webProbeScriptGovernanceHints(options: NodeWebProbeScriptOptions): string[] {
|
||||
return [
|
||||
"Prefer `web-probe observe start` plus `web-probe observe command` for interactive flows; use `observe collect/analyze` for repeated evidence reads.",
|
||||
"If the same script is needed more than once, add or extend a reusable command type in the web-probe observe command surface.",
|
||||
`For this target, start with: bun scripts/cli.ts web-probe observe start --node ${options.node} --lane ${options.lane} --target-path /projects/mdtodo`,
|
||||
];
|
||||
}
|
||||
|
||||
function webProbeScriptPreferredCommands(options: NodeWebProbeScriptOptions): Record<string, string> {
|
||||
return {
|
||||
startObserver: `bun scripts/cli.ts web-probe observe start --node ${options.node} --lane ${options.lane} --target-path /projects/mdtodo`,
|
||||
mdtodoSummary: "bun scripts/cli.ts web-probe observe collect <observerId> --view project-mdtodo-summary",
|
||||
analyze: "bun scripts/cli.ts web-probe observe analyze <observerId>",
|
||||
addCommandType: "Add a repo-owned web-probe observe command type before repeating this script.",
|
||||
};
|
||||
}
|
||||
|
||||
export function nodeWebProbeScriptRemoteShell(options: NodeWebProbeScriptOptions, secretSpec: RuntimeSecretSpec, password: string, webProbeProxy: NodeWebProbeHostProxyEnv): string {
|
||||
const userScriptB64 = Buffer.from(options.scriptText, "utf8").toString("base64");
|
||||
const runnerB64 = Buffer.from(nodeWebProbeScriptRunnerSource(), "utf8").toString("base64");
|
||||
|
||||
Reference in New Issue
Block a user