import { createHash } from 'node:crypto'; import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { buildStandalone, bundleRuntime } from './build-xzbt.mjs'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); export function buildAudioAcceptance(outputPath = resolve(root, 'prototypes/phase3/XZBT-audio-acceptance.html')) { const runtime = buildStandalone(); const challengeSource = readFileSync(resolve(root, 'exhibits/audio-challenge.xzbt'), 'utf8'); const stressSource = readFileSync(resolve(root, 'exhibits/audio-protection-stress.xzbt'), 'utf8'); const digest = source => createHash('sha256').update(source).digest('hex'); const metadata = { runtimeSha256: runtime.sha256, challengeSha256: digest(challengeSource), stressSha256: digest(stressSource), workload: 'phase3c4-overlap-v1', seed: 42, warmupSeconds: 30, measurementSeconds: 120 }; const source = `${bundleRuntime(false)}\nconst ACCEPTANCE_BUILD = ${JSON.stringify(metadata)};\nconst CHALLENGE = ${challengeSource};\nconst STRESS = ${stressSource};\n${readFileSync(resolve(root, 'tools/audio-acceptance.js'), 'utf8')}`; const template = readFileSync(resolve(root, 'prototypes/phase3/acceptance.template.html'), 'utf8'); const artifact = template.replace('/*__ACCEPTANCE_SCRIPT__*/', () => `'use strict';\n(() => {\n${source}\n})();`).replace(/\r\n/g, '\n'); mkdirSync(dirname(outputPath), { recursive: true }); writeFileSync(outputPath, artifact); return { outputPath, sha256: digest(artifact), bytes: Buffer.byteLength(artifact) }; } if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) console.log(JSON.stringify(buildAudioAcceptance(), null, 2));