mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-22 10:25:20 -06:00
c70aee247e
* refactor(scripts): migrate JavaScript tools to TypeScript * fix(ci): keep changed-scope preflight zero-install * fix(ci): preserve zero-install script owners * fix(ci): complete script migration follow-through * fix(release): keep stable closeout zero-install * fix(scripts): preserve standalone execution boundaries * fix(scripts): repair standalone loader boundaries * fix(scripts): normalize gateway observation ids * fix(scripts): keep Docker packager standalone * test(scripts): preserve rebase cleanup helpers * test(sessions): use tracked temp directory
358 lines
9.7 KiB
TypeScript
358 lines
9.7 KiB
TypeScript
#!/usr/bin/env node
|
|
// Builds an HTML/manifest evidence bundle from Telegram Desktop proof artifacts.
|
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
type CliArgs = Record<string, string>;
|
|
type LaneName = "baseline" | "candidate";
|
|
type SessionSummary = {
|
|
artifacts?: Partial<
|
|
Record<
|
|
"previewGifCropped" | "previewGif" | "screenshot" | "trimmedVideoCropped" | "trimmedVideo",
|
|
string
|
|
>
|
|
>;
|
|
report?: string;
|
|
status?: string;
|
|
sutAttestation?: { lane?: string; sha?: string };
|
|
};
|
|
type LoadedLane = {
|
|
outputDir: string;
|
|
repoRoot: string;
|
|
status: string;
|
|
summary: SessionSummary;
|
|
summaryPath: string;
|
|
};
|
|
type EvidenceArtifact = {
|
|
alt?: string;
|
|
inline?: boolean;
|
|
kind: string;
|
|
label: string;
|
|
lane: LaneName;
|
|
path: string;
|
|
required?: boolean;
|
|
targetPath: string;
|
|
width?: number;
|
|
};
|
|
type TelegramDesktopProofManifest = {
|
|
schemaVersion: number;
|
|
id: string;
|
|
title: string;
|
|
summary: string;
|
|
scenario: string;
|
|
comparison: {
|
|
baseline: { expected: string; status: string; ref?: string; sha?: string };
|
|
candidate: { expected: string; status: string; fixed: boolean; ref?: string; sha?: string };
|
|
pass: boolean;
|
|
};
|
|
artifacts: EvidenceArtifact[];
|
|
};
|
|
|
|
const LANES = [
|
|
{
|
|
altPrefix: "Baseline",
|
|
label: "Main",
|
|
lane: "baseline",
|
|
},
|
|
{
|
|
altPrefix: "Candidate",
|
|
label: "This PR",
|
|
lane: "candidate",
|
|
},
|
|
] satisfies ReadonlyArray<{
|
|
altPrefix: string;
|
|
label: string;
|
|
lane: LaneName;
|
|
}>;
|
|
|
|
function parseArgs(argv: string[]): CliArgs {
|
|
const args: CliArgs = {};
|
|
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 requireArg(args: CliArgs, name: string): string {
|
|
const value = args[name];
|
|
if (!value) {
|
|
throw new Error(`Missing --${name.replaceAll("_", "-")}.`);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function readJson(filePath: string): SessionSummary {
|
|
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
}
|
|
|
|
function copyArtifact({
|
|
outputDir,
|
|
required = true,
|
|
source,
|
|
targetPath,
|
|
}: {
|
|
outputDir: string;
|
|
required?: boolean;
|
|
source?: string;
|
|
targetPath: string;
|
|
}) {
|
|
if (!source || !existsSync(source)) {
|
|
if (required) {
|
|
throw new Error(`Missing required artifact: ${source}`);
|
|
}
|
|
return false;
|
|
}
|
|
const target = path.join(outputDir, targetPath);
|
|
mkdirSync(path.dirname(target), { recursive: true });
|
|
copyFileSync(source, target);
|
|
return true;
|
|
}
|
|
|
|
function resolveSummaryArtifact(
|
|
lane: LoadedLane,
|
|
key: keyof NonNullable<SessionSummary["artifacts"]>,
|
|
) {
|
|
const value = lane.summary.artifacts?.[key];
|
|
return typeof value === "string" ? path.resolve(lane.repoRoot, value) : undefined;
|
|
}
|
|
|
|
function loadLane({
|
|
outputDir,
|
|
repoRoot,
|
|
status,
|
|
}: {
|
|
outputDir: string;
|
|
repoRoot: string;
|
|
status?: string;
|
|
}): LoadedLane {
|
|
const summaryPath = path.join(outputDir, "telegram-user-crabbox-session-summary.json");
|
|
const summary = readJson(summaryPath);
|
|
return {
|
|
outputDir,
|
|
repoRoot,
|
|
status: status || summary.status || "unknown",
|
|
summary,
|
|
summaryPath,
|
|
};
|
|
}
|
|
|
|
function copyLaneArtifacts({
|
|
lane,
|
|
laneName,
|
|
outputDir,
|
|
}: {
|
|
lane: LoadedLane;
|
|
laneName: LaneName;
|
|
outputDir: string;
|
|
}) {
|
|
const prefix = laneName;
|
|
const gif =
|
|
resolveSummaryArtifact(lane, "previewGifCropped") ?? resolveSummaryArtifact(lane, "previewGif");
|
|
copyArtifact({
|
|
outputDir,
|
|
source: gif,
|
|
targetPath: `${prefix}/telegram-desktop-proof.gif`,
|
|
});
|
|
copyArtifact({
|
|
outputDir,
|
|
required: false,
|
|
source:
|
|
resolveSummaryArtifact(lane, "trimmedVideoCropped") ??
|
|
resolveSummaryArtifact(lane, "trimmedVideo"),
|
|
targetPath: `${prefix}/telegram-desktop-proof.mp4`,
|
|
});
|
|
copyArtifact({
|
|
outputDir,
|
|
required: false,
|
|
source: resolveSummaryArtifact(lane, "screenshot"),
|
|
targetPath: `${prefix}/telegram-desktop-proof.png`,
|
|
});
|
|
copyArtifact({
|
|
outputDir,
|
|
source: lane.summaryPath,
|
|
targetPath: `${prefix}/summary.json`,
|
|
});
|
|
copyArtifact({
|
|
outputDir,
|
|
required: false,
|
|
source:
|
|
typeof lane.summary.report === "string"
|
|
? path.resolve(lane.repoRoot, lane.summary.report)
|
|
: undefined,
|
|
targetPath: `${prefix}/report.md`,
|
|
});
|
|
}
|
|
|
|
function laneStatus(lane: LoadedLane) {
|
|
return lane.status === "pass" ? "pass" : "fail";
|
|
}
|
|
|
|
function requireLaneAttestation(lane: LoadedLane, expectedLane: LaneName, expectedSha: string) {
|
|
const attestation = lane.summary.sutAttestation;
|
|
if (attestation?.lane !== expectedLane || attestation?.sha !== expectedSha) {
|
|
throw new Error(`SUT attestation mismatch for ${expectedLane}.`);
|
|
}
|
|
}
|
|
|
|
function laneArtifactEntries(): EvidenceArtifact[] {
|
|
return LANES.flatMap(({ altPrefix, label, lane }) => [
|
|
{
|
|
alt: `${altPrefix} native Telegram Desktop proof GIF`,
|
|
inline: true,
|
|
kind: "motionPreview",
|
|
label,
|
|
lane,
|
|
path: `${lane}/telegram-desktop-proof.gif`,
|
|
targetPath: `${lane}/telegram-desktop-proof.gif`,
|
|
width: 420,
|
|
},
|
|
{
|
|
kind: "motionClip",
|
|
label: `${label} MP4`,
|
|
lane,
|
|
path: `${lane}/telegram-desktop-proof.mp4`,
|
|
required: false,
|
|
targetPath: `${lane}/telegram-desktop-proof.mp4`,
|
|
},
|
|
{
|
|
alt: `${altPrefix} native Telegram Desktop screenshot`,
|
|
inline: false,
|
|
kind: "desktopScreenshot",
|
|
label: `${label} screenshot`,
|
|
lane,
|
|
path: `${lane}/telegram-desktop-proof.png`,
|
|
required: false,
|
|
targetPath: `${lane}/telegram-desktop-proof.png`,
|
|
},
|
|
{
|
|
kind: "metadata",
|
|
label: `${label} session summary`,
|
|
lane,
|
|
path: `${lane}/summary.json`,
|
|
targetPath: `${lane}/summary.json`,
|
|
},
|
|
{
|
|
kind: "report",
|
|
label: `${label} session report`,
|
|
lane,
|
|
path: `${lane}/report.md`,
|
|
required: false,
|
|
targetPath: `${lane}/report.md`,
|
|
},
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Builds the manifest for paired baseline/candidate Telegram Desktop proof artifacts.
|
|
*/
|
|
function buildTelegramDesktopProofManifest({
|
|
baseline,
|
|
baselineRef,
|
|
baselineSha,
|
|
candidate,
|
|
candidateRef,
|
|
candidateSha,
|
|
scenarioLabel,
|
|
}: {
|
|
baseline: LoadedLane;
|
|
baselineRef?: string;
|
|
baselineSha?: string;
|
|
candidate: LoadedLane;
|
|
candidateRef?: string;
|
|
candidateSha?: string;
|
|
scenarioLabel?: string;
|
|
}): TelegramDesktopProofManifest {
|
|
const baselineStatus = laneStatus(baseline);
|
|
const candidateStatus = laneStatus(candidate);
|
|
const pass = baselineStatus === "pass" && candidateStatus === "pass";
|
|
return {
|
|
schemaVersion: 1,
|
|
id: "telegram-desktop-proof",
|
|
title: "Mantis Telegram Desktop Proof",
|
|
summary:
|
|
"Mantis captured native Telegram Desktop before/after GIF evidence with Convex-leased Telegram credentials.",
|
|
scenario: scenarioLabel || "telegram-desktop-proof",
|
|
comparison: {
|
|
baseline: {
|
|
...(baselineSha ? { sha: baselineSha } : {}),
|
|
...(baselineRef ? { ref: baselineRef } : {}),
|
|
expected: "baseline visual proof captured",
|
|
status: baselineStatus,
|
|
},
|
|
candidate: {
|
|
...(candidateSha ? { sha: candidateSha } : {}),
|
|
...(candidateRef ? { ref: candidateRef } : {}),
|
|
expected: "candidate visual proof captured",
|
|
status: candidateStatus,
|
|
fixed: candidateStatus === "pass",
|
|
},
|
|
pass,
|
|
},
|
|
artifacts: laneArtifactEntries(),
|
|
};
|
|
}
|
|
|
|
export function writeTelegramDesktopProofEvidence(rawArgs: string[] = process.argv.slice(2)): {
|
|
manifest: TelegramDesktopProofManifest;
|
|
manifestPath: string;
|
|
} {
|
|
const args = parseArgs(rawArgs);
|
|
const baselineOutputDir = requireArg(args, "baseline_output_dir");
|
|
const baselineRepoRoot = requireArg(args, "baseline_repo_root");
|
|
const baselineSha = requireArg(args, "baseline_sha");
|
|
const candidateOutputDir = requireArg(args, "candidate_output_dir");
|
|
const candidateRepoRoot = requireArg(args, "candidate_repo_root");
|
|
const candidateSha = requireArg(args, "candidate_sha");
|
|
const evidenceOutputDir = requireArg(args, "output_dir");
|
|
|
|
const outputDir = path.resolve(evidenceOutputDir);
|
|
mkdirSync(outputDir, { recursive: true });
|
|
const baseline = loadLane({
|
|
outputDir: path.resolve(baselineOutputDir),
|
|
repoRoot: path.resolve(baselineRepoRoot),
|
|
status: args.baseline_status,
|
|
});
|
|
const candidate = loadLane({
|
|
outputDir: path.resolve(candidateOutputDir),
|
|
repoRoot: path.resolve(candidateRepoRoot),
|
|
status: args.candidate_status,
|
|
});
|
|
requireLaneAttestation(baseline, "baseline", baselineSha);
|
|
requireLaneAttestation(candidate, "candidate", candidateSha);
|
|
copyLaneArtifacts({ lane: baseline, laneName: "baseline", outputDir });
|
|
copyLaneArtifacts({ lane: candidate, laneName: "candidate", outputDir });
|
|
const manifest = buildTelegramDesktopProofManifest({
|
|
baseline,
|
|
baselineRef: args.baseline_ref,
|
|
baselineSha,
|
|
candidate,
|
|
candidateRef: args.candidate_ref,
|
|
candidateSha,
|
|
scenarioLabel: args.scenario_label,
|
|
});
|
|
const manifestPath = path.join(outputDir, "mantis-evidence.json");
|
|
writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
return { manifest, manifestPath };
|
|
}
|
|
|
|
const executedPath = process.argv[1] ? path.resolve(process.argv[1]) : "";
|
|
if (executedPath === fileURLToPath(import.meta.url)) {
|
|
try {
|
|
writeTelegramDesktopProofEvidence();
|
|
} catch (error) {
|
|
console.error(error instanceof Error ? error.message : String(error));
|
|
process.exit(1);
|
|
}
|
|
}
|