const jwt = require('jsonwebtoken'); const http = require('http'); const { execSync } = require('child_process'); const fs = require('fs'); const KEY = 'user:1022172:47'; const SECRET = 'da4e4367277668aa6e048b0a04d1a417ba8bad630f4ac37ccdcea064a9de151e'; function makeJWT() { return jwt.sign({ iss: KEY, jti: Math.random().toString(36).slice(2), iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 300 }, SECRET); } function proxyGet(url, auth) { return new Promise((resolve, reject) => { const u = new URL(url); const req = http.request({ hostname: 'localhost', port: 12334, path: url, method: 'GET', headers: { 'Host': u.hostname, 'Authorization': auth } }, res => { const chunks = []; res.on('data', c => chunks.push(c)); res.on('end', () => resolve({ status: res.statusCode, body: Buffer.concat(chunks) })); }); req.on('error', reject); req.end(); }); } async function main() { const token = makeJWT(); const auth = 'JWT ' + token; const versionId = 6297193; const addonId = 'verstak-bridge@verstak.app'; // Get full version details console.log('==> Getting full version details...'); const url = `https://addons.mozilla.org/api/v5/addons/addon/${addonId}/versions/${versionId}/`; const res = await proxyGet(url, auth); const data = JSON.parse(res.body.toString()); // The file info is in data.file (singular), not data.files const file = data.file; if (!file) { console.log('No file object in response'); process.exit(1); } console.log(` File ID: ${file.id}`); console.log(` Status: ${file.status}`); console.log(` Size: ${file.size}`); console.log(` Hash: ${file.hash}`); console.log(` URL: ${file.url}`); console.log(` Download URL: ${file.download_url || file.url}`); const downloadUrl = file.download_url || file.url; if (!downloadUrl) { console.log('No download URL found'); console.log('Full file object:', JSON.stringify(file, null, 2)); process.exit(1); } // Download console.log(`==> Downloading signed XPI...`); fs.mkdirSync('release/firefox', { recursive: true }); const outPath = 'release/firefox/verstak-firefox-1.0.1.xpi'; try { execSync( `curl -s -x http://localhost:12334 -L "${downloadUrl}" -H "Authorization: ${auth}" -H "User-Agent: verstak-release/1.0" -o "${outPath}"`, { timeout: 60000 } ); const size = fs.statSync(outPath).size; console.log(` Saved: ${outPath} (${(size / 1024).toFixed(1)} KB)`); // Verify ZIP magic const fd = fs.openSync(outPath, 'r'); const magic = Buffer.alloc(4); fs.readSync(fd, magic, 0, 4, 0); fs.closeSync(fd); const isZip = magic[0] === 0x50 && magic[1] === 0x4B; console.log(` ZIP magic: ${isZip ? 'VALID ✓' : 'INVALID ✗'} (${magic.toString('hex')})`); if (!isZip || size < 1000) { console.log(' WARNING: File may not be a valid XPI'); console.log(' Content:', fs.readFileSync(outPath).toString('utf8').substring(0, 200)); process.exit(1); } console.log('==> DONE ✓'); } catch (e) { console.error(' Download failed:', e.message); process.exit(1); } } main().catch(e => { console.error('FATAL:', e.message); process.exit(1); });