()
| 543 | } |
| 544 | |
| 545 | async function main() { |
| 546 | const platform = getPlatform(); |
| 547 | const arch = getArch(); |
| 548 | const ext = platform === 'windows' ? 'zip' : 'tar.gz'; |
| 549 | // Package-manager shims are portable one-shot instances: one cached binary |
| 550 | // per platform, entered directly. |
| 551 | const binName = platform === 'windows' |
| 552 | ? WINDOWS_BINARY_NAME |
| 553 | : 'codebase-memory-mcp'; |
| 554 | const binPath = path.join(BIN_DIR, binName); |
| 555 | const cacheNames = platform === 'windows' |
| 556 | ? [WINDOWS_BINARY_NAME] |
| 557 | : [binName]; |
| 558 | const extractedNames = platform === 'windows' |
| 559 | ? [WINDOWS_BINARY_NAME] |
| 560 | : [binName]; |
| 561 | const archiveNames = platform === 'windows' |
| 562 | ? WINDOWS_ARCHIVE_NAMES |
| 563 | : UNIX_ARCHIVE_NAMES; |
| 564 | const cachePaths = cacheNames.map((name) => path.join(BIN_DIR, name)); |
| 565 | |
| 566 | if (platform === 'windows' && windowsBinaryReady(BIN_DIR)) return; |
| 567 | if (platform !== 'windows' && |
| 568 | cachePaths.every((candidate) => fs.existsSync(candidate))) { |
| 569 | try { |
| 570 | verifyCandidate(binPath); |
| 571 | return; // already installed and runnable, nothing to do |
| 572 | } catch (_) { |
| 573 | // Fall through and atomically replace the invalid binary. |
| 574 | } |
| 575 | } |
| 576 | |
| 577 | fs.mkdirSync(BIN_DIR, { recursive: true }); |
| 578 | |
| 579 | // Linux ships a fully-static "-portable" build; the standard linux binary |
| 580 | // dynamically links glibc 2.38+ and fails on older distros. macOS/Windows |
| 581 | // have no such variant. Keep in sync with install.sh / pypi _cli.py / cli.c. |
| 582 | const variant = platform === 'linux' ? '-portable' : ''; |
| 583 | // Opt into the UI build (embedded graph visualization) with CBM_VARIANT=ui. |
| 584 | // Default is the standard (headless) build. Mirrors install.sh --ui. |
| 585 | const ui = (process.env.CBM_VARIANT || '').toLowerCase() === 'ui' ? 'ui-' : ''; |
| 586 | const archive = `codebase-memory-mcp-${ui}${platform}-${arch}${variant}.${ext}`; |
| 587 | const url = `https://github.com/${REPO}/releases/download/v${VERSION}/${archive}`; |
| 588 | |
| 589 | const uiLabel = ui ? '(ui) ' : ''; |
| 590 | process.stdout.write(`codebase-memory-mcp: downloading v${VERSION} ${uiLabel}for ${platform}/${arch}...\n`); |
| 591 | |
| 592 | const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cbm-install-')); |
| 593 | const tmpArchive = path.join(tmpDir, `cbm.${ext}`); |
| 594 | |
| 595 | try { |
| 596 | await download(url, tmpArchive); |
| 597 | await verifyChecksum(tmpArchive, archive); |
| 598 | |
| 599 | // Validate the complete archive namespace, then extract only executables. |
| 600 | if (ext === 'tar.gz') { |
| 601 | extractExactTarArchive( |
| 602 | tmpArchive, tmpDir, archiveNames, binName, |
no test coverage detected