fix(auto-update): clean stale OMO cache roots

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
deopa0402
2026-04-29 00:53:11 +09:00
committed by 전나맛있는어쩌고힙한하루보내세요
parent babee921b2
commit 37d9d613b6
2 changed files with 128 additions and 56 deletions
+80 -20
View File
@@ -1,6 +1,6 @@
import * as fs from "node:fs"
import * as path from "node:path"
import { CACHE_DIR, PACKAGE_NAME, getUserConfigDir } from "./constants"
import { ACCEPTED_PACKAGE_NAMES, CACHE_DIR, PACKAGE_NAME, getUserConfigDir } from "./constants"
import { log } from "../../shared/logger"
interface BunLockfile {
@@ -12,22 +12,36 @@ interface BunLockfile {
packages?: Record<string, unknown>
}
interface InvalidatePackageOptions {
acceptedPackageNames?: readonly string[]
cacheDir?: string
defaultPackageName?: string
userConfigDir?: string
}
function stripTrailingCommas(json: string): string {
return json.replace(/,(\s*[}\]])/g, "$1")
}
function removeFromTextBunLock(lockPath: string, packageName: string): boolean {
function removeFromTextBunLock(lockPath: string, packageNames: readonly string[]): boolean {
try {
const content = fs.readFileSync(lockPath, "utf-8")
const lock = JSON.parse(stripTrailingCommas(content)) as BunLockfile
let removed = false
if (lock.packages?.[packageName]) {
delete lock.packages[packageName]
fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2))
log(`[auto-update-checker] Removed from bun.lock: ${packageName}`)
return true
for (const packageName of packageNames) {
if (lock.packages?.[packageName]) {
delete lock.packages[packageName]
log(`[auto-update-checker] Removed from bun.lock: ${packageName}`)
removed = true
}
}
return false
if (removed) {
fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2))
}
return removed
} catch {
return false
}
@@ -43,12 +57,12 @@ function deleteBinaryBunLock(lockPath: string): boolean {
}
}
function removeFromBunLock(packageName: string): boolean {
const textLockPath = path.join(CACHE_DIR, "bun.lock")
const binaryLockPath = path.join(CACHE_DIR, "bun.lockb")
function removeFromBunLock(cacheDir: string, packageNames: readonly string[]): boolean {
const textLockPath = path.join(cacheDir, "bun.lock")
const binaryLockPath = path.join(cacheDir, "bun.lockb")
if (fs.existsSync(textLockPath)) {
return removeFromTextBunLock(textLockPath, packageName)
return removeFromTextBunLock(textLockPath, packageNames)
}
// Binary lockfiles cannot be parsed; deletion forces bun to re-resolve
@@ -59,16 +73,61 @@ function removeFromBunLock(packageName: string): boolean {
return false
}
export function invalidatePackage(packageName: string = PACKAGE_NAME): boolean {
function getInvalidationPackageNames(
packageName: string,
defaultPackageName: string,
acceptedPackageNames: readonly string[]
): readonly string[] {
if (packageName === defaultPackageName) {
return acceptedPackageNames
}
return [packageName]
}
function removeSpecifierRootDirs(cacheDir: string, packageNames: readonly string[]): boolean {
const parentDirs = [cacheDir, path.join(cacheDir, "packages")]
const prefixes = packageNames.map(packageName => `${packageName}@`)
let removed = false
for (const parentDir of parentDirs) {
if (!fs.existsSync(parentDir)) {
continue
}
for (const entry of fs.readdirSync(parentDir, { withFileTypes: true })) {
if (!entry.isDirectory() || !prefixes.some(prefix => entry.name.startsWith(prefix))) {
continue
}
const specifierDir = path.join(parentDir, entry.name)
fs.rmSync(specifierDir, { recursive: true, force: true })
log(`[auto-update-checker] Specifier cache removed: ${specifierDir}`)
removed = true
}
}
return removed
}
export function invalidatePackage(
packageName: string = PACKAGE_NAME,
options: InvalidatePackageOptions = {}
): boolean {
try {
const userConfigDir = getUserConfigDir()
const pkgDirs = [
path.join(userConfigDir, "node_modules", packageName),
path.join(CACHE_DIR, "node_modules", packageName),
]
const acceptedPackageNames = options.acceptedPackageNames ?? ACCEPTED_PACKAGE_NAMES
const cacheDir = options.cacheDir ?? CACHE_DIR
const defaultPackageName = options.defaultPackageName ?? PACKAGE_NAME
const userConfigDir = options.userConfigDir ?? getUserConfigDir()
const packageNames = getInvalidationPackageNames(packageName, defaultPackageName, acceptedPackageNames)
const pkgDirs = packageNames.flatMap(name => [
path.join(userConfigDir, "node_modules", name),
path.join(cacheDir, "node_modules", name),
])
let packageRemoved = false
let lockRemoved = false
let specifierRemoved = false
for (const pkgDir of pkgDirs) {
if (fs.existsSync(pkgDir)) {
@@ -78,9 +137,10 @@ export function invalidatePackage(packageName: string = PACKAGE_NAME): boolean {
}
}
lockRemoved = removeFromBunLock(packageName)
specifierRemoved = removeSpecifierRootDirs(cacheDir, packageNames)
lockRemoved = removeFromBunLock(cacheDir, packageNames)
if (!packageRemoved && !lockRemoved) {
if (!packageRemoved && !specifierRemoved && !lockRemoved) {
log(`[auto-update-checker] Package not found, nothing to invalidate: ${packageName}`)
return false
}