mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-15 11:56:01 +00:00
* ci(mantis): add web ui chat proof lane * ci(mantis): tighten web ui proof candidate parsing * ci: tighten Mantis Web UI proof lane --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
175 lines
5.4 KiB
JavaScript
175 lines
5.4 KiB
JavaScript
#!/usr/bin/env node
|
|
// Builds a Mantis evidence manifest from Control UI web chat proof artifacts.
|
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
function parseArgs(argv) {
|
|
const args = {};
|
|
for (let index = 0; index < argv.length; index += 1) {
|
|
const key = argv[index];
|
|
if (!key.startsWith("--")) {
|
|
throw new Error(`Unexpected argument: ${key}`);
|
|
}
|
|
const name = key.slice(2).replaceAll("-", "_");
|
|
const value = argv[index + 1];
|
|
if (!value || value.startsWith("--")) {
|
|
throw new Error(`Missing value for ${key}`);
|
|
}
|
|
args[name] = value;
|
|
index += 1;
|
|
}
|
|
return args;
|
|
}
|
|
|
|
function normalizeStatus(value) {
|
|
const normalized = String(value ?? "")
|
|
.trim()
|
|
.toLowerCase();
|
|
if (normalized === "pass") {
|
|
return "pass";
|
|
}
|
|
if (normalized === "fail") {
|
|
return "fail";
|
|
}
|
|
throw new Error(`Unsupported web UI chat proof status: ${value}`);
|
|
}
|
|
|
|
function artifactEntry({ inline = false, kind, label, path: artifactPath, required, targetPath }) {
|
|
return {
|
|
kind,
|
|
lane: "candidate",
|
|
label,
|
|
path: artifactPath,
|
|
targetPath,
|
|
required,
|
|
...(inline ? { alt: label, inline: true, width: 900 } : {}),
|
|
};
|
|
}
|
|
|
|
export function buildWebUiChatEvidenceManifest({ candidateRef, candidateSha, status }) {
|
|
const passed = status === "pass";
|
|
return {
|
|
schemaVersion: 1,
|
|
id: "web-ui-chat-proof",
|
|
title: "Mantis Web UI Chat Proof",
|
|
summary:
|
|
"Mantis ran the OpenClaw Control UI chat proof against the candidate ref, sent a message through the mocked Gateway, rendered the final reply in the browser, and captured browser artifacts for review.",
|
|
scenario: "web-ui-chat-proof",
|
|
comparison: {
|
|
candidate: {
|
|
...(candidateSha ? { sha: candidateSha } : {}),
|
|
...(candidateRef ? { ref: candidateRef } : {}),
|
|
expected: "Control UI chat sends through the Gateway and renders the final reply",
|
|
status,
|
|
fixed: passed,
|
|
},
|
|
pass: passed,
|
|
},
|
|
artifacts: [
|
|
artifactEntry({
|
|
inline: true,
|
|
kind: "desktopScreenshot",
|
|
label: "Control UI web chat proof",
|
|
path: "web-ui-chat.png",
|
|
required: passed,
|
|
targetPath: "web-ui-chat.png",
|
|
}),
|
|
artifactEntry({
|
|
kind: "fullVideo",
|
|
label: "Control UI web chat recording",
|
|
path: "web-ui-chat.webm",
|
|
required: false,
|
|
targetPath: "web-ui-chat.webm",
|
|
}),
|
|
artifactEntry({
|
|
kind: "metadata",
|
|
label: "Control UI web chat proof metadata",
|
|
path: "web-ui-chat-proof.json",
|
|
required: passed,
|
|
targetPath: "web-ui-chat-proof.json",
|
|
}),
|
|
artifactEntry({
|
|
kind: "metadata",
|
|
label: "Control UI web chat Vitest log",
|
|
path: "vitest.log",
|
|
required: false,
|
|
targetPath: "vitest.log",
|
|
}),
|
|
{
|
|
kind: "report",
|
|
lane: "run",
|
|
label: "Mantis web UI chat report",
|
|
path: "mantis-report.md",
|
|
targetPath: "mantis-report.md",
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
function renderReport({ candidateRef, candidateSha, outputDir, status }) {
|
|
const artifactStatus = (artifactPath) =>
|
|
existsSync(path.join(outputDir, artifactPath)) ? "present" : "missing";
|
|
return [
|
|
"# Mantis Web UI Chat Proof",
|
|
"",
|
|
`Status: ${status}`,
|
|
`Candidate ref: ${candidateRef || "unspecified"}`,
|
|
`Candidate SHA: ${candidateSha || "unspecified"}`,
|
|
"",
|
|
"## Scenario",
|
|
"",
|
|
"OpenClaw Control UI chat was loaded in a browser with the mocked Gateway harness. The proof sends a chat message through the GUI, verifies the `chat.send` request, emits a final Gateway reply, and waits for the reply to render in the web chat thread.",
|
|
"",
|
|
"## Artifacts",
|
|
"",
|
|
`- Screenshot: \`web-ui-chat.png\` (${artifactStatus("web-ui-chat.png")})`,
|
|
`- Recording: \`web-ui-chat.webm\` (${artifactStatus("web-ui-chat.webm")})`,
|
|
`- Proof metadata: \`web-ui-chat-proof.json\` (${artifactStatus("web-ui-chat-proof.json")})`,
|
|
`- Vitest log: \`vitest.log\` (${artifactStatus("vitest.log")})`,
|
|
"",
|
|
].join("\n");
|
|
}
|
|
|
|
export function writeWebUiChatEvidence(rawArgs = process.argv.slice(2)) {
|
|
const args = parseArgs(rawArgs);
|
|
if (!args.output_dir) {
|
|
throw new Error("Missing --output-dir.");
|
|
}
|
|
if (!args.status) {
|
|
throw new Error("Missing --status.");
|
|
}
|
|
const outputDir = path.resolve(args.output_dir);
|
|
mkdirSync(outputDir, { recursive: true });
|
|
const status = normalizeStatus(args.status);
|
|
const manifest = buildWebUiChatEvidenceManifest({
|
|
candidateRef: args.candidate_ref,
|
|
candidateSha: args.candidate_sha,
|
|
status,
|
|
});
|
|
const reportPath = path.join(outputDir, "mantis-report.md");
|
|
writeFileSync(
|
|
reportPath,
|
|
renderReport({
|
|
candidateRef: args.candidate_ref,
|
|
candidateSha: args.candidate_sha,
|
|
outputDir,
|
|
status,
|
|
}),
|
|
"utf8",
|
|
);
|
|
const manifestPath = path.join(outputDir, "mantis-evidence.json");
|
|
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
return { manifest, manifestPath, reportPath };
|
|
}
|
|
|
|
const executedPath = process.argv[1] ? path.resolve(process.argv[1]) : "";
|
|
if (executedPath === fileURLToPath(import.meta.url)) {
|
|
try {
|
|
writeWebUiChatEvidence();
|
|
} catch (error) {
|
|
console.error(error instanceof Error ? error.message : String(error));
|
|
process.exit(1);
|
|
}
|
|
}
|