diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f04c7ce1..2937a926 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -304,6 +304,8 @@ jobs: node -e 'console.log("ADAPTER_JAVA_VERSION="+require("./packages/adapter-java/package.json").version)' >> $GITHUB_ENV node -e 'console.log("ADAPTER_DOTNET_VERSION="+require("./packages/adapter-dotnet/package.json").version)' >> $GITHUB_ENV node -e 'console.log("CLI_VERSION="+require("./packages/mcp-debugger/package.json").version)' >> $GITHUB_ENV + # CodeLLDB platform packages are versioned by the vendored CodeLLDB release (issue #383) + node -e 'console.log("CODELLDB_PKG_VERSION="+require("./packages/codelldb-common/vendor-manifest.json").codelldb.version)' >> $GITHUB_ENV - name: Set npm dist-tag run: | if [[ "${CLI_VERSION}" == *"-beta"* ]] || [[ "${CLI_VERSION}" == *"-alpha"* ]]; then @@ -317,6 +319,17 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Downloads the digest-pinned VSIXs for all five platforms (the build above + # vendored linux-x64 only under CI=true) and copies the payloads into the + # @debugmcp/codelldb-* platform packages. Verification runs in a SEPARATE + # process: a vendoring process that died silently (issue #389) cannot + # vouch for itself, and --verify-only fails the job if any package would + # pack without its binaries (issue #383). + - name: Stage CodeLLDB platform packages + run: | + node scripts/stage-codelldb-packages.mjs + node scripts/stage-codelldb-packages.mjs --verify-only + # npm (unlike pnpm) does not rewrite workspace:* at publish time; without # this step the published adapter packages carry an uninstallable # "workspace:*" dependency on @debugmcp/shared (EUNSUPPORTEDPROTOCOL). @@ -335,6 +348,46 @@ jobs: npm pack --dry-run -w @debugmcp/adapter-java npm pack --dry-run -w @debugmcp/adapter-dotnet npm pack --dry-run -w @debugmcp/mcp-debugger + npm pack --dry-run -w @debugmcp/codelldb-win32-x64 + npm pack --dry-run -w @debugmcp/codelldb-darwin-x64 + npm pack --dry-run -w @debugmcp/codelldb-darwin-arm64 + npm pack --dry-run -w @debugmcp/codelldb-linux-x64 + npm pack --dry-run -w @debugmcp/codelldb-linux-arm64 + + # CodeLLDB platform packages (issue #383). Published BEFORE the CLI package + # so its optionalDependencies always resolve on the registry. Versioned by + # the CodeLLDB release and tagged latest explicitly (they are shared across + # beta/latest channels), so re-releases at an unchanged pin are skipped by + # the npm-view guards. First publish must use the token flow; once the five + # exist, configure trusted publishers and move them into the OIDC step. + - name: Publish CodeLLDB platform packages to npm (token, first publish) + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + # trap (not a trailing rm) so the token file cannot survive a + # mid-loop publish failure on the runner. + trap 'rm -f "$HOME/.npmrc"' EXIT + echo "//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}" > "$HOME/.npmrc" + + # "already exists" is the NORMAL path here (every re-release at an + # unchanged CodeLLDB pin walks it), so the guard must distinguish a + # true 404 from a transient registry error - publishing over an + # existing version would otherwise kill the job before the OIDC step. + for pkg in codelldb-win32-x64 codelldb-darwin-x64 codelldb-darwin-arm64 codelldb-linux-x64 codelldb-linux-arm64; do + set +e + VIEW_OUT=$(npm view @debugmcp/${pkg}@${CODELLDB_PKG_VERSION} version 2>&1) + VIEW_CODE=$? + set -e + if [ "$VIEW_CODE" -eq 0 ]; then + echo "@debugmcp/${pkg}@${CODELLDB_PKG_VERSION} already exists, skipping" + elif echo "$VIEW_OUT" | grep -q "E404"; then + npm publish -w @debugmcp/${pkg} --access public --provenance --tag latest + else + echo "npm view for @debugmcp/${pkg}@${CODELLDB_PKG_VERSION} failed with a non-404 error; refusing to guess:" + echo "$VIEW_OUT" + exit 1 + fi + done # Existing packages publish via OIDC trusted publishing: no token anywhere, # npm mints a short-lived credential from the workflow's OIDC identity and @@ -423,6 +476,11 @@ jobs: npm pack -w @debugmcp/adapter-java --pack-destination release-artifacts npm pack -w @debugmcp/adapter-dotnet --pack-destination release-artifacts npm pack -w @debugmcp/mcp-debugger --pack-destination release-artifacts + npm pack -w @debugmcp/codelldb-win32-x64 --pack-destination release-artifacts + npm pack -w @debugmcp/codelldb-darwin-x64 --pack-destination release-artifacts + npm pack -w @debugmcp/codelldb-darwin-arm64 --pack-destination release-artifacts + npm pack -w @debugmcp/codelldb-linux-x64 --pack-destination release-artifacts + npm pack -w @debugmcp/codelldb-linux-arm64 --pack-destination release-artifacts - name: Upload release artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ef4b1ae..8b517242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- **CodeLLDB ships as per-platform npm packages (esbuild pattern)** — five new packages `@debugmcp/codelldb-{win32-x64,darwin-x64,darwin-arm64,linux-x64,linux-arm64}` (versioned by the CodeLLDB release, currently 1.11.8, payload staged from the digest-pinned VSIXs) are `optionalDependencies` of `@debugmcp/mcp-debugger`, so npm installs exactly the one matching your os/cpu. Rust and C/C++ debugging now work out of the box on every platform npm serves — previously the CLI tarball bundled linux-x64 only — and the core tarball shrinks from ~54 MB to a few MB. The resolver probes the installed platform package last — after the vendor tree and after `CODELLDB_PATH` — so an explicit `CODELLDB_PATH` still overrides the auto-installed package, and installs with `--omit=optional` keep working via `CODELLDB_PATH` (#383) + ### Fixed - **vendor-codelldb.js can no longer die silently with exit 0** — the script-level root cause behind #389 (the Docker workaround shipped in v0.24.2 stands): a stalled extract-zip promise drained the event loop and Node exited 0 with no failure output. Extraction now runs under a watchdog (default 120 s, `CODELLDB_EXTRACT_TIMEOUT_MS`) whose pending timer keeps the event loop alive and converts a stall into a normal retry/failure, and a premature-exit guard forces exit code 1 with a requested/completed/unresolved-platforms diagnostic if the process would otherwise exit 0 before vendoring finished (#389) diff --git a/README.md b/README.md index e039a0f0..6912af30 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ The server also serves condensed guidance in-band: MCP `instructions` on connect > **Requirements:** Node.js 22+ for the server. Each language you debug also needs its own toolchain installed (Python + debugpy, Ruby + the `debug` gem / `rdbg`, Node.js, Go + Delve, JDK 21+, .NET SDK, the Rust toolchain, or a C/C++ compiler — g++/clang++, only needed for source-file launch). > -> **CodeLLDB platform note (npx/npm installs):** the npm package bundles the CodeLLDB debug engine for **linux-x64 only**, so Rust and C/C++ debugging work out of the box on Linux (CI, containers, cloud sandboxes). On Windows/macOS set `CODELLDB_PATH` to a [CodeLLDB release](https://github.com/vadimcn/codelldb/releases) binary, build from source (vendors your platform automatically), or use the Docker image. +> **CodeLLDB platform note (npx/npm installs):** the CodeLLDB debug engine ships as per-platform optional dependencies (`@debugmcp/codelldb-win32-x64`, `-darwin-x64`, `-darwin-arm64`, `-linux-x64`, `-linux-arm64`) — npm installs exactly the one matching your platform, so Rust and C/C++ debugging work out of the box everywhere npm serves. If you install with `--omit=optional`, set `CODELLDB_PATH` to a [CodeLLDB release](https://github.com/vadimcn/codelldb/releases) binary instead, or use the Docker image. ### For MCP Clients (Claude Desktop, etc.) diff --git a/SUPPLY-CHAIN-SECURITY.md b/SUPPLY-CHAIN-SECURITY.md index 726be014..6916e5d2 100644 --- a/SUPPLY-CHAIN-SECURITY.md +++ b/SUPPLY-CHAIN-SECURITY.md @@ -37,7 +37,7 @@ Two upstream debug engines are vendored (as prebuilt artifacts) into distributed | Component | Upstream | License | Pin manifest | Embedded in | |-----------|----------|---------|--------------|-------------| | js-debug (VS Code JavaScript debugger, DAP server build) | [microsoft/vscode-js-debug](https://github.com/microsoft/vscode-js-debug) | MIT | [`packages/adapter-javascript/vendor-manifest.json`](packages/adapter-javascript/vendor-manifest.json) | `@debugmcp/adapter-javascript`, `@debugmcp/mcp-debugger` CLI bundle, Docker image | -| CodeLLDB (LLDB-based DAP adapter) | [vadimcn/codelldb](https://github.com/vadimcn/codelldb) | MIT | [`packages/codelldb-common/vendor-manifest.json`](packages/codelldb-common/vendor-manifest.json) | `@debugmcp/mcp-debugger` CLI bundle (linux-x64), Docker image | +| CodeLLDB (LLDB-based DAP adapter) | [vadimcn/codelldb](https://github.com/vadimcn/codelldb) | MIT | [`packages/codelldb-common/vendor-manifest.json`](packages/codelldb-common/vendor-manifest.json) | `@debugmcp/codelldb-{win32-x64,darwin-x64,darwin-arm64,linux-x64,linux-arm64}` platform packages (optionalDependencies of `@debugmcp/mcp-debugger`, versioned by the CodeLLDB release, payload staged from the digest-pinned VSIXs by `scripts/stage-codelldb-packages.mjs`), Docker image | The manifests pin the upstream version, the SHA-256 of each release asset, and (for js-debug) the digest of the derived server file. Overriding the pinned version requires an explicit `*_ALLOW_UNPINNED=true` escape hatch intended for local experiments only; release builds always verify against the committed digests. Version bumps are deliberate PRs that update the manifest (the bump procedure is documented inside each manifest file). diff --git a/docs/cpp/README.md b/docs/cpp/README.md index 03bf777a..c3bcc681 100644 --- a/docs/cpp/README.md +++ b/docs/cpp/README.md @@ -10,7 +10,7 @@ MCP Client → mcp-debugger → proxy worker → CodeLLDB (vendored) → LLDB - CodeLLDB binaries are vendored in `packages/codelldb-common/vendor/codelldb//` (shared with the Rust adapter — one ~150 MB copy per platform, downloaded automatically during `pnpm install`). - The adapter spawns CodeLLDB in TCP mode and speaks DAP to it. No system LLDB or gdb installation is required. -- `CODELLDB_PATH` env var overrides the vendored binary (e.g. npm users on macOS/Windows pointing at the VSCode extension's copy). +- npm/npx installs get CodeLLDB via the per-platform `@debugmcp/codelldb-*` optional dependencies; setting `CODELLDB_PATH` (e.g. to the VSCode extension's copy) overrides the installed platform package, though a repo/Docker vendored copy still wins over both. ## Prerequisites diff --git a/docs/release-checklist.md b/docs/release-checklist.md index 895b4299..83a66dfd 100644 --- a/docs/release-checklist.md +++ b/docs/release-checklist.md @@ -5,7 +5,7 @@ Pre-release validation for mcp-debugger. Run `npm run release:dry-run` to automa ## Before Tagging ### Automated (via `npm run release:dry-run`) -- [ ] Package versions match (the dry-run script checks root plus **all 12** workspace packages, including the private/bundle-only `adapter-rust`, `adapter-cpp`, and `codelldb-common`) +- [ ] Package versions match (the dry-run script checks root plus **all 17** workspace packages, including the private/bundle-only `adapter-rust`, `adapter-cpp`, and `codelldb-common`; the five `codelldb-` payload packages are checked against the **CodeLLDB pin**, not the repo version) - [ ] `CHANGELOG.md` has `[x.y.z] - YYYY-MM-DD` entry with date - [ ] `CHANGELOG.md` has empty `[Unreleased]` section at top - [ ] `npm run build` succeeds @@ -18,13 +18,14 @@ Pre-release validation for mcp-debugger. Run `npm run release:dry-run` to automa ### Manual - [ ] **npm trusted publishing configured** — every *previously published* `@debugmcp/*` package must have a trusted publisher at npmjs.com → package Settings → Trusted Publisher (GitHub Actions; org/user: `debugmcp`, repo: `mcp-debugger`, workflow: `release.yml`, environment: blank). These packages publish token-free via OIDC; a publish without this config fails (404/permission error) — configure, then re-run via workflow_dispatch. -- [ ] **First-time packages** — any package that has never been on npm publishes via the `NPM_TOKEN` step in `release.yml` this once. After the release: configure its trusted publisher, then move it from the token step into the OIDC step. When no first-publishes remain, delete the token step and the `NPM_TOKEN` secret. +- [ ] **First-time packages** — any package that has never been on npm publishes via the `NPM_TOKEN` step in `release.yml` this once. After the release: configure its trusted publisher, then move it from the token step into the OIDC step. When no first-publishes remain, delete the token step and the `NPM_TOKEN` secret. (The five `@debugmcp/codelldb-` packages have their own token step and follow the same dance after their first release.) - [ ] **Docker Hub credentials** — `DOCKER_USERNAME` and `DOCKER_PASSWORD` secrets are current - [ ] **PyPI token** — `PYPI_TOKEN` secret is current - [ ] `release.yml` default ref updated to current tag (for workflow_dispatch reruns) - [ ] All new adapters have their toolchain in `release.yml` **both** `build-and-test` and `npm-publish` jobs - [ ] New adapters intended for npm publishing have `publishConfig.access: "public"`, a `git+https` `repository.url` with `directory`, and appear in: `release.yml` (pack dry-run, publish, pack-artifacts) and `PUBLISHED_PKGS` in `scripts/release-dry-run.sh`. Bundle-only packages carry `"private": true`. - [ ] Vendored-engine pins current: `packages/adapter-javascript/vendor-manifest.json` and `packages/codelldb-common/vendor-manifest.json` match the versions you intend to ship (digest verification fails the build on drift) +- [ ] **CodeLLDB bump procedure** (when bumping the CodeLLDB pin): update `vendor-manifest.json` (version + all five VSIX digests), update `DEFAULT_CODELLDB_VERSION` in `packages/codelldb-common/src/codelldb-resolver.ts`, run `node scripts/sync-versions.cjs` (it writes the pin into the five `packages/codelldb-/package.json` versions automatically), re-vendor, and run the rust + cpp + codelldb-common test suites (drift guards enforce manifest ↔ resolver ↔ package versions stay in sync). The next release then republishes exactly the five platform packages; at an unchanged pin the npm-view guards skip them. - [ ] Contributors credited in CHANGELOG (check `git log --format="%an" | sort -u`) ## Common Failures diff --git a/docs/rust-debugging.md b/docs/rust-debugging.md index de36420f..f49a5ca2 100644 --- a/docs/rust-debugging.md +++ b/docs/rust-debugging.md @@ -19,7 +19,7 @@ npm run build:adapter # Downloads and extracts CodeLLDB ## CodeLLDB Vendoring -The Rust adapter bundles the CodeLLDB binaries into `packages/codelldb-common/vendor/codelldb`. Vendoring runs automatically when you install or build the workspace. To keep the published npm package within registry limits, the pre-built CLI ships only the Linux x64 CodeLLDB runtime. If you are on macOS or Windows, set the `CODELLDB_PATH` environment variable to your local CodeLLDB installation (for example from the VSCode extension) or run `pnpm --filter @debugmcp/codelldb-common run build:adapter` from a cloned repository to download your platform binaries. +The Rust adapter bundles the CodeLLDB binaries into `packages/codelldb-common/vendor/codelldb`. Vendoring runs automatically when you install or build the workspace. The published CLI installs CodeLLDB via per-platform `@debugmcp/codelldb-*` optional dependencies — npm picks the one matching your os/cpu, so no manual setup is needed. If you installed with `--omit=optional`, set the `CODELLDB_PATH` environment variable to a local CodeLLDB installation (for example from the VSCode extension) or run `pnpm --filter @debugmcp/codelldb-common run build:adapter` from a cloned repository to download your platform binaries. - `pnpm install` (postinstall hook) - `pnpm vendor` or `pnpm vendor:adapters` diff --git a/packages/adapter-cpp/src/cpp-adapter-factory.ts b/packages/adapter-cpp/src/cpp-adapter-factory.ts index 31704927..b93277a6 100644 --- a/packages/adapter-cpp/src/cpp-adapter-factory.ts +++ b/packages/adapter-cpp/src/cpp-adapter-factory.ts @@ -54,7 +54,7 @@ export class CppAdapterFactory implements IAdapterFactory { // Check CodeLLDB — the only hard requirement const resolvedCodelldb = await resolveCodeLLDBExecutable(); if (!resolvedCodelldb) { - errors.push('CodeLLDB not found. Run: npm run build:adapter'); + errors.push('CodeLLDB not found. It normally ships via the @debugmcp/codelldb-* optional dependencies; set CODELLDB_PATH, or in a repo checkout run: npm run build:adapter'); } else { codelldbPath = resolvedCodelldb; codelldbVersion = await getCodeLLDBVersion() || undefined; diff --git a/packages/adapter-cpp/src/cpp-debug-adapter.ts b/packages/adapter-cpp/src/cpp-debug-adapter.ts index 338ad07e..d7e8b746 100644 --- a/packages/adapter-cpp/src/cpp-debug-adapter.ts +++ b/packages/adapter-cpp/src/cpp-debug-adapter.ts @@ -204,7 +204,7 @@ export class CppDebugAdapter extends EventEmitter implements IDebugAdapter { if (!codelldbPath) { errors.push({ code: 'CODELLDB_NOT_FOUND', - message: 'CodeLLDB executable not found. Run: npm run build:adapter', + message: 'CodeLLDB executable not found. It normally ships via the @debugmcp/codelldb-* optional dependencies (reinstall without --omit=optional), or set CODELLDB_PATH, or in a repo checkout run: npm run build:adapter', recoverable: true }); } @@ -439,7 +439,7 @@ export class CppDebugAdapter extends EventEmitter implements IDebugAdapter { if (!resolvedPath) { throw new AdapterError( - 'CodeLLDB executable not found. Run: npm run build:adapter', + 'CodeLLDB executable not found. It normally ships via the @debugmcp/codelldb-* optional dependencies (reinstall without --omit=optional), or set CODELLDB_PATH, or in a repo checkout run: npm run build:adapter', AdapterErrorCode.ENVIRONMENT_INVALID ); } @@ -788,7 +788,7 @@ Install: const message = error.message.toLowerCase(); if (message.includes('codelldb') && message.includes('not found')) { - return 'CodeLLDB is not installed. Please run: npm run build:adapter'; + return 'CodeLLDB is not installed. It normally ships via the @debugmcp/codelldb-* optional dependencies; set CODELLDB_PATH, or in a repo checkout run: npm run build:adapter'; } if (message.includes('compiler') && message.includes('not found')) { diff --git a/packages/adapter-rust/src/rust-adapter-factory.ts b/packages/adapter-rust/src/rust-adapter-factory.ts index f293cd94..d2870211 100644 --- a/packages/adapter-rust/src/rust-adapter-factory.ts +++ b/packages/adapter-rust/src/rust-adapter-factory.ts @@ -55,7 +55,7 @@ export class RustAdapterFactory implements IAdapterFactory { // Check CodeLLDB const resolvedCodelldb = await resolveCodeLLDBExecutable(); if (!resolvedCodelldb) { - errors.push('CodeLLDB not found. Run: npm run build:adapter'); + errors.push('CodeLLDB not found. It normally ships via the @debugmcp/codelldb-* optional dependencies; set CODELLDB_PATH, or in a repo checkout run: npm run build:adapter'); } else { codelldbPath = resolvedCodelldb; codelldbVersion = await getCodeLLDBVersion() || undefined; diff --git a/packages/adapter-rust/src/rust-debug-adapter.ts b/packages/adapter-rust/src/rust-debug-adapter.ts index 2a69c7c6..c786fa2e 100644 --- a/packages/adapter-rust/src/rust-debug-adapter.ts +++ b/packages/adapter-rust/src/rust-debug-adapter.ts @@ -209,7 +209,7 @@ export class RustDebugAdapter extends EventEmitter implements IDebugAdapter { if (!codelldbPath) { errors.push({ code: 'CODELLDB_NOT_FOUND', - message: 'CodeLLDB executable not found. Run: npm run build:adapter', + message: 'CodeLLDB executable not found. It normally ships via the @debugmcp/codelldb-* optional dependencies (reinstall without --omit=optional), or set CODELLDB_PATH, or in a repo checkout run: npm run build:adapter', recoverable: true }); } @@ -579,7 +579,7 @@ export class RustDebugAdapter extends EventEmitter implements IDebugAdapter { if (!resolvedPath) { throw new AdapterError( - 'CodeLLDB executable not found. Run: npm run build:adapter', + 'CodeLLDB executable not found. It normally ships via the @debugmcp/codelldb-* optional dependencies (reinstall without --omit=optional), or set CODELLDB_PATH, or in a repo checkout run: npm run build:adapter', AdapterErrorCode.ENVIRONMENT_INVALID ); } @@ -948,7 +948,7 @@ You can also specify the Rust executable path explicitly in your debug configura const message = error.message.toLowerCase(); if (message.includes('codelldb') && message.includes('not found')) { - return 'CodeLLDB is not installed. Please run: npm run build:adapter in packages/adapter-rust/'; + return 'CodeLLDB is not installed. It normally ships via the @debugmcp/codelldb-* optional dependencies; set CODELLDB_PATH, or in a repo checkout run: npm run build:adapter'; } if (message.includes('cargo') && message.includes('not found')) { diff --git a/packages/codelldb-common/src/codelldb-resolver.ts b/packages/codelldb-common/src/codelldb-resolver.ts index 8639a0a5..669f42dd 100644 --- a/packages/codelldb-common/src/codelldb-resolver.ts +++ b/packages/codelldb-common/src/codelldb-resolver.ts @@ -10,9 +10,11 @@ import * as fs from 'fs/promises'; import { constants as fsConstants, existsSync } from 'fs'; import * as path from 'path'; import { fileURLToPath } from 'url'; +import { createRequire } from 'module'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); +const nodeRequire = createRequire(import.meta.url); /** Keep in sync with the pinned version in vendor-manifest.json (the vendor script's default) */ export const DEFAULT_CODELLDB_VERSION = '1.11.8'; @@ -85,6 +87,46 @@ function defaultPackageRoot(): string { return path.resolve(__dirname, '..'); } +/** npm package name for a per-platform CodeLLDB binary payload (issue #383). */ +export function getCodeLLDBPlatformPackageName(platformDir: string): string { + return `@debugmcp/codelldb-${platformDir}`; +} + +/** + * Root directory of the installed @debugmcp/codelldb- platform package, + * or null when it is not installed. These packages are optionalDependencies + * of @debugmcp/mcp-debugger (npm installs exactly the os/cpu match), so this + * resolves in npx/global installs. Resolving from codelldb-common inside the + * monorepo fails (it declares no platform-package dependency, drift-guarded); + * workspace-aware contexts (the bundled CLI's install root, pnpm NODE_PATH) + * may resolve the payload-less workspace shells, which is harmless because + * the resolvers probe this candidate LAST and its executable does not exist. + * `resolvePkg` is injectable so tests stay hermetic; the default path is + * memoized per platformDir (the answer cannot change within a process, and + * a failed require.resolve walks node_modules up to the filesystem root). + */ +const platformPackageRootMemo = new Map(); + +export function resolveCodeLLDBPlatformPackageRoot( + platformDir: string, + resolvePkg?: (id: string) => string +): string | null { + if (resolvePkg === undefined && platformPackageRootMemo.has(platformDir)) { + return platformPackageRootMemo.get(platformDir) ?? null; + } + const resolve = resolvePkg ?? ((id: string) => nodeRequire.resolve(id)); + let root: string | null; + try { + root = path.dirname(resolve(`${getCodeLLDBPlatformPackageName(platformDir)}/package.json`)); + } catch { + root = null; + } + if (resolvePkg === undefined) { + platformPackageRootMemo.set(platformDir, root); + } + return root; +} + /** * Synchronous resolver core. Platform/arch default to the live process values * READ AT CALL TIME (tests stub the process global; RustDebugAdapter injects @@ -96,11 +138,14 @@ export function resolveCodeLLDBExecutableSyncImpl(options?: { arch?: string; packageRoot?: string; exists?: (p: string) => boolean; + resolvePlatformPackageRoot?: (platformDir: string) => string | null; }): string | null { const platform = options?.platform ?? process.platform; const arch = options?.arch ?? process.arch; const packageRoot = options?.packageRoot ?? defaultPackageRoot(); const exists = options?.exists ?? existsSync; + const resolvePlatformPackageRoot = + options?.resolvePlatformPackageRoot ?? resolveCodeLLDBPlatformPackageRoot; const platformDir = getCodeLLDBPlatformDir(platform, arch); if (!platformDir) { @@ -124,7 +169,9 @@ export function resolveCodeLLDBExecutableSyncImpl(options?: { } } - // Check environment variable as fallback (after vendored candidates) + // Explicit CODELLDB_PATH beats the auto-installed platform package (it is + // deliberate user configuration) but not a vendored copy — the pre-existing + // documented order for dev checkouts and the Docker image. if (process.env.CODELLDB_PATH) { try { if (exists(process.env.CODELLDB_PATH)) { @@ -135,13 +182,33 @@ export function resolveCodeLLDBExecutableSyncImpl(options?: { } } + // Last resort: the installed @debugmcp/codelldb- platform package + // (npx/global installs). Resolved lazily — the require walk only runs when + // everything above missed. + const platformPackageRoot = resolvePlatformPackageRoot(platformDir); + if (platformPackageRoot) { + const candidate = path.join(platformPackageRoot, 'adapter', getCodeLLDBExecutableName(platform)); + try { + if (exists(candidate)) { + return candidate; + } + } catch { + // Fall through + } + } + return null; } /** * Resolve the CodeLLDB executable path based on platform */ -export async function resolveCodeLLDBExecutable(): Promise { +export async function resolveCodeLLDBExecutable(options?: { + resolvePlatformPackageRoot?: (platformDir: string) => string | null; +}): Promise { + const resolvePlatformPackageRoot = + options?.resolvePlatformPackageRoot ?? resolveCodeLLDBPlatformPackageRoot; + const platformDir = getCodeLLDBPlatformDir(process.platform, process.arch); if (!platformDir) { return null; @@ -163,7 +230,9 @@ export async function resolveCodeLLDBExecutable(): Promise { } } - // Check environment variable as fallback + // Explicit CODELLDB_PATH beats the auto-installed platform package (it is + // deliberate user configuration) but not a vendored copy — same order as + // the sync impl above. if (process.env.CODELLDB_PATH) { try { await fs.access(process.env.CODELLDB_PATH, fsConstants.F_OK); @@ -173,14 +242,32 @@ export async function resolveCodeLLDBExecutable(): Promise { } } + // Last resort: the installed @debugmcp/codelldb- platform package + // (npx/global installs). Resolved lazily — the require walk only runs when + // everything above missed. + const platformPackageRoot = resolvePlatformPackageRoot(platformDir); + if (platformPackageRoot) { + const candidate = path.join(platformPackageRoot, 'adapter', getCodeLLDBExecutableName(process.platform)); + try { + await fs.access(candidate, fsConstants.F_OK); + return candidate; + } catch { + // Fall through + } + } + return null; } /** * Check if CodeLLDB is installed and get version */ -export async function getCodeLLDBVersion(): Promise { - const codelldbPath = await resolveCodeLLDBExecutable(); +export async function getCodeLLDBVersion(options?: { + resolvePlatformPackageRoot?: (platformDir: string) => string | null; +}): Promise { + const resolvePlatformPackageRoot = + options?.resolvePlatformPackageRoot ?? resolveCodeLLDBPlatformPackageRoot; + const codelldbPath = await resolveCodeLLDBExecutable({ resolvePlatformPackageRoot }); if (!codelldbPath) { return null; @@ -191,11 +278,19 @@ export async function getCodeLLDBVersion(): Promise { return DEFAULT_CODELLDB_VERSION; } - const versionFileCandidates = buildVendorCandidatePaths( - defaultPackageRoot(), - platformDir, - 'version.json' - ); + // The version.json that describes the binary we actually resolved sits + // beside it (/adapter/codelldb -> /version.json) — probe that + // first so the reported version cannot be misattributed to a different + // install (e.g. a stale vendor tree under the cwd fallback). + const versionFileCandidates = [ + path.resolve(path.dirname(codelldbPath), '..', 'version.json'), + ...buildVendorCandidatePaths(defaultPackageRoot(), platformDir, 'version.json') + ]; + + const platformPackageRoot = resolvePlatformPackageRoot(platformDir); + if (platformPackageRoot) { + versionFileCandidates.push(path.join(platformPackageRoot, 'version.json')); + } for (const versionFile of versionFileCandidates) { try { diff --git a/packages/codelldb-common/src/index.ts b/packages/codelldb-common/src/index.ts index ec85db43..37158c05 100644 --- a/packages/codelldb-common/src/index.ts +++ b/packages/codelldb-common/src/index.ts @@ -12,6 +12,8 @@ export { SUPPORTED_CODELLDB_PLATFORM_DIRS, getCodeLLDBPlatformDir, getCodeLLDBExecutableName, + getCodeLLDBPlatformPackageName, + resolveCodeLLDBPlatformPackageRoot, buildVendorCandidatePaths, resolveCodeLLDBExecutableSyncImpl, resolveCodeLLDBExecutable, diff --git a/packages/codelldb-common/tests/codelldb-platform-packages.test.ts b/packages/codelldb-common/tests/codelldb-platform-packages.test.ts new file mode 100644 index 00000000..fe9f7088 --- /dev/null +++ b/packages/codelldb-common/tests/codelldb-platform-packages.test.ts @@ -0,0 +1,139 @@ +/** + * Drift guards for the five @debugmcp/codelldb- packages (issue #383). + * + * These packages are binary payload shells: their committed content is only + * package.json/README/LICENSE/.gitignore, with the CodeLLDB payload staged at + * pack time by scripts/stage-codelldb-packages.mjs. The guards pin the + * invariants the release pipeline and the resolver depend on — including the + * hardcoded five-platform lists in release.yml and release-dry-run.sh, which + * nothing else ties to SUPPORTED_CODELLDB_PLATFORM_DIRS. + */ +import { describe, it, expect } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { + SUPPORTED_CODELLDB_PLATFORM_DIRS, + getCodeLLDBPlatformDir +} from '../src/codelldb-resolver.js'; + +interface PlatformPackageJson { + name: string; + version: string; + os: string[]; + cpu: string[]; + files: string[]; + exports?: unknown; + scripts?: Record; + dependencies?: Record; + publishConfig?: { access?: string }; + repository?: { url?: string; directory?: string }; +} + +const manifest = JSON.parse( + readFileSync(new URL('../vendor-manifest.json', import.meta.url), 'utf-8') +) as { codelldb: { version: string } }; + +const EXPECTED_OS_CPU: Record = { + // win32 allows arm64 too: no upstream win32-arm64 build exists, so + // Windows-on-ARM installs the x64 package and runs it under emulation. + 'win32-x64': { os: ['win32'], cpu: ['x64', 'arm64'] }, + 'darwin-x64': { os: ['darwin'], cpu: ['x64'] }, + 'darwin-arm64': { os: ['darwin'], cpu: ['arm64'] }, + 'linux-x64': { os: ['linux'], cpu: ['x64'] }, + 'linux-arm64': { os: ['linux'], cpu: ['arm64'] } +}; + +function readPlatformPackage(dir: string): PlatformPackageJson { + return JSON.parse( + readFileSync(new URL(`../../codelldb-${dir}/package.json`, import.meta.url), 'utf-8') + ) as PlatformPackageJson; +} + +/** All codelldb-- dirs a source text mentions. */ +function extractPlatformDirs(source: string): Set { + return new Set( + [...source.matchAll(/codelldb-((?:win32|darwin|linux)-[a-z0-9]+)/g)].map((m) => m[1]) + ); +} + +describe('codelldb platform packages', () => { + it.each([...SUPPORTED_CODELLDB_PLATFORM_DIRS])('%s package.json invariants', (dir) => { + const pkg = readPlatformPackage(dir); + + expect(pkg.name).toBe(`@debugmcp/codelldb-${dir}`); + // Versioned by CodeLLDB, not by the repo — sync-versions.cjs writes the + // pin into any workspace package declaring os/cpu. + expect(pkg.version).toBe(manifest.codelldb.version); + expect(pkg.os).toEqual(EXPECTED_OS_CPU[dir].os); + expect(pkg.cpu).toEqual(EXPECTED_OS_CPU[dir].cpu); + // The staged payload plus docs — nothing else may ship. + expect(pkg.files).toEqual(['adapter', 'lldb', 'lang_support', 'version.json', 'README.md', 'LICENSE']); + // Payload shells: no lifecycle scripts (postinstall must skip them), no + // deps, and no exports map — the resolver require.resolve()s + // '/package.json', which an exports field would break with + // ERR_PACKAGE_PATH_NOT_EXPORTED that the resolver swallows into null. + expect(pkg.scripts).toBeUndefined(); + expect(pkg.dependencies).toBeUndefined(); + expect(pkg.exports).toBeUndefined(); + expect(pkg.publishConfig?.access).toBe('public'); + // repository.directory is required for npm --provenance + expect(pkg.repository?.url).toBe('git+https://github.com/debugmcp/mcp-debugger.git'); + expect(pkg.repository?.directory).toBe(`packages/codelldb-${dir}`); + }); + + it('every declared os/cpu combination maps back to the package via the resolver', () => { + // npm's install selection (os/cpu fields) and the resolver's lookup + // (getCodeLLDBPlatformDir) must agree, or a platform gets the package + // installed but never resolved — e.g. win32/arm64 must map to win32-x64. + for (const dir of SUPPORTED_CODELLDB_PLATFORM_DIRS) { + const pkg = readPlatformPackage(dir); + for (const os of pkg.os) { + for (const cpu of pkg.cpu) { + expect( + getCodeLLDBPlatformDir(os as NodeJS.Platform, cpu), + `getCodeLLDBPlatformDir('${os}', '${cpu}')` + ).toBe(dir); + } + } + } + }); + + it('codelldb-common declares no platform-package dependency (resolver precedence)', () => { + // The platform-package candidate must stay the LAST resort: the vendor + // tree and CODELLDB_PATH win in dev/Docker because resolution from + // codelldb-common finds no platform package (only workspace-aware + // contexts see the payload-less shells, whose executable never exists). + const pkg = JSON.parse( + readFileSync(new URL('../package.json', import.meta.url), 'utf-8') + ) as { dependencies?: Record; devDependencies?: Record }; + const allDeps = { ...pkg.dependencies, ...pkg.devDependencies }; + for (const dep of Object.keys(allDeps)) { + expect(dep).not.toMatch(/^@debugmcp\/codelldb-(win32|darwin|linux)-/); + } + }); + + it('release.yml covers exactly the supported platforms (pack, publish, provenance)', () => { + const yml = readFileSync(new URL('../../../.github/workflows/release.yml', import.meta.url), 'utf-8'); + expect(extractPlatformDirs(yml)).toEqual(new Set(SUPPORTED_CODELLDB_PLATFORM_DIRS)); + for (const dir of SUPPORTED_CODELLDB_PLATFORM_DIRS) { + // dry-run pack + publish loop + provenance pack + const occurrences = yml.split(`codelldb-${dir}`).length - 1; + expect(occurrences, `release.yml mentions of codelldb-${dir}`).toBeGreaterThanOrEqual(3); + } + }); + + it('release-dry-run.sh covers exactly the supported platforms', () => { + const sh = readFileSync(new URL('../../../scripts/release-dry-run.sh', import.meta.url), 'utf-8'); + expect(extractPlatformDirs(sh)).toEqual(new Set(SUPPORTED_CODELLDB_PLATFORM_DIRS)); + }); + + it('@debugmcp/mcp-debugger lists all five platform packages as optionalDependencies', () => { + const pkg = JSON.parse( + readFileSync(new URL('../../mcp-debugger/package.json', import.meta.url), 'utf-8') + ) as { optionalDependencies?: Record }; + const expected = [...SUPPORTED_CODELLDB_PLATFORM_DIRS].map((d) => `@debugmcp/codelldb-${d}`).sort(); + expect(Object.keys(pkg.optionalDependencies ?? {}).sort()).toEqual(expected); + for (const spec of Object.values(pkg.optionalDependencies ?? {})) { + expect(spec).toBe('workspace:*'); + } + }); +}); diff --git a/packages/codelldb-common/tests/codelldb-resolver.test.ts b/packages/codelldb-common/tests/codelldb-resolver.test.ts index 8ffb9b00..28fcd08c 100644 --- a/packages/codelldb-common/tests/codelldb-resolver.test.ts +++ b/packages/codelldb-common/tests/codelldb-resolver.test.ts @@ -22,6 +22,8 @@ import { DEFAULT_CODELLDB_VERSION, getCodeLLDBPlatformDir, getCodeLLDBExecutableName, + getCodeLLDBPlatformPackageName, + resolveCodeLLDBPlatformPackageRoot, SUPPORTED_CODELLDB_PLATFORM_DIRS, buildVendorCandidatePaths, resolveCodeLLDBExecutableSyncImpl @@ -57,6 +59,8 @@ describe('codelldb-resolver', () => { describe('resolveCodeLLDBExecutable', () => { describe.each([ { platform: 'win32', arch: 'x64', dir: 'win32-x64', exe: 'codelldb.exe' }, + // Windows-on-ARM installs the x64 package (no upstream arm64 build) + { platform: 'win32', arch: 'arm64', dir: 'win32-x64', exe: 'codelldb.exe' }, { platform: 'darwin', arch: 'x64', dir: 'darwin-x64', exe: 'codelldb' }, { platform: 'darwin', arch: 'arm64', dir: 'darwin-arm64', exe: 'codelldb' }, { platform: 'linux', arch: 'x64', dir: 'linux-x64', exe: 'codelldb' }, @@ -98,7 +102,9 @@ describe('codelldb-resolver', () => { stubPlatform('linux', 'x64'); accessMock.mockRejectedValue(new Error('ENOENT')); - await expect(resolveCodeLLDBExecutable()).resolves.toBeNull(); + await expect( + resolveCodeLLDBExecutable({ resolvePlatformPackageRoot: () => null }) + ).resolves.toBeNull(); expect(accessMock).toHaveBeenCalledTimes(4); const suffix = path.join('vendor', 'codelldb', 'linux-x64', 'adapter', 'codelldb'); @@ -114,7 +120,9 @@ describe('codelldb-resolver', () => { p === '/custom/codelldb' ? Promise.resolve() : Promise.reject(new Error('ENOENT')) ); - await expect(resolveCodeLLDBExecutable()).resolves.toBe('/custom/codelldb'); + await expect( + resolveCodeLLDBExecutable({ resolvePlatformPackageRoot: () => null }) + ).resolves.toBe('/custom/codelldb'); expect(accessMock).toHaveBeenCalledTimes(5); // 4 vendored candidates + env path }); @@ -123,7 +131,9 @@ describe('codelldb-resolver', () => { vi.stubEnv('CODELLDB_PATH', '/missing/codelldb'); accessMock.mockRejectedValue(new Error('ENOENT')); - await expect(resolveCodeLLDBExecutable()).resolves.toBeNull(); + await expect( + resolveCodeLLDBExecutable({ resolvePlatformPackageRoot: () => null }) + ).resolves.toBeNull(); expect(accessMock).toHaveBeenCalledTimes(5); }); }); @@ -164,8 +174,11 @@ describe('codelldb-resolver', () => { accessMock.mockResolvedValue(undefined); readFileMock.mockRejectedValue(new Error('ENOENT')); - await expect(getCodeLLDBVersion()).resolves.toBe(DEFAULT_CODELLDB_VERSION); - expect(readFileMock).toHaveBeenCalledTimes(4); + await expect( + getCodeLLDBVersion({ resolvePlatformPackageRoot: () => null }) + ).resolves.toBe(DEFAULT_CODELLDB_VERSION); + // Sibling-of-resolved-binary candidate + the 4 vendor candidates + expect(readFileMock).toHaveBeenCalledTimes(5); }); it('falls back to DEFAULT_CODELLDB_VERSION when version.json lacks a version field', async () => { @@ -291,7 +304,9 @@ describe('codelldb-resolver', () => { vi.stubEnv('CODELLDB_PATH', '/custom/sync/codelldb'); const exists = vi.fn((p: string) => p === '/custom/sync/codelldb'); - const result = resolveCodeLLDBExecutableSyncImpl({ platform: 'linux', arch: 'x64', exists }); + const result = resolveCodeLLDBExecutableSyncImpl({ + platform: 'linux', arch: 'x64', exists, resolvePlatformPackageRoot: () => null + }); expect(result).toBe('/custom/sync/codelldb'); expect(exists).toHaveBeenCalledTimes(5); @@ -301,7 +316,9 @@ describe('codelldb-resolver', () => { vi.stubEnv('CODELLDB_PATH', '/missing/codelldb'); const exists = vi.fn().mockReturnValue(false); - expect(resolveCodeLLDBExecutableSyncImpl({ platform: 'linux', arch: 'x64', exists })).toBeNull(); + expect(resolveCodeLLDBExecutableSyncImpl({ + platform: 'linux', arch: 'x64', exists, resolvePlatformPackageRoot: () => null + })).toBeNull(); expect(exists).toHaveBeenCalledTimes(5); }); @@ -315,6 +332,111 @@ describe('codelldb-resolver', () => { }); }); + describe('platform package resolution (issue #383)', () => { + const pkgRoot = path.resolve('/npx-install/node_modules/@debugmcp/codelldb-linux-x64'); + + it('getCodeLLDBPlatformPackageName maps a platform dir to the npm package name', () => { + expect(getCodeLLDBPlatformPackageName('linux-x64')).toBe('@debugmcp/codelldb-linux-x64'); + expect(getCodeLLDBPlatformPackageName('win32-x64')).toBe('@debugmcp/codelldb-win32-x64'); + }); + + it('resolveCodeLLDBPlatformPackageRoot returns the package dir on success and null on failure', () => { + const resolvePkg = vi.fn().mockReturnValue(path.join(pkgRoot, 'package.json')); + expect(resolveCodeLLDBPlatformPackageRoot('linux-x64', resolvePkg)).toBe(pkgRoot); + expect(resolvePkg).toHaveBeenCalledWith('@debugmcp/codelldb-linux-x64/package.json'); + + const failing = vi.fn(() => { + throw new Error('MODULE_NOT_FOUND'); + }); + expect(resolveCodeLLDBPlatformPackageRoot('linux-x64', failing)).toBeNull(); + }); + + it('sync: resolves the installed platform package after the vendor candidates miss', () => { + const expected = path.join(pkgRoot, 'adapter', 'codelldb'); + const exists = vi.fn((p: string) => p === expected); + + const result = resolveCodeLLDBExecutableSyncImpl({ + platform: 'linux', + arch: 'x64', + exists, + resolvePlatformPackageRoot: () => pkgRoot + }); + + expect(result).toBe(expected); + // 4 vendor candidates first, then the platform-package candidate + expect(exists).toHaveBeenCalledTimes(5); + expect(exists.mock.calls[4][0]).toBe(expected); + }); + + it('sync: the vendor tree wins without even resolving the platform package (lazy)', () => { + const exists = vi.fn().mockReturnValue(true); + const resolvePlatformPackageRoot = vi.fn().mockReturnValue(pkgRoot); + + const result = resolveCodeLLDBExecutableSyncImpl({ + platform: 'linux', + arch: 'x64', + exists, + resolvePlatformPackageRoot + }); + + expect(exists).toHaveBeenCalledTimes(1); + expect(result).not.toContain('@debugmcp'); + // Lazy: the require walk never runs when a vendor candidate hits. + expect(resolvePlatformPackageRoot).not.toHaveBeenCalled(); + }); + + it('sync: explicit CODELLDB_PATH beats the installed platform package', () => { + vi.stubEnv('CODELLDB_PATH', '/custom/pkg/codelldb'); + const packageBinary = path.join(pkgRoot, 'adapter', 'codelldb'); + const exists = vi.fn((p: string) => p === '/custom/pkg/codelldb' || p === packageBinary); + const resolvePlatformPackageRoot = vi.fn().mockReturnValue(pkgRoot); + + const result = resolveCodeLLDBExecutableSyncImpl({ + platform: 'linux', + arch: 'x64', + exists, + resolvePlatformPackageRoot + }); + + expect(result).toBe('/custom/pkg/codelldb'); + // 4 vendor misses + env hit; the platform package is never consulted. + expect(exists).toHaveBeenCalledTimes(5); + expect(resolvePlatformPackageRoot).not.toHaveBeenCalled(); + }); + + it('sync: platform package is the last resort when CODELLDB_PATH is set but missing', () => { + vi.stubEnv('CODELLDB_PATH', '/missing/custom/codelldb'); + const packageBinary = path.join(pkgRoot, 'adapter', 'codelldb'); + const exists = vi.fn((p: string) => p === packageBinary); + + const result = resolveCodeLLDBExecutableSyncImpl({ + platform: 'linux', + arch: 'x64', + exists, + resolvePlatformPackageRoot: () => pkgRoot + }); + + expect(result).toBe(packageBinary); + // 4 vendor + env + platform package + expect(exists).toHaveBeenCalledTimes(6); + expect(exists.mock.calls[5][0]).toBe(packageBinary); + }); + + it('async: resolves the installed platform package after the vendor candidates miss', async () => { + stubPlatform('linux', 'x64'); + const expected = path.join(pkgRoot, 'adapter', 'codelldb'); + accessMock.mockImplementation((p: string) => + p === expected ? Promise.resolve() : Promise.reject(new Error('ENOENT')) + ); + + await expect( + resolveCodeLLDBExecutable({ resolvePlatformPackageRoot: () => pkgRoot }) + ).resolves.toBe(expected); + expect(accessMock).toHaveBeenCalledTimes(5); + }); + + }); + describe('platform table drift guards', () => { it('matches the PLATFORMS keys in scripts/vendor-codelldb.js', () => { const source = readFileSync(new URL('../scripts/vendor-codelldb.js', import.meta.url), 'utf-8'); diff --git a/packages/codelldb-darwin-arm64/.gitignore b/packages/codelldb-darwin-arm64/.gitignore new file mode 100644 index 00000000..88fc673d --- /dev/null +++ b/packages/codelldb-darwin-arm64/.gitignore @@ -0,0 +1,5 @@ +# Binary payload staged by scripts/stage-codelldb-packages.mjs - never committed +adapter/ +lldb/ +lang_support/ +version.json diff --git a/packages/codelldb-darwin-arm64/LICENSE b/packages/codelldb-darwin-arm64/LICENSE new file mode 100644 index 00000000..ab7141a7 --- /dev/null +++ b/packages/codelldb-darwin-arm64/LICENSE @@ -0,0 +1,28 @@ +MIT License + +Copyright (c) 2025 Sycamore LLC and mcp-debugger contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +This package redistributes unmodified binaries from the CodeLLDB project +(https://github.com/vadimcn/codelldb), which is licensed under the MIT License, +and bundled LLDB/LLVM components licensed under Apache-2.0 WITH LLVM-exception. +See the upstream repository for the full third-party license texts. diff --git a/packages/codelldb-darwin-arm64/README.md b/packages/codelldb-darwin-arm64/README.md new file mode 100644 index 00000000..ae0b5187 --- /dev/null +++ b/packages/codelldb-darwin-arm64/README.md @@ -0,0 +1,22 @@ +# @debugmcp/codelldb-darwin-arm64 + +Prebuilt [CodeLLDB](https://github.com/vadimcn/codelldb) debug adapter binaries for **macOS arm64 (Apple Silicon)**. + +This package is an internal binary payload for [`@debugmcp/mcp-debugger`](https://www.npmjs.com/package/@debugmcp/mcp-debugger), which lists all five platform variants as `optionalDependencies` (the esbuild pattern) so npm installs exactly the one matching your platform. It enables Rust and C/C++ debugging out of the box. You should not need to depend on this package directly. + +## Contents + +``` +adapter/ codelldb executable + helper scripts +lldb/ liblldb + embedded Python runtime +lang_support/ language helpers (when shipped upstream) +version.json vendored CodeLLDB version marker +``` + +The payload is extracted unmodified from the upstream `codelldb-darwin-arm64.vsix` release asset, whose sha256 digest is pinned in the repository's `packages/codelldb-common/vendor-manifest.json`. + +## Licenses + +- CodeLLDB is MIT-licensed (© Vadim Chugunov). +- The bundled LLDB/LLVM components are licensed under Apache-2.0 WITH LLVM-exception. +- This package's own packaging files are MIT-licensed (see LICENSE). diff --git a/packages/codelldb-darwin-arm64/package.json b/packages/codelldb-darwin-arm64/package.json new file mode 100644 index 00000000..30e5758e --- /dev/null +++ b/packages/codelldb-darwin-arm64/package.json @@ -0,0 +1,30 @@ +{ + "name": "@debugmcp/codelldb-darwin-arm64", + "version": "1.11.8", + "description": "CodeLLDB debug adapter binaries for macOS arm64 (Apple Silicon), vendored for @debugmcp/mcp-debugger (Rust/C++ debugging)", + "repository": { + "type": "git", + "url": "git+https://github.com/debugmcp/mcp-debugger.git", + "directory": "packages/codelldb-darwin-arm64" + }, + "license": "MIT", + "os": [ + "darwin" + ], + "cpu": [ + "arm64" + ], + "preferUnplugged": true, + "files": [ + "adapter", + "lldb", + "lang_support", + "version.json", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "author": "Sycamore LLC (https://github.com/debugmcp)" +} diff --git a/packages/codelldb-darwin-x64/.gitignore b/packages/codelldb-darwin-x64/.gitignore new file mode 100644 index 00000000..88fc673d --- /dev/null +++ b/packages/codelldb-darwin-x64/.gitignore @@ -0,0 +1,5 @@ +# Binary payload staged by scripts/stage-codelldb-packages.mjs - never committed +adapter/ +lldb/ +lang_support/ +version.json diff --git a/packages/codelldb-darwin-x64/LICENSE b/packages/codelldb-darwin-x64/LICENSE new file mode 100644 index 00000000..ab7141a7 --- /dev/null +++ b/packages/codelldb-darwin-x64/LICENSE @@ -0,0 +1,28 @@ +MIT License + +Copyright (c) 2025 Sycamore LLC and mcp-debugger contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +This package redistributes unmodified binaries from the CodeLLDB project +(https://github.com/vadimcn/codelldb), which is licensed under the MIT License, +and bundled LLDB/LLVM components licensed under Apache-2.0 WITH LLVM-exception. +See the upstream repository for the full third-party license texts. diff --git a/packages/codelldb-darwin-x64/README.md b/packages/codelldb-darwin-x64/README.md new file mode 100644 index 00000000..167af583 --- /dev/null +++ b/packages/codelldb-darwin-x64/README.md @@ -0,0 +1,22 @@ +# @debugmcp/codelldb-darwin-x64 + +Prebuilt [CodeLLDB](https://github.com/vadimcn/codelldb) debug adapter binaries for **macOS x64 (Intel)**. + +This package is an internal binary payload for [`@debugmcp/mcp-debugger`](https://www.npmjs.com/package/@debugmcp/mcp-debugger), which lists all five platform variants as `optionalDependencies` (the esbuild pattern) so npm installs exactly the one matching your platform. It enables Rust and C/C++ debugging out of the box. You should not need to depend on this package directly. + +## Contents + +``` +adapter/ codelldb executable + helper scripts +lldb/ liblldb + embedded Python runtime +lang_support/ language helpers (when shipped upstream) +version.json vendored CodeLLDB version marker +``` + +The payload is extracted unmodified from the upstream `codelldb-darwin-x64.vsix` release asset, whose sha256 digest is pinned in the repository's `packages/codelldb-common/vendor-manifest.json`. + +## Licenses + +- CodeLLDB is MIT-licensed (© Vadim Chugunov). +- The bundled LLDB/LLVM components are licensed under Apache-2.0 WITH LLVM-exception. +- This package's own packaging files are MIT-licensed (see LICENSE). diff --git a/packages/codelldb-darwin-x64/package.json b/packages/codelldb-darwin-x64/package.json new file mode 100644 index 00000000..0cb23061 --- /dev/null +++ b/packages/codelldb-darwin-x64/package.json @@ -0,0 +1,30 @@ +{ + "name": "@debugmcp/codelldb-darwin-x64", + "version": "1.11.8", + "description": "CodeLLDB debug adapter binaries for macOS x64 (Intel), vendored for @debugmcp/mcp-debugger (Rust/C++ debugging)", + "repository": { + "type": "git", + "url": "git+https://github.com/debugmcp/mcp-debugger.git", + "directory": "packages/codelldb-darwin-x64" + }, + "license": "MIT", + "os": [ + "darwin" + ], + "cpu": [ + "x64" + ], + "preferUnplugged": true, + "files": [ + "adapter", + "lldb", + "lang_support", + "version.json", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "author": "Sycamore LLC (https://github.com/debugmcp)" +} diff --git a/packages/codelldb-linux-arm64/.gitignore b/packages/codelldb-linux-arm64/.gitignore new file mode 100644 index 00000000..88fc673d --- /dev/null +++ b/packages/codelldb-linux-arm64/.gitignore @@ -0,0 +1,5 @@ +# Binary payload staged by scripts/stage-codelldb-packages.mjs - never committed +adapter/ +lldb/ +lang_support/ +version.json diff --git a/packages/codelldb-linux-arm64/LICENSE b/packages/codelldb-linux-arm64/LICENSE new file mode 100644 index 00000000..ab7141a7 --- /dev/null +++ b/packages/codelldb-linux-arm64/LICENSE @@ -0,0 +1,28 @@ +MIT License + +Copyright (c) 2025 Sycamore LLC and mcp-debugger contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +This package redistributes unmodified binaries from the CodeLLDB project +(https://github.com/vadimcn/codelldb), which is licensed under the MIT License, +and bundled LLDB/LLVM components licensed under Apache-2.0 WITH LLVM-exception. +See the upstream repository for the full third-party license texts. diff --git a/packages/codelldb-linux-arm64/README.md b/packages/codelldb-linux-arm64/README.md new file mode 100644 index 00000000..b446c34e --- /dev/null +++ b/packages/codelldb-linux-arm64/README.md @@ -0,0 +1,22 @@ +# @debugmcp/codelldb-linux-arm64 + +Prebuilt [CodeLLDB](https://github.com/vadimcn/codelldb) debug adapter binaries for **Linux arm64**. + +This package is an internal binary payload for [`@debugmcp/mcp-debugger`](https://www.npmjs.com/package/@debugmcp/mcp-debugger), which lists all five platform variants as `optionalDependencies` (the esbuild pattern) so npm installs exactly the one matching your platform. It enables Rust and C/C++ debugging out of the box. You should not need to depend on this package directly. + +## Contents + +``` +adapter/ codelldb executable + helper scripts +lldb/ liblldb + embedded Python runtime +lang_support/ language helpers (when shipped upstream) +version.json vendored CodeLLDB version marker +``` + +The payload is extracted unmodified from the upstream `codelldb-linux-arm64.vsix` release asset, whose sha256 digest is pinned in the repository's `packages/codelldb-common/vendor-manifest.json`. + +## Licenses + +- CodeLLDB is MIT-licensed (© Vadim Chugunov). +- The bundled LLDB/LLVM components are licensed under Apache-2.0 WITH LLVM-exception. +- This package's own packaging files are MIT-licensed (see LICENSE). diff --git a/packages/codelldb-linux-arm64/package.json b/packages/codelldb-linux-arm64/package.json new file mode 100644 index 00000000..a29cee40 --- /dev/null +++ b/packages/codelldb-linux-arm64/package.json @@ -0,0 +1,30 @@ +{ + "name": "@debugmcp/codelldb-linux-arm64", + "version": "1.11.8", + "description": "CodeLLDB debug adapter binaries for Linux arm64, vendored for @debugmcp/mcp-debugger (Rust/C++ debugging)", + "repository": { + "type": "git", + "url": "git+https://github.com/debugmcp/mcp-debugger.git", + "directory": "packages/codelldb-linux-arm64" + }, + "license": "MIT", + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "preferUnplugged": true, + "files": [ + "adapter", + "lldb", + "lang_support", + "version.json", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "author": "Sycamore LLC (https://github.com/debugmcp)" +} diff --git a/packages/codelldb-linux-x64/.gitignore b/packages/codelldb-linux-x64/.gitignore new file mode 100644 index 00000000..88fc673d --- /dev/null +++ b/packages/codelldb-linux-x64/.gitignore @@ -0,0 +1,5 @@ +# Binary payload staged by scripts/stage-codelldb-packages.mjs - never committed +adapter/ +lldb/ +lang_support/ +version.json diff --git a/packages/codelldb-linux-x64/LICENSE b/packages/codelldb-linux-x64/LICENSE new file mode 100644 index 00000000..ab7141a7 --- /dev/null +++ b/packages/codelldb-linux-x64/LICENSE @@ -0,0 +1,28 @@ +MIT License + +Copyright (c) 2025 Sycamore LLC and mcp-debugger contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +This package redistributes unmodified binaries from the CodeLLDB project +(https://github.com/vadimcn/codelldb), which is licensed under the MIT License, +and bundled LLDB/LLVM components licensed under Apache-2.0 WITH LLVM-exception. +See the upstream repository for the full third-party license texts. diff --git a/packages/codelldb-linux-x64/README.md b/packages/codelldb-linux-x64/README.md new file mode 100644 index 00000000..b4398065 --- /dev/null +++ b/packages/codelldb-linux-x64/README.md @@ -0,0 +1,22 @@ +# @debugmcp/codelldb-linux-x64 + +Prebuilt [CodeLLDB](https://github.com/vadimcn/codelldb) debug adapter binaries for **Linux x64**. + +This package is an internal binary payload for [`@debugmcp/mcp-debugger`](https://www.npmjs.com/package/@debugmcp/mcp-debugger), which lists all five platform variants as `optionalDependencies` (the esbuild pattern) so npm installs exactly the one matching your platform. It enables Rust and C/C++ debugging out of the box. You should not need to depend on this package directly. + +## Contents + +``` +adapter/ codelldb executable + helper scripts +lldb/ liblldb + embedded Python runtime +lang_support/ language helpers (when shipped upstream) +version.json vendored CodeLLDB version marker +``` + +The payload is extracted unmodified from the upstream `codelldb-linux-x64.vsix` release asset, whose sha256 digest is pinned in the repository's `packages/codelldb-common/vendor-manifest.json`. + +## Licenses + +- CodeLLDB is MIT-licensed (© Vadim Chugunov). +- The bundled LLDB/LLVM components are licensed under Apache-2.0 WITH LLVM-exception. +- This package's own packaging files are MIT-licensed (see LICENSE). diff --git a/packages/codelldb-linux-x64/package.json b/packages/codelldb-linux-x64/package.json new file mode 100644 index 00000000..0b267a65 --- /dev/null +++ b/packages/codelldb-linux-x64/package.json @@ -0,0 +1,30 @@ +{ + "name": "@debugmcp/codelldb-linux-x64", + "version": "1.11.8", + "description": "CodeLLDB debug adapter binaries for Linux x64, vendored for @debugmcp/mcp-debugger (Rust/C++ debugging)", + "repository": { + "type": "git", + "url": "git+https://github.com/debugmcp/mcp-debugger.git", + "directory": "packages/codelldb-linux-x64" + }, + "license": "MIT", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "preferUnplugged": true, + "files": [ + "adapter", + "lldb", + "lang_support", + "version.json", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "author": "Sycamore LLC (https://github.com/debugmcp)" +} diff --git a/packages/codelldb-win32-x64/.gitignore b/packages/codelldb-win32-x64/.gitignore new file mode 100644 index 00000000..88fc673d --- /dev/null +++ b/packages/codelldb-win32-x64/.gitignore @@ -0,0 +1,5 @@ +# Binary payload staged by scripts/stage-codelldb-packages.mjs - never committed +adapter/ +lldb/ +lang_support/ +version.json diff --git a/packages/codelldb-win32-x64/LICENSE b/packages/codelldb-win32-x64/LICENSE new file mode 100644 index 00000000..ab7141a7 --- /dev/null +++ b/packages/codelldb-win32-x64/LICENSE @@ -0,0 +1,28 @@ +MIT License + +Copyright (c) 2025 Sycamore LLC and mcp-debugger contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +This package redistributes unmodified binaries from the CodeLLDB project +(https://github.com/vadimcn/codelldb), which is licensed under the MIT License, +and bundled LLDB/LLVM components licensed under Apache-2.0 WITH LLVM-exception. +See the upstream repository for the full third-party license texts. diff --git a/packages/codelldb-win32-x64/README.md b/packages/codelldb-win32-x64/README.md new file mode 100644 index 00000000..17faf484 --- /dev/null +++ b/packages/codelldb-win32-x64/README.md @@ -0,0 +1,24 @@ +# @debugmcp/codelldb-win32-x64 + +Prebuilt [CodeLLDB](https://github.com/vadimcn/codelldb) debug adapter binaries for **Windows x64**. + +This package is an internal binary payload for [`@debugmcp/mcp-debugger`](https://www.npmjs.com/package/@debugmcp/mcp-debugger), which lists all five platform variants as `optionalDependencies` (the esbuild pattern) so npm installs exactly the one matching your platform. It enables Rust and C/C++ debugging out of the box. You should not need to depend on this package directly. + +The `cpu` field also allows `arm64`: there is no upstream win32-arm64 CodeLLDB build, so Windows-on-ARM installs this x64 package and runs it under x64 emulation. + +## Contents + +``` +adapter/ codelldb executable + helper scripts +lldb/ liblldb + embedded Python runtime +lang_support/ language helpers (when shipped upstream) +version.json vendored CodeLLDB version marker +``` + +The payload is extracted unmodified from the upstream `codelldb-win32-x64.vsix` release asset, whose sha256 digest is pinned in the repository's `packages/codelldb-common/vendor-manifest.json`. + +## Licenses + +- CodeLLDB is MIT-licensed (© Vadim Chugunov). +- The bundled LLDB/LLVM components are licensed under Apache-2.0 WITH LLVM-exception. +- This package's own packaging files are MIT-licensed (see LICENSE). diff --git a/packages/codelldb-win32-x64/package.json b/packages/codelldb-win32-x64/package.json new file mode 100644 index 00000000..691529a5 --- /dev/null +++ b/packages/codelldb-win32-x64/package.json @@ -0,0 +1,31 @@ +{ + "name": "@debugmcp/codelldb-win32-x64", + "version": "1.11.8", + "description": "CodeLLDB debug adapter binaries for Windows x64, vendored for @debugmcp/mcp-debugger (Rust/C++ debugging)", + "repository": { + "type": "git", + "url": "git+https://github.com/debugmcp/mcp-debugger.git", + "directory": "packages/codelldb-win32-x64" + }, + "license": "MIT", + "os": [ + "win32" + ], + "cpu": [ + "x64", + "arm64" + ], + "preferUnplugged": true, + "files": [ + "adapter", + "lldb", + "lang_support", + "version.json", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "author": "Sycamore LLC (https://github.com/debugmcp)" +} diff --git a/packages/mcp-debugger/README.md b/packages/mcp-debugger/README.md index 240f488a..bf45f092 100644 --- a/packages/mcp-debugger/README.md +++ b/packages/mcp-debugger/README.md @@ -61,7 +61,7 @@ All language adapters are bundled into the CLI package. No separate installation - **.NET**: netcoredbg + a compatible .NET runtime (Portable PDBs) - **C/C++**: none for prebuilt binaries (CodeLLDB vendored); a compiler (g++/clang++) only for lone-source-file launch -> **CodeLLDB platform note:** this npm package bundles CodeLLDB for **linux-x64 only** — Rust and C/C++ debugging work out of the box on Linux (CI, containers, cloud sandboxes). On Windows/macOS, point `CODELLDB_PATH` at a [CodeLLDB](https://github.com/vadimcn/codelldb/releases) binary for your platform, or use the Docker image. +> **CodeLLDB platform note:** the CodeLLDB debug engine ships via per-platform optional dependencies (`@debugmcp/codelldb-`) — npm installs exactly the one matching your os/cpu, so Rust and C/C++ debugging work out of the box on Windows, macOS, and Linux. Installs with `--omit=optional` skip it; point `CODELLDB_PATH` at a [CodeLLDB](https://github.com/vadimcn/codelldb/releases) binary instead, or use the Docker image. **Attach without a toolchain:** direct-connect attach modes (Python `debugpy --listen`, Ruby `rdbg --open`, Java JDWP) need no local language toolchain — the debug engine runs inside the target. `list_supported_languages` reports per-mode availability with reasons. @@ -72,7 +72,7 @@ All language adapters are bundled into the CLI package. No separate installation | `DEBUG_MCP_NO_REDACT=1` | Disable default-on secret redaction in variable/evaluate/output results | | `DEBUG_MCP_VARIABLE_ACCESS=explicit` | Least-privilege mode: `get_variables` requires explicit `names: [...]` | | `DEBUG_MCP_BP_ADDRESSING=line\|assert\|content` | Breakpoint addressing mode (default `content`: statement anchors + content assertions) | -| `CODELLDB_PATH` | Path to a CodeLLDB binary for platforms not bundled (see note above) | +| `CODELLDB_PATH` | Path to a CodeLLDB binary when the platform packages are unavailable (see note above) | | `CPP_MSVC_BEHAVIOR=warn\|error\|continue` | What to do when a C/C++ target looks MSVC-built (partial PDB fidelity) | | `MCP_HTTP_STALE_SESSION_MS` | HTTP mode: reap crash-abandoned MCP sessions after this idle time (default 30 min) | diff --git a/packages/mcp-debugger/package.json b/packages/mcp-debugger/package.json index 60cae89d..633e977b 100644 --- a/packages/mcp-debugger/package.json +++ b/packages/mcp-debugger/package.json @@ -30,6 +30,13 @@ "access": "public" }, "dependencies": {}, + "optionalDependencies": { + "@debugmcp/codelldb-darwin-arm64": "workspace:*", + "@debugmcp/codelldb-darwin-x64": "workspace:*", + "@debugmcp/codelldb-linux-arm64": "workspace:*", + "@debugmcp/codelldb-linux-x64": "workspace:*", + "@debugmcp/codelldb-win32-x64": "workspace:*" + }, "devDependencies": { "@debugmcp/adapter-cpp": "workspace:*", "@debugmcp/adapter-dotnet": "workspace:*", diff --git a/packages/mcp-debugger/scripts/bundle-cli.js b/packages/mcp-debugger/scripts/bundle-cli.js index b6c0f3e1..2d787b43 100644 --- a/packages/mcp-debugger/scripts/bundle-cli.js +++ b/packages/mcp-debugger/scripts/bundle-cli.js @@ -172,23 +172,23 @@ async function bundleCLI() { const rustVendorSrc = path.join(repoRoot, 'packages/codelldb-common/vendor/codelldb'); if (fs.existsSync(rustVendorSrc)) { - const rawPlatforms = process.env.CODELLDB_PACKAGE_PLATFORMS ?? 'linux-x64'; + // Default empty since issue #383: CodeLLDB ships via the per-platform + // @debugmcp/codelldb-* optionalDependencies instead of inside this + // tarball. Set CODELLDB_PACKAGE_PLATFORMS to build a fat bundle + // ('none' is the explicit skip for override contexts). + const rawPlatforms = process.env.CODELLDB_PACKAGE_PLATFORMS ?? ''; const requestedPlatforms = rawPlatforms .split(',') .map((platform) => platform.trim()) - .filter(Boolean); + .filter((platform) => platform && platform.toLowerCase() !== 'none'); if (requestedPlatforms.length === 0) { - console.log('Skipping CodeLLDB inclusion (CODELLDB_PACKAGE_PLATFORMS empty).'); + console.log('Skipping CodeLLDB inclusion (resolved via @debugmcp/codelldb-* platform packages).'); } else { const rustVendorDest = path.join(distDir, 'vendor/codelldb'); fs.mkdirSync(rustVendorDest, { recursive: true }); for (const platform of requestedPlatforms) { - if (platform.toLowerCase() === 'none') { - continue; - } - const srcDir = path.join(rustVendorSrc, platform); if (!fs.existsSync(srcDir)) { console.warn(`[CodeLLDB bundler] Requested platform "${platform}" not found in vendor directory.`); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42f57f37..c039c694 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -396,6 +396,16 @@ importers: specifier: ^4.1.10 version: 4.1.10(@types/node@26.2.0)(@vitest/coverage-istanbul@4.1.10)(@vitest/coverage-v8@4.1.10)(vite@7.3.6(@types/node@26.2.0)) + packages/codelldb-darwin-arm64: {} + + packages/codelldb-darwin-x64: {} + + packages/codelldb-linux-arm64: {} + + packages/codelldb-linux-x64: {} + + packages/codelldb-win32-x64: {} + packages/mcp-debugger: devDependencies: '@debugmcp/adapter-cpp': @@ -428,6 +438,22 @@ importers: '@debugmcp/shared': specifier: workspace:* version: link:../shared + optionalDependencies: + '@debugmcp/codelldb-darwin-arm64': + specifier: workspace:* + version: link:../codelldb-darwin-arm64 + '@debugmcp/codelldb-darwin-x64': + specifier: workspace:* + version: link:../codelldb-darwin-x64 + '@debugmcp/codelldb-linux-arm64': + specifier: workspace:* + version: link:../codelldb-linux-arm64 + '@debugmcp/codelldb-linux-x64': + specifier: workspace:* + version: link:../codelldb-linux-x64 + '@debugmcp/codelldb-win32-x64': + specifier: workspace:* + version: link:../codelldb-win32-x64 packages/shared: dependencies: diff --git a/scripts/prepare-pack.js b/scripts/prepare-pack.js index 56912c96..60a0370a 100644 --- a/scripts/prepare-pack.js +++ b/scripts/prepare-pack.js @@ -29,7 +29,11 @@ function getWorkspaceVersions() { const packagesDir = path.resolve(__dirname, '../packages'); // Read each workspace package - const workspaces = ['shared', 'codelldb-common', 'adapter-cpp', 'adapter-dotnet', 'adapter-go', 'adapter-java', 'adapter-javascript', 'adapter-python', 'adapter-mock', 'adapter-ruby', 'adapter-rust']; + const workspaces = [ + 'shared', 'codelldb-common', 'adapter-cpp', 'adapter-dotnet', 'adapter-go', 'adapter-java', + 'adapter-javascript', 'adapter-python', 'adapter-mock', 'adapter-ruby', 'adapter-rust', + 'codelldb-win32-x64', 'codelldb-darwin-x64', 'codelldb-darwin-arm64', 'codelldb-linux-x64', 'codelldb-linux-arm64' + ]; for (const ws of workspaces) { const pkgPath = path.join(packagesDir, ws, 'package.json'); @@ -80,7 +84,11 @@ function resolveWorkspaceDeps(pkg, versions) { if (resolved.peerDependencies) { resolved.peerDependencies = resolveDeps(resolved.peerDependencies); } - + + if (resolved.optionalDependencies) { + resolved.optionalDependencies = resolveDeps(resolved.optionalDependencies); + } + return resolved; } diff --git a/scripts/release-dry-run.sh b/scripts/release-dry-run.sh index 5e0150a3..2e980309 100644 --- a/scripts/release-dry-run.sh +++ b/scripts/release-dry-run.sh @@ -54,6 +54,28 @@ else fail "Package versions are inconsistent" fi +# CodeLLDB platform packages are versioned by the vendored CodeLLDB release, +# NOT the repo version (issue #383). Command substitutions are guarded so a +# malformed JSON becomes a red ✗ line instead of aborting the whole checklist +# under set -e. +if ! CODELLDB_PIN=$(node -e "console.log(require('./packages/codelldb-common/vendor-manifest.json').codelldb.version)"); then + fail "vendor-manifest.json unreadable — cannot check CodeLLDB platform package versions" + CODELLDB_PIN="" +fi +CODELLDB_PLATFORM_PKGS=("codelldb-win32-x64" "codelldb-darwin-x64" "codelldb-darwin-arm64" "codelldb-linux-x64" "codelldb-linux-arm64") +echo " CodeLLDB pin: ${CODELLDB_PIN:-}" +for pkg_dir in "${CODELLDB_PLATFORM_PKGS[@]}"; do + if ! PKG_VER=$(node -e "console.log(require('./packages/$pkg_dir/package.json').version)"); then + fail "$pkg_dir package.json unreadable" + continue + fi + if [[ "$PKG_VER" == "$CODELLDB_PIN" ]]; then + pass "$pkg_dir version matches CodeLLDB pin ($PKG_VER)" + else + fail "$pkg_dir at $PKG_VER but vendor-manifest pins $CODELLDB_PIN" + fi +done + # --- 2. CHANGELOG has this version with a date --- echo "" echo "── CHANGELOG ──" @@ -108,6 +130,45 @@ for pkg_dir in "${PUBLISHED_PKGS[@]}"; do fi done +# CodeLLDB platform packages: manifest sanity always; pack dry-run only when +# the payload is staged (stage-codelldb-packages.mjs runs at release time). +for pkg_dir in "${CODELLDB_PLATFORM_PKGS[@]}"; do + if ! pkg_name=$(node -e "console.log(require('./packages/$pkg_dir/package.json').name)"); then + fail "$pkg_dir package.json unreadable" + continue + fi + if ! MANIFEST_OK=$(node -e " + const p = require('./packages/$pkg_dir/package.json'); + const ok = Array.isArray(p.os) && p.os.length > 0 && + Array.isArray(p.cpu) && p.cpu.length > 0 && + p.publishConfig?.access === 'public' && + (p.repository?.url || '').includes('github.com/debugmcp/mcp-debugger') && + p.repository?.directory === 'packages/$pkg_dir'; + console.log(ok ? 'ok' : 'bad'); + "); then + MANIFEST_OK="bad" + fi + if [[ "$MANIFEST_OK" == "ok" ]]; then + pass "$pkg_name manifest (os/cpu/publishConfig/repository)" + else + fail "$pkg_name manifest incomplete (os/cpu/publishConfig/repository)" + fi + + if [[ -f "packages/$pkg_dir/version.json" ]]; then + if npm pack --dry-run -w "$pkg_name" > /dev/null 2>&1; then + pass "npm pack $pkg_name (payload staged)" + else + fail "npm pack $pkg_name failed" + fi + else + warn "$pkg_name payload not staged locally — release runs scripts/stage-codelldb-packages.mjs --verify" + fi + + if npm view "${pkg_name}@${CODELLDB_PIN}" version > /dev/null 2>&1; then + warn "${pkg_name}@${CODELLDB_PIN} already published — will be skipped" + fi +done + # --- 6. Check GitHub secrets --- echo "" echo "── Publishing credentials ──" diff --git a/scripts/stage-codelldb-packages.mjs b/scripts/stage-codelldb-packages.mjs new file mode 100644 index 00000000..fce77713 --- /dev/null +++ b/scripts/stage-codelldb-packages.mjs @@ -0,0 +1,210 @@ +#!/usr/bin/env node + +/** + * Stage CodeLLDB binaries into the per-platform npm packages (issue #383). + * + * Copies the vendored payload (adapter/, lldb/, lang_support/, version.json) + * from packages/codelldb-common/vendor/codelldb// into + * packages/codelldb-/ so those packages can be packed and published. + * Missing/stale platforms are vendored on demand via the digest-pinned vendor + * script (its own freshness check + CODELLDB_FORCE_REBUILD apply). Run at + * pack/publish time only - the payload is git-ignored and the platform + * packages have no build step. + * + * Usage: + * node scripts/stage-codelldb-packages.mjs [--verify] [--verify-only] [platform ...] + * + * With no platform arguments, all supported platforms are staged. + * --verify additionally fails unless each staged package holds the codelldb + * executable, the platform's liblldb library, and a version.json matching + * vendor-manifest.json - the guard against publishing a near-empty package + * (npm silently omits `files` entries that do not exist). --verify-only + * skips staging and only verifies, so the release workflow can verify in a + * FRESH process (a vendoring process that died silently cannot vouch for + * itself - issue #389). + */ + +import fs from 'fs/promises'; +import { existsSync, readFileSync } from 'fs'; +import path from 'path'; +import { fileURLToPath, pathToFileURL } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(__dirname, '..'); +const vendorRoot = path.join(repoRoot, 'packages', 'codelldb-common', 'vendor', 'codelldb'); +const manifestPath = path.join(repoRoot, 'packages', 'codelldb-common', 'vendor-manifest.json'); +const pinnedVersion = JSON.parse(readFileSync(manifestPath, 'utf8')).codelldb.version; + +// Single source of truth for platform keys and payload layout - the vendor +// script's PLATFORMS table (drift-guarded against the resolver's dir list). +const { downloadAndExtract, PLATFORMS } = await import( + pathToFileURL( + path.join(repoRoot, 'packages', 'codelldb-common', 'scripts', 'vendor-codelldb.js') + ).href +); + +// PLATFORMS paths are VSIX-relative ('extension/adapter/codelldb.exe'); +// staged payloads drop the 'extension/' prefix. +function packageRelative(vsixPath) { + return vsixPath.replace(/^extension\//, '').split('/'); +} + +const PAYLOAD_ENTRIES = ['adapter', 'lldb', 'lang_support', 'version.json']; + +const failures = []; + +function log(msg) { + console.log(`[stage-codelldb] ${msg}`); +} + +function fail(msg) { + console.error(`[stage-codelldb][error] ${msg}`); + failures.push(msg); +} + +async function stagePlatform(platformDir) { + const sourceDir = path.join(vendorRoot, platformDir); + const packageDir = path.join(repoRoot, 'packages', `codelldb-${platformDir}`); + + if (!existsSync(path.join(packageDir, 'package.json'))) { + fail(`Platform package missing: ${packageDir}`); + return false; + } + + // The vendor script skips fresh platforms, re-vendors stale ones, and + // verifies the pinned digest on download. + if (!(await downloadAndExtract(platformDir))) { + fail(`Unable to vendor CodeLLDB for ${platformDir}`); + return false; + } + + for (const entry of PAYLOAD_ENTRIES) { + const source = path.join(sourceDir, entry); + const target = path.join(packageDir, entry); + await fs.rm(target, { recursive: true, force: true }); + if (!existsSync(source)) { + if (entry === 'lang_support') { + continue; // optional upstream payload + } + fail(`${platformDir}: expected ${source} after vendoring`); + return false; + } + await fs.cp(source, target, { recursive: true }); + } + + log(`Staged ${platformDir} -> packages/codelldb-${platformDir}`); + return true; +} + +async function checkExecutableBit(platformDir, filePath) { + const stats = await fs.stat(filePath); + if (!(stats.mode & 0o111)) { + fail(`${platformDir}: ${filePath} is not executable`); + return false; + } + return true; +} + +async function verifyPlatform(platformDir) { + const packageDir = path.join(repoRoot, 'packages', `codelldb-${platformDir}`); + const info = PLATFORMS[platformDir]; + const binaryPath = path.join(packageDir, ...packageRelative(info.binaryPath)); + const liblldbPath = path.join(packageDir, ...packageRelative(info.libPath)); + const versionFile = path.join(packageDir, 'version.json'); + let ok = true; + + if (!existsSync(binaryPath)) { + fail(`${platformDir}: missing executable ${binaryPath}`); + ok = false; + } + + if (!existsSync(liblldbPath)) { + fail(`${platformDir}: missing liblldb ${liblldbPath}`); + ok = false; + } + + // Exec-bit verification for POSIX payloads: the codelldb binary plus every + // helper under lldb/bin (debugserver, lldb-server, lldb-argdumper rely on + // extract-zip preserving zip unix attrs). NTFS does not track exec bits, + // so a win32 staging host cannot verify - warn loudly rather than pass + // silently (the release runner is Linux, where the check is real). + if (platformDir !== 'win32-x64') { + if (process.platform === 'win32') { + console.warn( + `[stage-codelldb][warn] ${platformDir}: staging on Windows cannot verify POSIX exec bits - ` + + 'do not publish this payload from this machine; the release runner re-stages on Linux.' + ); + } else { + if (existsSync(binaryPath)) { + ok = (await checkExecutableBit(platformDir, binaryPath)) && ok; + } + const lldbBin = path.join(packageDir, 'lldb', 'bin'); + if (existsSync(lldbBin)) { + for (const entry of await fs.readdir(lldbBin, { withFileTypes: true })) { + if (entry.isFile()) { + ok = (await checkExecutableBit(platformDir, path.join(lldbBin, entry.name))) && ok; + } + } + } + } + } + + try { + const { version } = JSON.parse(await fs.readFile(versionFile, 'utf8')); + if (version !== pinnedVersion) { + fail(`${platformDir}: version.json says ${version}, vendor-manifest pins ${pinnedVersion}`); + ok = false; + } + } catch (error) { + fail(`${platformDir}: unreadable ${versionFile}: ${error instanceof Error ? error.message : error}`); + ok = false; + } + + if (ok) { + log(`Verified ${platformDir}`); + } + return ok; +} + +async function main() { + const args = process.argv.slice(2); + const verify = args.includes('--verify') || args.includes('--verify-only'); + const stageStep = !args.includes('--verify-only'); + const requested = args.filter(a => !a.startsWith('--')); + const platforms = requested.length > 0 ? requested : Object.keys(PLATFORMS); + + const unknown = platforms.filter(p => !PLATFORMS[p]); + for (const p of unknown) { + fail(`Unknown platform: ${p} (supported: ${Object.keys(PLATFORMS).join(', ')})`); + } + const valid = platforms.filter(p => PLATFORMS[p]); + + if (stageStep) { + // The vendor script uses per-platform temp/target dirs, so parallel + // staging is safe and cuts the release step to ~1x download time. + const staged = await Promise.all(valid.map(p => stagePlatform(p))); + if (verify) { + for (let i = 0; i < valid.length; i++) { + if (staged[i]) { + await verifyPlatform(valid[i]); + } + } + } + } else { + for (const p of valid) { + await verifyPlatform(p); + } + } + + if (failures.length > 0) { + console.error(`[stage-codelldb][error] ${failures.length} failure(s); see above.`); + process.exitCode = 1; + } else { + log('All requested platforms processed successfully.'); + } +} + +main().catch(error => { + fail(error instanceof Error ? (error.stack ?? error.message) : String(error)); + process.exitCode = 1; +}); diff --git a/scripts/sync-versions.cjs b/scripts/sync-versions.cjs index 0d76071a..24def64e 100755 --- a/scripts/sync-versions.cjs +++ b/scripts/sync-versions.cjs @@ -104,6 +104,23 @@ async function syncVersions() { const oldVersion = pkg.version; const packageName = path.basename(pkgPath) || 'root'; + // CodeLLDB platform payload packages (the only workspace packages with + // os/cpu fields) are versioned by the vendored CodeLLDB release, not the + // repo version (issue #383) — sync them to the vendor-manifest pin so a + // pin bump is just "edit vendor-manifest.json, run sync". + if (pkg.os && pkg.cpu) { + const pin = require('../packages/codelldb-common/vendor-manifest.json').codelldb.version; + if (oldVersion !== pin) { + pkg.version = pin; + fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n'); + versionChanges.push(` ✅ ${packageName}: ${oldVersion} → CodeLLDB pin ${pin}`); + updatedCount++; + } else { + versionChanges.push(` ⏩ ${packageName}: at CodeLLDB pin ${pin}`); + } + continue; + } + let changed = false; if (oldVersion !== targetVersion) { diff --git a/skills/debugging/references/cpp.md b/skills/debugging/references/cpp.md index 80a95790..29284028 100644 --- a/skills/debugging/references/cpp.md +++ b/skills/debugging/references/cpp.md @@ -50,7 +50,7 @@ attach_to_process {"sessionId": "", "processId": 4242} |---|---|---| | Breakpoints never hit | Built without debug info, DWARF-5-in-PE (MinGW default), or optimized | Rebuild `-gdwarf-4 -O0`; absolute source paths | | Variables `` (Windows) | MSVC PDB binary | Rebuild with MinGW-w64/clang (DWARF) | -| "Can't find CodeLLDB" | Not vendored / npx package on non-Linux | `pnpm --filter @debugmcp/codelldb-common run build:adapter`, or `CODELLDB_PATH` | +| "Can't find CodeLLDB" | Not vendored / npx install with `--omit=optional` | `pnpm --filter @debugmcp/codelldb-common run build:adapter`, reinstall with optional deps, or `CODELLDB_PATH` | | Attach EPERM (Linux) | ptrace scope | `sudo sysctl kernel.yama.ptrace_scope=0` | | First stop in system frames | Launch-time system stop | One `continue_execution` | | Compile fails on source-file launch | No compiler on PATH | Install g++/clang++, or pass a prebuilt binary | diff --git a/skills/debugging/references/rust.md b/skills/debugging/references/rust.md index 3b848f3a..99cfef6e 100644 --- a/skills/debugging/references/rust.md +++ b/skills/debugging/references/rust.md @@ -3,7 +3,7 @@ ## Prerequisites - Rust toolchain (`rustc`, `cargo`) installed via rustup. -- CodeLLDB debug adapter is vendored automatically at install/build time (`pnpm install` postinstall, or `pnpm --filter @debugmcp/codelldb-common run build:adapter`). The published npx CLI ships only the Linux x64 CodeLLDB runtime — on macOS/Windows set `CODELLDB_PATH` to a local CodeLLDB install (e.g. from the VSCode extension) or vendor from a cloned repo. +- CodeLLDB debug adapter is vendored automatically at install/build time (`pnpm install` postinstall, or `pnpm --filter @debugmcp/codelldb-common run build:adapter`). The published npx CLI installs CodeLLDB via per-platform `@debugmcp/codelldb-*` optional dependencies (all major platforms covered); with `--omit=optional` set `CODELLDB_PATH` to a local CodeLLDB install or vendor from a cloned repo. - **Windows: use the GNU toolchain.** CodeLLDB needs DWARF symbols; MSVC builds emit PDB, which LLDB reads only partially (variables often show ``). Build with: ```bash rustup target add x86_64-pc-windows-gnu @@ -70,7 +70,7 @@ Not supported. The Rust adapter implements launch mode only — `attach_to_proce |---|---|---| | Breakpoints never hit | Release/optimized build lacks usable debug info | `cargo build` (debug profile), `opt-level = 0`; absolute source paths | | Variables `` / garbage strings (Windows) | MSVC toolchain — PDB symbols | Rebuild: `cargo +stable-gnu build --target x86_64-pc-windows-gnu`; verify with `check-rust-binary` | -| "Can't find CodeLLDB" | Adapter not vendored / npx package on non-Linux | Run `pnpm --filter @debugmcp/codelldb-common run build:adapter`, or set `CODELLDB_PATH` | +| "Can't find CodeLLDB" | Adapter not vendored / npx install with `--omit=optional` | Run `pnpm --filter @debugmcp/codelldb-common run build:adapter`, reinstall with optional deps, or set `CODELLDB_PATH` | | First stop is in system/ntdll frames (or a SIGSTOP stop on Linux) | Launch-time system stop | `continue_execution` once, then you land on your breakpoint | | `continue_execution` re-stops on the same line | The line is a macro (`format!`, `println!`, …) whose expansion resolves to several breakpoint locations; each stop is a genuine hit at a different PC (issue #255) | Continue again until the line's locations are drained, or `step_over` once to cross the whole line | | `dlltool ... CreateProcess` build error | rustup GNU toolchain missing `as.exe` | Install MSYS2 mingw-w64 binutils/gcc; prepend `C:\msys64\mingw64\bin` to PATH | diff --git a/tests/e2e/npx/npx-smoke-rust.test.ts b/tests/e2e/npx/npx-smoke-rust.test.ts new file mode 100644 index 00000000..d84dbaeb --- /dev/null +++ b/tests/e2e/npx/npx-smoke-rust.test.ts @@ -0,0 +1,180 @@ +/** + * NPX Rust Smoke Tests (issue #383) + * + * Verifies that a plain npm/npx install delivers CodeLLDB via the + * @debugmcp/codelldb- optionalDependency and that Rust debugging + * works end-to-end from the installed package. + * + * Self-gating: skips until the platform package for this host is resolvable + * on the npm registry at the pinned CodeLLDB version (i.e. before the first + * release that publishes the five platform packages, or offline). + */ + +import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest'; +import { exec } from 'child_process'; +import { promisify } from 'util'; +import { existsSync, readFileSync } from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { + buildAndPackNpmPackage, + installPackageGlobally, + createNpxMcpClient, + cleanupGlobalInstall, + getPackageSize +} from './npx-test-utils.js'; +import { parseSdkToolResult } from '../smoke-test-utils.js'; +import { prepareRustExample } from '../rust-example-utils.js'; +import { + getCodeLLDBPlatformDir, + getCodeLLDBExecutableName +} from '@debugmcp/codelldb-common'; +import type { Client } from '@modelcontextprotocol/sdk/client/index.js'; + +const execAsync = promisify(exec); + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const ROOT = path.resolve(__dirname, '../../..'); + +const platformDir = getCodeLLDBPlatformDir(process.platform, process.arch); +const codelldbPin = ( + JSON.parse( + readFileSync(path.join(ROOT, 'packages', 'codelldb-common', 'vendor-manifest.json'), 'utf8') + ) as { codelldb: { version: string } } +).codelldb.version; + +async function platformPackagePublished(): Promise { + if (!platformDir) return false; + try { + // Timeboxed: this runs at collection time, and an offline npm's internal + // retries must not be able to stall the whole e2e run. + await execAsync(`npm view @debugmcp/codelldb-${platformDir}@${codelldbPin} version`, { + timeout: 15_000 + }); + return true; + } catch { + return false; + } +} + +const packageAvailable = await platformPackagePublished(); +if (!packageAvailable) { + console.log( + `[NPX Rust] Skipping: @debugmcp/codelldb-${platformDir}@${codelldbPin} not on the registry yet ` + + '(arms automatically after the first release that publishes the platform packages)' + ); +} + +describe.sequential.skipIf(!packageAvailable)('NPX: Rust Debugging Smoke Tests', () => { + let mcpClient: Client | null = null; + let cleanup: (() => Promise) | null = null; + let sessionId: string | null = null; + + beforeAll(async () => { + console.log('[NPX Rust] Building and packing npm package...'); + const tarballPath = await buildAndPackNpmPackage(); + + const size = await getPackageSize(tarballPath); + console.log(`[NPX Rust] Package size: ${size.sizeMB.toFixed(2)} MB`); + + console.log('[NPX Rust] Installing package globally...'); + await installPackageGlobally(tarballPath); + + const result = await createNpxMcpClient({ logLevel: 'debug' }); + mcpClient = result.client; + cleanup = result.cleanup; + console.log('[NPX Rust] MCP client connected'); + }, 240000); + + afterAll(async () => { + if (sessionId && mcpClient) { + try { + await mcpClient.callTool({ name: 'close_debug_session', arguments: { sessionId } }); + } catch { + // Session may already be closed + } + } + if (cleanup) { + await cleanup(); + } + await cleanupGlobalInstall(); + console.log('[NPX Rust] Cleanup completed'); + }); + + afterEach(async () => { + if (sessionId && mcpClient) { + try { + await mcpClient.callTool({ name: 'close_debug_session', arguments: { sessionId } }); + } catch { + // Ignore cleanup errors + } + sessionId = null; + } + }); + + it('npm delivered the CodeLLDB platform package next to the installed CLI', async () => { + const { stdout } = await execAsync('npm root -g'); + const globalRoot = stdout.trim(); + const exe = getCodeLLDBExecutableName(process.platform); + // Global installs nest deps under the package; npm may also hoist. + const candidates = [ + path.join(globalRoot, '@debugmcp', 'mcp-debugger', 'node_modules', '@debugmcp', `codelldb-${platformDir}`, 'adapter', exe), + path.join(globalRoot, '@debugmcp', `codelldb-${platformDir}`, 'adapter', exe) + ]; + const found = candidates.find((c) => existsSync(c)); + expect(found, `codelldb binary not found at: ${candidates.join(' | ')}`).toBeDefined(); + console.log(`[NPX Rust] ✓ Platform package payload at ${found}`); + }); + + it('completes a full Rust debugging cycle via the npx-installed CLI', async () => { + const { sourcePath, binaryPath } = await prepareRustExample('hello_world'); + expect(existsSync(binaryPath)).toBe(true); + + const createResponse = parseSdkToolResult(await mcpClient!.callTool({ + name: 'create_debug_session', + arguments: { language: 'rust', name: 'npx-rust-smoke' } + })); + expect(createResponse.sessionId).toBeDefined(); + sessionId = createResponse.sessionId as string; + + const bpResponse = parseSdkToolResult(await mcpClient!.callTool({ + name: 'set_breakpoint', + arguments: { sessionId, file: sourcePath, line: 26 } + })); + expect(bpResponse.success).toBe(true); + + const startResponse = parseSdkToolResult(await mcpClient!.callTool({ + name: 'start_debugging', + arguments: { + sessionId, + scriptPath: binaryPath, + dapLaunchArgs: { stopOnEntry: false } + } + })); + expect(startResponse.state).toContain('paused'); + console.log('[NPX Rust] ✓ Paused at breakpoint'); + + const stackResponse = parseSdkToolResult(await mcpClient!.callTool({ + name: 'get_stack_trace', + arguments: { sessionId } + })); + expect(stackResponse.success).toBe(true); + + const continueResponse = parseSdkToolResult(await mcpClient!.callTool({ + name: 'continue_execution', + arguments: { sessionId } + })); + expect(continueResponse.success).toBe(true); + + await new Promise((resolve) => setTimeout(resolve, 2000)); + + const closeResponse = parseSdkToolResult(await mcpClient!.callTool({ + name: 'close_debug_session', + arguments: { sessionId } + })); + expect(closeResponse.success).toBe(true); + sessionId = null; + console.log('[NPX Rust] ✅ All checks passed'); + }, 180000); +}); diff --git a/tests/e2e/npx/npx-test-utils.ts b/tests/e2e/npx/npx-test-utils.ts index 3f70f823..48f1b3ac 100644 --- a/tests/e2e/npx/npx-test-utils.ts +++ b/tests/e2e/npx/npx-test-utils.ts @@ -235,8 +235,29 @@ export async function installPackageGlobally(tarballPath: string): Promise // Package might not be installed } - // Install from tarball - await execAsync(`npm install -g "${tarballPath}"`); + // Install from tarball. The @debugmcp/codelldb-* optionalDependencies live + // on the registry (issue #383); until their first publish resolving them + // fails with E404/ETARGET, so retry without optional deps (rust/cpp then + // fall back to CODELLDB_PATH/vendor tree). The retry is deliberately + // narrow: any OTHER install failure must stay loud — a blanket retry + // would let e.g. a broken optionalDependencies rewrite pass CI silently. + try { + await execAsync(`npm install -g "${tarballPath}"`); + } catch (installError) { + const text = installError instanceof Error + ? `${installError.message}\n${(installError as { stderr?: string }).stderr ?? ''}` + : String(installError); + const isMissingCodelldbPackage = + /E404|ETARGET/.test(text) && text.includes('@debugmcp/codelldb-'); + if (!isMissingCodelldbPackage) { + throw installError; + } + console.warn( + '[NPX Test] npm install -g failed resolving a @debugmcp/codelldb-* optional dep ' + + '(not yet published?); retrying with --omit=optional' + ); + await execAsync(`npm install -g "${tarballPath}" --omit=optional`); + } console.log('[NPX Test] Package installed globally'); // Verify installation