fix: resolve symlinks in auto-update path detection (#348)
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -1,9 +1,18 @@
|
||||
import { exec } from "node:child_process";
|
||||
import { realpathSync } from "node:fs";
|
||||
import { promisify } from "node:util";
|
||||
import { getVersion } from "../version";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
// Debug logging - set LETTA_DEBUG_AUTOUPDATE=1 to enable
|
||||
const DEBUG = process.env.LETTA_DEBUG_AUTOUPDATE === "1";
|
||||
function debugLog(...args: unknown[]) {
|
||||
if (DEBUG) {
|
||||
console.error("[auto-update]", ...args);
|
||||
}
|
||||
}
|
||||
|
||||
interface UpdateCheckResult {
|
||||
updateAvailable: boolean;
|
||||
latestVersion?: string;
|
||||
@@ -17,30 +26,48 @@ function isAutoUpdateEnabled(): boolean {
|
||||
function isRunningLocally(): boolean {
|
||||
const argv = process.argv[1] || "";
|
||||
|
||||
// Resolve symlinks to get the real path
|
||||
// npm creates symlinks in /bin/ that point to /lib/node_modules/
|
||||
// Without resolving, argv would be like ~/.nvm/.../bin/letta (no node_modules)
|
||||
let resolvedPath = argv;
|
||||
try {
|
||||
resolvedPath = realpathSync(argv);
|
||||
} catch {
|
||||
// If realpath fails (file doesn't exist), use original path
|
||||
}
|
||||
|
||||
debugLog("argv[1]:", argv);
|
||||
debugLog("resolved path:", resolvedPath);
|
||||
|
||||
// If running from node_modules, it's npm installed (should auto-update)
|
||||
// Otherwise it's local dev (source or built locally)
|
||||
return !argv.includes("node_modules");
|
||||
return !resolvedPath.includes("node_modules");
|
||||
}
|
||||
|
||||
async function checkForUpdate(): Promise<UpdateCheckResult> {
|
||||
const currentVersion = getVersion();
|
||||
debugLog("Current version:", currentVersion);
|
||||
|
||||
try {
|
||||
debugLog("Checking npm for latest version...");
|
||||
const { stdout } = await execAsync(
|
||||
"npm view @letta-ai/letta-code version",
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
const latestVersion = stdout.trim();
|
||||
debugLog("Latest version from npm:", latestVersion);
|
||||
|
||||
if (latestVersion !== currentVersion) {
|
||||
debugLog("Update available!");
|
||||
return {
|
||||
updateAvailable: true,
|
||||
latestVersion,
|
||||
currentVersion,
|
||||
};
|
||||
}
|
||||
} catch (_error) {
|
||||
// Silently fail
|
||||
debugLog("Already on latest version");
|
||||
} catch (error) {
|
||||
debugLog("Failed to check for updates:", error);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -51,11 +78,14 @@ async function checkForUpdate(): Promise<UpdateCheckResult> {
|
||||
|
||||
async function performUpdate(): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
debugLog("Running npm install -g @letta-ai/letta-code@latest...");
|
||||
await execAsync("npm install -g @letta-ai/letta-code@latest", {
|
||||
timeout: 60000,
|
||||
});
|
||||
debugLog("Update completed successfully");
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
debugLog("Update failed:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
@@ -64,7 +94,18 @@ async function performUpdate(): Promise<{ success: boolean; error?: string }> {
|
||||
}
|
||||
|
||||
export async function checkAndAutoUpdate() {
|
||||
if (!isAutoUpdateEnabled() || isRunningLocally()) {
|
||||
debugLog("Auto-update check starting...");
|
||||
debugLog("isAutoUpdateEnabled:", isAutoUpdateEnabled());
|
||||
const runningLocally = isRunningLocally();
|
||||
debugLog("isRunningLocally:", runningLocally);
|
||||
|
||||
if (!isAutoUpdateEnabled()) {
|
||||
debugLog("Auto-update disabled via DISABLE_AUTOUPDATER=1");
|
||||
return;
|
||||
}
|
||||
|
||||
if (runningLocally) {
|
||||
debugLog("Running locally, skipping auto-update");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user