MCPcopy Create free account
hub / github.com/Noumena-Network/code / cleanupNpmCacheForAnthropicPackages

Function cleanupNpmCacheForAnthropicPackages

src/utils/cleanup.ts:439–536  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

437 * Only runs once per day for Ant users.
438 */
439export async function cleanupNpmCacheForAnthropicPackages(): Promise<void> {
440 const markerPath = join(getClaudeConfigHomeDir(), '.npm-cache-cleanup')
441
442 try {
443 const stat = await fs.stat(markerPath)
444 if (Date.now() - stat.mtimeMs < ONE_DAY_MS) {
445 logForDebugging('npm cache cleanup: skipping, ran recently')
446 return
447 }
448 } catch {
449 // File doesn't exist, proceed with cleanup
450 }
451
452 try {
453 await lockfile.lock(markerPath, { retries: 0, realpath: false })
454 } catch {
455 logForDebugging('npm cache cleanup: skipping, lock held')
456 return
457 }
458
459 logForDebugging('npm cache cleanup: starting')
460
461 const npmCachePath = join(homedir(), '.npm', '_cacache')
462
463 const NPM_CACHE_RETENTION_COUNT = 5
464
465 const startTime = Date.now()
466 try {
467 const cacache = await import('cacache')
468 const cutoff = startTime - ONE_DAY_MS
469
470 // Stream index entries and collect all Anthropic package entries.
471 // Previous implementation used cacache.verify() which does a full
472 // integrity check + GC of the ENTIRE cache — O(all content blobs).
473 // On large caches this took 60+ seconds and blocked the event loop.
474 const stream = cacache.ls.stream(npmCachePath)
475 const anthropicEntries: { key: string; time: number }[] = []
476 for await (const entry of stream as AsyncIterable<{
477 key: string
478 time: number
479 }>) {
480 if (entry.key.includes('@anthropic-ai/claude-')) {
481 anthropicEntries.push({ key: entry.key, time: entry.time })
482 }
483 }
484
485 // Group by package name (everything before the last @version separator)
486 const byPackage = new Map<string, { key: string; time: number }[]>()
487 for (const entry of anthropicEntries) {
488 const atVersionIdx = entry.key.lastIndexOf('@')
489 const pkgName =
490 atVersionIdx > 0 ? entry.key.slice(0, atVersionIdx) : entry.key
491 const existing = byPackage.get(pkgName) ?? []
492 existing.push(entry)
493 byPackage.set(pkgName, existing)
494 }
495
496 // Remove entries older than 1 day OR beyond the top N most recent per package

Callers 2

Calls 5

setMethod · 0.80
logForDebuggingFunction · 0.70
logErrorFunction · 0.70
logEventFunction · 0.50
getMethod · 0.45

Tested by

no test coverage detected