Skip to content

fix(sdk,cli): evict stale local bundle cache entries, add cache info/clear commands (#60, #78, #79, #95)#113

Open
shwetank-dev wants to merge 6 commits into
NimbleBrainInc:mainfrom
shwetank-dev:issue-79-78-60-95
Open

fix(sdk,cli): evict stale local bundle cache entries, add cache info/clear commands (#60, #78, #79, #95)#113
shwetank-dev wants to merge 6 commits into
NimbleBrainInc:mainfrom
shwetank-dev:issue-79-78-60-95

Conversation

@shwetank-dev
Copy link
Copy Markdown
Contributor

Summary

This PR bundles four fixes and features from the QA backlog:

#78 — Check platform in loadBundle cache guards

loadBundle was returning a cached bundle even when the cached platform (os/arch) didn't match the current host. Added platform comparison to both the early-return and the registry short-circuit checks.

#60 — Populate bundle cache after bundle pull

bundle pull downloaded the bundle to disk but didn't extract it into the cache, so a subsequent bundle run would re-download unnecessarily. Fixed by calling extractBundle after the download in pull.ts.

#79 — Surface checkForUpdate failures instead of swallowing them

checkForUpdate returned null on both "up to date" and "network error", making the two indistinguishable. Changed the return type to a discriminated union (up-to-date | update-available | check-failed) and wrapped the body in try/catch. The outdated command now emits a stderr warning for check-failed entries instead of silently skipping them.

#95 — Evict stale local bundle cache entries + mpak cache info/clear (phase 1)

Root cause: Local bundles are cached under _local/<hash(absolutePath)>/. Rebuilding a bundle with a new versioned filename (mcp-foo-v0.1.0.mcpbv0.1.1.mcpb) produces a new hash and a new cache entry — the old one is never evicted. One developer accumulated 2.1 GB from 33 stale local entries.

Fix: After preparing a local bundle, evictOtherLocalBundles(bundleName, currentHash) scans _local/ for other entries sharing the same manifest.name and removes them. One entry per bundle, always. Eviction key is manifest.name (not parent directory) to avoid false positives when multiple different bundles share a dist folder.

New SDK surface:

  • MpakBundleCache.evictOtherLocalBundles(bundleName, currentHash) — called automatically on every local bundle prepare
  • MpakBundleCache.getCacheInfo()CacheInfo — returns registry and local entries with per-entry disk sizes and a total
  • dirSizeBytes(dir) helper in helpers.ts
  • CacheInfo, RegistryCacheEntry, LocalCacheEntry exported from public index

New CLI commands:

  • mpak cache info — two-section table (registry / local) with sizes and pull/extract dates; --json supported
  • mpak cache clear — deletes ~/.mpak/cache/ entirely; shows size to be freed, prompts for confirmation, --force skips prompt

Test plan

  • SDK: 253 tests passing (pnpm --filter @nimblebrain/mpak-sdk test)
  • CLI: 187 tests passing (pnpm --filter @nimblebrain/mpak test)
  • Eviction: installing same-named bundle from a new path removes the old _local/<hash> entry
  • mpak cache info shows registry and local bundles with correct sizes
  • mpak cache info --json outputs valid JSON matching CacheInfo shape
  • mpak cache clear prompts, reports freed space, deletes cache directory
  • mpak cache clear --force skips prompt
  • mpak cache clear on empty cache prints "already empty" without error
  • Pre-existing @nimblebrain/mpak-web typecheck failure is unrelated to this PR (reproduced on main)

Closes #60, #78, #79, #95

🤖 Generated with Claude Code

shwetank-dev and others added 5 commits May 10, 2026 14:05
Both early-return guards in loadBundle compared only version, so a
cached darwin bundle was returned on a linux host when versions matched.
Move detectPlatform() above guard 1 and add os/arch checks to both guards.
Adds unit tests for platform mismatch, platform match, and force flag.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
bundle pull downloaded bytes to disk but never updated ~/.mpak/cache/,
so bundle run would continue using the old version until an explicit
update was triggered.

Added MpakBundleCache.extractBundle(name, data, bundle) which takes
already-downloaded bytes and extracts them into the cache — reusing the
bytes already in memory from downloadBundle so no second network call is
made. pull.ts calls extractBundle after writeFileSync.

Added integration test that asserts ~/.mpak/cache/<bundle>/.mpak-meta.json
exists and contains the correct version after a pull.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ing them (NimbleBrainInc#79)

Previously checkForUpdate returned string | null, making a network error
or registry failure indistinguishable from "bundle is up to date". Both
returned null, so getOutdatedBundles would silently report all bundles
as current even when checks were failing.

Replace the return type with a discriminated union (UpdateCheckResult)
with three variants: up-to-date, update-available, and check-failed.
The check-failed variant carries the failure reason so callers can warn
the user rather than silently swallowing the error.

Update getOutdatedBundles in outdated.ts to emit a stderr warning on
check-failed instead of treating it as up-to-date. Update all affected
tests and export the new type from the SDK index.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…fo (NimbleBrainInc#95)

Local bundles cached under _local/<hash> were keyed by absolute path, so
rebuilding a bundle with a new versioned filename (v0.1.0 -> v0.1.1) produced
a new cache entry each time without ever evicting the old one.

Fix: after preparing a local bundle, scan _local/ for other entries sharing
the same manifest.name and remove them. One entry per bundle, always.

Also adds getCacheInfo() to MpakBundleCache, which returns registry and local
entries with per-entry disk sizes and a total. Surfaces this via
`mpak cache info` (--json supported).

New helpers:
- MpakBundleCache.evictOtherLocalBundles(bundleName, currentHash)
- MpakBundleCache.getCacheInfo() -> CacheInfo
- dirSizeBytes(dir) in helpers.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deletes the entire ~/.mpak/cache/ directory and reports how much disk
space was freed. Prompts for confirmation before deleting; --force skips
the prompt for scripted/non-interactive use.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bundle run uses stale cache after pulling a new version

1 participant