Merge pull request #470 from pikasTech/fix/469-web-probe-compact
fix: 收敛 web-probe script stdout 摘要
This commit is contained in:
@@ -5131,16 +5131,47 @@ function runNodeWebProbeScript(
|
||||
): Record<string, unknown> {
|
||||
const script = nodeWebProbeScriptRemoteShell(options, secretSpec, material.password ?? "");
|
||||
const result = runTransWorkspaceStdinScript(options.node, spec.workspace, script, options.commandTimeoutSeconds);
|
||||
const report = compactWebProbeScriptResult(parseJsonObject(result.stdout));
|
||||
const parsedReport = parseJsonObject(result.stdout);
|
||||
const report = compactWebProbeScriptResult(parsedReport);
|
||||
const passed = result.exitCode === 0 && report?.ok === true;
|
||||
const summary = nullableRecord(report?.summary);
|
||||
const stdoutBytes = Buffer.byteLength(result.stdout, "utf8");
|
||||
const outputFailureKind = parsedReport === null
|
||||
? stdoutBytes > 64 * 1024
|
||||
? "web-probe-output-too-large"
|
||||
: "web-probe-report-parse-failed"
|
||||
: null;
|
||||
const degradedReason = typeof summary?.degradedReason === "string"
|
||||
? summary.degradedReason
|
||||
: typeof report?.failureKind === "string"
|
||||
? report.failureKind
|
||||
: outputFailureKind
|
||||
? outputFailureKind
|
||||
: result.timedOut
|
||||
? "web-probe-command-timeout"
|
||||
: null;
|
||||
const failureKind = typeof summary?.failureKind === "string"
|
||||
? summary.failureKind
|
||||
: typeof report?.failureKind === "string"
|
||||
? report.failureKind
|
||||
: outputFailureKind;
|
||||
const effectiveSummary = summary ?? (outputFailureKind === null ? null : {
|
||||
ok: false,
|
||||
status: "blocked",
|
||||
degradedReason: outputFailureKind,
|
||||
failureKind: outputFailureKind,
|
||||
failedCondition: outputFailureKind === "web-probe-output-too-large"
|
||||
? "remote web-probe stdout exceeded the bounded summary parser input"
|
||||
: "remote web-probe stdout did not contain a parseable JSON report",
|
||||
stdoutBytes,
|
||||
stderrTail: result.stderr.trim().slice(-2000),
|
||||
valuesRedacted: true,
|
||||
});
|
||||
const compactResult = compactCommandResultRedacted(result, [material.password ?? ""]);
|
||||
if (outputFailureKind !== null) {
|
||||
compactResult.stdoutTail = redactKnownSecrets(result.stdout.slice(-2000), [material.password ?? ""]);
|
||||
compactResult.stderrTail = redactKnownSecrets(result.stderr.slice(-2000), [material.password ?? ""]);
|
||||
}
|
||||
return {
|
||||
ok: passed,
|
||||
status: passed ? "pass" : "blocked",
|
||||
@@ -5152,10 +5183,10 @@ function runNodeWebProbeScript(
|
||||
credential,
|
||||
scriptSource: options.scriptSource,
|
||||
degradedReason,
|
||||
failureKind: typeof summary?.failureKind === "string" ? summary.failureKind : typeof report?.failureKind === "string" ? report.failureKind : null,
|
||||
summary,
|
||||
failureKind,
|
||||
summary: effectiveSummary,
|
||||
probe: report,
|
||||
result: compactCommandResultRedacted(result, [material.password ?? ""]),
|
||||
result: compactResult,
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1499,7 +1499,175 @@ async function emit(payload) {
|
||||
artifacts: { runDir, items: artifactRecords },
|
||||
};
|
||||
finalPayload.summary = scriptIssueSummary(finalPayload);
|
||||
process.stdout.write(JSON.stringify(sanitize(finalPayload), null, 2) + "\n");
|
||||
process.stdout.write(JSON.stringify(sanitize(compactStdoutPayload(finalPayload)), null, 2) + "\n");
|
||||
}
|
||||
|
||||
function compactStdoutPayload(payload) {
|
||||
return {
|
||||
ok: payload?.ok === true,
|
||||
status: payload?.status ?? null,
|
||||
command: payload?.command ?? "web-probe-script",
|
||||
generatedAt: payload?.generatedAt ?? null,
|
||||
startedAt: payload?.startedAt ?? null,
|
||||
baseUrl: payload?.baseUrl ?? null,
|
||||
finalUrl: payload?.finalUrl ?? payload?.lastUrl ?? null,
|
||||
lastUrl: payload?.lastUrl ?? null,
|
||||
scriptSha256: payload?.scriptSha256 ?? null,
|
||||
runDir,
|
||||
reportPath: payload?.reportPath ?? null,
|
||||
reportSha256: payload?.reportSha256 ?? null,
|
||||
auth: payload?.auth ?? null,
|
||||
script: compactScriptForStdout(payload?.script),
|
||||
steps: compactStepsForStdout(publicSteps()),
|
||||
failureKind: payload?.failureKind ?? null,
|
||||
error: payload?.error ?? null,
|
||||
errorMessage: typeof payload?.errorMessage === "string" ? compactText(payload.errorMessage, 1200) : payload?.errorMessage ?? null,
|
||||
guidance: typeof payload?.guidance === "string" ? compactText(payload.guidance, 1200) : payload?.guidance ?? null,
|
||||
lastScreenshot: compactArtifactForStdout(payload?.lastScreenshot),
|
||||
readiness: compactJsonForStdout(payload?.readiness),
|
||||
artifacts: compactArtifactsForStdout(payload?.artifacts),
|
||||
summary: compactSummaryForStdout(payload?.summary),
|
||||
safety: {
|
||||
valuesRedacted: true,
|
||||
secretValuesPrinted: false,
|
||||
stdoutCompacted: true,
|
||||
fullReportInArtifact: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function compactScriptForStdout(script) {
|
||||
const value = script && typeof script === "object" ? script : {};
|
||||
const steps = Array.isArray(value.steps) ? value.steps : [];
|
||||
return {
|
||||
ok: value.ok === true,
|
||||
result: compactJsonForStdout(value.result),
|
||||
stepCount: steps.length,
|
||||
};
|
||||
}
|
||||
|
||||
function compactStepsForStdout(steps) {
|
||||
if (!Array.isArray(steps)) return [];
|
||||
return steps.slice(-3).map((step) => compactStepForStdout(step));
|
||||
}
|
||||
|
||||
function compactStepForStdout(step) {
|
||||
const value = step && typeof step === "object" ? step : {};
|
||||
return {
|
||||
index: Number.isFinite(value.index) ? value.index : null,
|
||||
name: typeof value.name === "string" ? value.name : null,
|
||||
ok: typeof value.ok === "boolean" ? value.ok : null,
|
||||
atMs: Number.isFinite(value.atMs) ? value.atMs : null,
|
||||
data: compactStepDataForStdout(value.data),
|
||||
};
|
||||
}
|
||||
|
||||
function compactStepDataForStdout(data) {
|
||||
if (data && typeof data === "object" && !Array.isArray(data) && Array.isArray(data.items) && typeof data.failedCount === "number") {
|
||||
return compactApiMatrixForStdout(data);
|
||||
}
|
||||
if (data && typeof data === "object" && !Array.isArray(data)) {
|
||||
const out = {};
|
||||
for (const [key, value] of Object.entries(data).slice(0, 8)) {
|
||||
if (key === "apiMatrix") out[key] = compactApiMatrixForStdout(value);
|
||||
else if (/screenshot/iu.test(key)) out[key] = compactArtifactForStdout(value);
|
||||
else out[key] = compactJsonForStdout(value);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
return compactJsonForStdout(data);
|
||||
}
|
||||
|
||||
function compactSummaryForStdout(summary) {
|
||||
const value = summary && typeof summary === "object" ? summary : {};
|
||||
return {
|
||||
ok: value.ok === true,
|
||||
status: value.status ?? null,
|
||||
degradedReason: value.degradedReason ?? null,
|
||||
failureKind: value.failureKind ?? null,
|
||||
failedCondition: typeof value.failedCondition === "string" ? compactText(value.failedCondition, 1200) : value.failedCondition ?? null,
|
||||
nextAction: typeof value.nextAction === "string" ? compactText(value.nextAction, 1200) : value.nextAction ?? null,
|
||||
baseUrl: value.baseUrl ?? null,
|
||||
finalUrl: value.finalUrl ?? null,
|
||||
scriptSha256: value.scriptSha256 ?? null,
|
||||
runDir: value.runDir ?? runDir,
|
||||
reportPath: value.reportPath ?? null,
|
||||
reportSha256: value.reportSha256 ?? null,
|
||||
lastScreenshot: compactArtifactForStdout(value.lastScreenshot),
|
||||
screenshots: Array.isArray(value.screenshots) ? value.screenshots.slice(-5).map((item) => compactArtifactForStdout(item)).filter(Boolean) : [],
|
||||
apiMatrix: compactApiMatrixForStdout(value.apiMatrix),
|
||||
stepCount: Number.isFinite(value.stepCount) ? value.stepCount : null,
|
||||
lastStep: compactStepForStdout(value.lastStep),
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}
|
||||
|
||||
function compactArtifactsForStdout(artifacts) {
|
||||
const items = Array.isArray(artifacts?.items) ? artifacts.items : artifactRecords;
|
||||
return {
|
||||
runDir,
|
||||
count: items.length,
|
||||
screenshots: items.filter((item) => item && typeof item === "object" && item.kind === "screenshot").slice(-5).map((item) => compactArtifactForStdout(item)).filter(Boolean),
|
||||
items: items.slice(-12).map((item) => compactArtifactForStdout(item)).filter(Boolean),
|
||||
};
|
||||
}
|
||||
|
||||
function compactArtifactForStdout(item) {
|
||||
if (!item || typeof item !== "object") return null;
|
||||
return {
|
||||
kind: item.kind ?? null,
|
||||
path: item.path ?? null,
|
||||
byteCount: Number.isFinite(item.byteCount) ? item.byteCount : null,
|
||||
sha256: item.sha256 ?? null,
|
||||
error: typeof item.error === "string" ? compactText(item.error, 600) : item.error ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function compactApiMatrixForStdout(matrix) {
|
||||
if (!matrix || typeof matrix !== "object" || Array.isArray(matrix)) return null;
|
||||
return {
|
||||
ok: matrix.ok === true,
|
||||
count: Number.isFinite(matrix.count) ? matrix.count : null,
|
||||
okCount: Number.isFinite(matrix.okCount) ? matrix.okCount : null,
|
||||
failedCount: Number.isFinite(matrix.failedCount) ? matrix.failedCount : null,
|
||||
items: Array.isArray(matrix.items)
|
||||
? matrix.items.slice(0, 12).map((item) => {
|
||||
const row = item && typeof item === "object" ? item : {};
|
||||
return {
|
||||
name: typeof row.name === "string" ? row.name : null,
|
||||
path: typeof row.path === "string" ? row.path : null,
|
||||
method: typeof row.method === "string" ? row.method : null,
|
||||
ok: row.ok === true,
|
||||
status: Number.isFinite(row.status) ? row.status : null,
|
||||
error: typeof row.error === "string" ? compactText(row.error, 300) : row.error ?? null,
|
||||
failureKind: typeof row.failureKind === "string" ? row.failureKind : null,
|
||||
bodyKeys: Array.isArray(row.bodyKeys) ? row.bodyKeys.filter((key) => typeof key === "string").slice(0, 12) : [],
|
||||
};
|
||||
})
|
||||
: [],
|
||||
};
|
||||
}
|
||||
|
||||
function compactJsonForStdout(value, depth = 0) {
|
||||
if (value === null || value === undefined) return value ?? null;
|
||||
if (typeof value === "string") return compactText(value, 180);
|
||||
if (typeof value === "number" || typeof value === "boolean") return value;
|
||||
if (typeof value === "bigint") return String(value);
|
||||
if (typeof value === "function" || typeof value === "symbol") return "[" + typeof value + "]";
|
||||
if (depth >= 3) return "[max-depth]";
|
||||
if (Array.isArray(value)) return value.slice(0, 4).map((item) => compactJsonForStdout(item, depth + 1));
|
||||
if (typeof value === "object") {
|
||||
const out = {};
|
||||
for (const [key, nested] of Object.entries(value).slice(0, 8)) {
|
||||
out[key] = compactJsonForStdout(nested, depth + 1);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
return compactText(String(value), 180);
|
||||
}
|
||||
|
||||
function compactText(value, maxChars) {
|
||||
return redactString(String(value)).replace(/\s+/gu, " ").trim().slice(0, maxChars);
|
||||
}
|
||||
|
||||
function scriptIssueSummary(payload) {
|
||||
|
||||
@@ -48,7 +48,7 @@ function compactWebProbeScriptBlock(value: unknown): Record<string, unknown> {
|
||||
|
||||
function compactWebProbeSteps(value: unknown): Record<string, unknown>[] {
|
||||
if (!Array.isArray(value)) return [];
|
||||
return value.slice(-30).map((item) => {
|
||||
return value.slice(-5).map((item) => {
|
||||
const step = record(item);
|
||||
return {
|
||||
index: typeof step.index === "number" ? step.index : null,
|
||||
@@ -85,18 +85,18 @@ function compactIssueSummary(value: Record<string, unknown>): Record<string, unk
|
||||
|
||||
function compactJsonForIssue(value: unknown, depth = 0): unknown {
|
||||
if (value === null || value === undefined) return value ?? null;
|
||||
if (typeof value === "string") return value.replace(/\s+/gu, " ").trim().slice(0, 600);
|
||||
if (typeof value === "string") return value.replace(/\s+/gu, " ").trim().slice(0, 240);
|
||||
if (typeof value === "number" || typeof value === "boolean") return value;
|
||||
if (depth >= 5) return "[max-depth]";
|
||||
if (Array.isArray(value)) return value.slice(0, 30).map((item) => compactJsonForIssue(item, depth + 1));
|
||||
if (depth >= 4) return "[max-depth]";
|
||||
if (Array.isArray(value)) return value.slice(0, 8).map((item) => compactJsonForIssue(item, depth + 1));
|
||||
if (typeof value === "object") {
|
||||
const out: Record<string, unknown> = {};
|
||||
for (const [key, nested] of Object.entries(value as Record<string, unknown>).slice(0, 40)) {
|
||||
for (const [key, nested] of Object.entries(value as Record<string, unknown>).slice(0, 16)) {
|
||||
out[key] = compactJsonForIssue(nested, depth + 1);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
return String(value).slice(0, 600);
|
||||
return String(value).slice(0, 240);
|
||||
}
|
||||
|
||||
function compactStepForIssue(value: unknown): Record<string, unknown> | null {
|
||||
|
||||
Reference in New Issue
Block a user