// Explicit opt-in real-hardware smoke check. Does not submit Wi-Fi or bind an account. import { spawn } from 'node:child_process'; import { createInterface } from 'node:readline'; import { readFileSync } from 'node:fs'; import ts from 'typescript'; const label = JSON.parse(readFileSync(process.argv[2], 'utf8')); const compiled = ts.transpileModule(readFileSync(new URL('../src/ble/security2.ts', import.meta.url), 'utf8'), { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext } }).outputText; const { Security2 } = await import('data:text/javascript;base64,' + Buffer.from(compiled).toString('base64')); const python = process.env.BLE_PYTHON; if (!python) throw new Error('Set BLE_PYTHON to the ESP-IDF Python executable'); const child = spawn(python, ['../.tmp-ble-release/ble_bridge.py', 'EchoEar-' + label.device_id.slice(-6)], { stdio: ['pipe','pipe','inherit'] }); const lines = createInterface({ input: child.stdout })[Symbol.asyncIterator](); async function receive() { const line = await lines.next(); if (line.done) throw new Error('BLE bridge ended'); const result = JSON.parse(line.value); if (result.error) throw new Error(result.error); return result; } try { const connected = await receive(); console.log(JSON.stringify(connected)); const security = new Security2(); async function exchange(type, data) { const header = Buffer.from([type, data.length & 255, data.length >> 8]); child.stdin.write(JSON.stringify({ frame: Buffer.concat([header, data]).toString('base64') }) + '\n'); const frame = Buffer.from((await receive()).frame,'base64'); if (frame[0] !== type || frame.readUInt16LE(1) + 3 !== frame.length) throw new Error('Invalid device reply'); return frame.subarray(3); } await security.handshake(label.device_id, label.setup_password, data => exchange(0, data)); console.log('Real ESP-IDF Security2 handshake verified'); for (const cmd of ['get_info','get_status','get_info','get_status']) { const raw = Buffer.from(JSON.stringify({ cmd })); const response = JSON.parse(Buffer.from(await security.decrypt(await exchange(1, await security.encrypt(raw)))).toString()); if (cmd === 'get_info' && response.device_id !== label.device_id) throw new Error('Wrong physical device'); console.log(JSON.stringify({ cmd, response })); } security.clear(); } finally { child.stdin.end(JSON.stringify({ close: true }) + '\n'); await new Promise(resolve => child.on('exit', resolve)); }