diff --git a/package.json b/package.json index dbd18d9..a049af2 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "test:update-chain:manual": "bun run src/tests/update-chain-smoke.ts --mode manual", "test:update-chain:startup": "bun run src/tests/update-chain-smoke.ts --mode startup", "prepublishOnly": "bun run build", - "postinstall": "node scripts/postinstall-patches.js" + "postinstall": "node scripts/postinstall-patches.js || echo letta: vendor patches skipped" }, "lint-staged": { "*.{ts,tsx,js,jsx,json}": [ diff --git a/src/updater/auto-update.ts b/src/updater/auto-update.ts index 9b1654e..8d9e4f5 100644 --- a/src/updater/auto-update.ts +++ b/src/updater/auto-update.ts @@ -323,6 +323,32 @@ async function performUpdate(): Promise<{ } } + // npm race condition retry: covers TAR_ENTRY_ERROR (parallel extraction races), + // uv_cwd (npm CWD deleted during atomic package swap), and spawn sh ENOENT + // (sharp postinstall CWD missing). All are transient npm parallelism issues. + const isNpmRaceCondition = + pm === "npm" && + (errorMsg.includes("TAR_ENTRY_ERROR") || + errorMsg.includes("uv_cwd") || + (errorMsg.includes("spawn sh") && errorMsg.includes("ENOENT"))); + + if (isNpmRaceCondition) { + debugLog("npm race condition detected, cleaning up and retrying..."); + if (globalPath) { + await cleanupOrphanedDirs(globalPath); + } + try { + await execFileAsync(pm, installArgs, { timeout: 60000 }); + debugLog("Update succeeded after race condition retry"); + return { success: true }; + } catch (retryError) { + const retryMsg = + retryError instanceof Error ? retryError.message : String(retryError); + debugLog("Update failed after race condition retry:", retryMsg); + return { success: false, error: retryMsg }; + } + } + debugLog("Update failed:", error); return { success: false, error: errorMsg }; }