| 66 | |
| 67 | // Fetch checksums.txt and verify the archive hash. |
| 68 | async function verifyChecksum(archivePath, archiveName) { |
| 69 | const url = `https://github.com/${REPO}/releases/download/v${VERSION}/checksums.txt`; |
| 70 | const tmpChecksums = archivePath + '.checksums'; |
| 71 | try { |
| 72 | await download(url, tmpChecksums); |
| 73 | const lines = fs.readFileSync(tmpChecksums, 'utf-8').split('\n'); |
| 74 | const match = lines.find((l) => l.includes(archiveName)); |
| 75 | if (!match) return; // checksum line not found — non-fatal |
| 76 | const expected = match.split(/\s+/)[0]; |
| 77 | const actual = crypto |
| 78 | .createHash('sha256') |
| 79 | .update(fs.readFileSync(archivePath)) |
| 80 | .digest('hex'); |
| 81 | if (expected !== actual) { |
| 82 | throw new Error( |
| 83 | `Checksum mismatch for ${archiveName}:\n expected: ${expected}\n actual: ${actual}`, |
| 84 | ); |
| 85 | } |
| 86 | process.stdout.write('codebase-memory-mcp: checksum verified.\n'); |
| 87 | } catch (err) { |
| 88 | if (err.message.startsWith('Checksum mismatch')) throw err; |
| 89 | // Non-fatal: checksum unavailable (network issue, pre-release, etc.) |
| 90 | } finally { |
| 91 | try { fs.unlinkSync(tmpChecksums); } catch (_) { /* ignore */ } |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | async function main() { |
| 96 | const platform = getPlatform(); |