#!/usr/bin/env node import { mkdir, writeFile } from 'node:fs/promises'; import { dirname, resolve } from 'node:path'; import { pathToFileURL } from 'node:url'; import { defaultProductExecutable, verifyPiProductArtifact, } from './lib/pi-product-artifact.mjs'; import { runProbe } from './probe-pi-runtime.mjs'; export function parsePiArtifactVerifierArgs(argv, projectRoot = process.cwd()) { const options = { projectRoot: resolve(projectRoot), executable: undefined, reportPath: undefined, samples: 1, timeoutMs: 10_000, }; for (let index = 0; index < argv.length; index += 1) { const argument = argv[index]; const next = () => { const value = argv[index + 1]; if (!value || value.startsWith('--')) throw new Error(`${argument} requires a value`); index += 1; return value; }; if (argument === '--') continue; if (argument === '--app-exe') options.executable = resolve(next()); else if (argument === '--report') options.reportPath = resolve(next()); else if (argument === '--samples') options.samples = Number.parseInt(next(), 10); else if (argument === '--timeout-ms') options.timeoutMs = Number.parseInt(next(), 10); else if (argument === '--help') options.help = true; else throw new Error(`Unknown argument: ${argument}`); } for (const [name, value] of [['--samples', options.samples], ['--timeout-ms', options.timeoutMs]]) { if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`${name} must be a positive integer`); } options.executable ??= defaultProductExecutable(options.projectRoot); return options; } function printHelp() { process.stdout.write('Usage: node scripts/verify-pi-product-artifact.mjs [options]\n\n'); process.stdout.write(' --app-exe Final unpacked Makelore executable\n'); process.stdout.write(' --samples Packaged get_state/runtime samples (default: 1)\n'); process.stdout.write(' --timeout-ms Runtime probe timeout (default: 10000)\n'); process.stdout.write(' --report Write structured JSON evidence\n'); } export async function runPiArtifactVerifier(options) { const artifact = await verifyPiProductArtifact(options); const runtime = await runProbe({ samples: options.samples, timeoutMs: options.timeoutMs, stage: false, keepStage: false, reportPath: undefined, providerFixturePath: undefined, imagePath: undefined, electronExecutablePath: artifact.artifact.executable, cliPath: artifact.artifact.cliPath, artifactLabel: 'final-makelore-product-artifact', }, options.projectRoot); if (runtime.result === 'fail') throw new Error('Final product Pi get_state/runtime probe failed'); const report = { schemaVersion: 1, generatedAt: new Date().toISOString(), artifact, runtime, result: 'pass', }; if (options.reportPath) { await mkdir(dirname(options.reportPath), { recursive: true }); await writeFile(options.reportPath, `${JSON.stringify(report, null, 2)}\n`); } return report; } async function main() { const options = parsePiArtifactVerifierArgs(process.argv.slice(2)); if (options.help) { printHelp(); return; } const report = await runPiArtifactVerifier(options); process.stdout.write(`${JSON.stringify(report, null, 2)}\n`); } const isMain = process.argv[1] && pathToFileURL(resolve(process.argv[1])).href === import.meta.url; if (isMain) { main().catch((error) => { process.stderr.write(`${error.stack ?? error.message}\n`); process.exitCode = 1; }); }