add auto updates (#172)

This commit is contained in:
Christina Tong
2025-12-10 17:49:20 -08:00
committed by GitHub
parent f9c3e0767e
commit 6b8215aa87
3 changed files with 155 additions and 1 deletions

View File

@@ -393,6 +393,28 @@ Permissions are also configured in `.letta/settings.json`:
}
```
## Updates
Letta Code automatically checks for updates on startup and installs them in the background.
### Auto updates
* **Update checks**: Performed on startup
* **Update process**: Downloads and installs automatically in the background
* **Applying updates**: Updates take effect the next time you start Letta Code
**Disable auto-updates:**
Set the `DISABLE_AUTOUPDATER` environment variable in your shell:
```bash
export DISABLE_AUTOUPDATER=1
```
### Update manually
```bash
letta update
```
## Self-hosting
To use Letta Code with a self-hosted server, set `LETTA_BASE_URL` to your server IP, e.g. `export LETTA_BASE_URL="http://localhost:8283"`.

View File

@@ -26,6 +26,9 @@ USAGE
# headless
letta -p "..." One-off prompt in headless mode (no TTY UI)
# maintenance
letta update Manually check for updates and install if available
OPTIONS
-h, --help Show this help and exit
-v, --version Print version and exit
@@ -105,6 +108,12 @@ async function main() {
await settingsManager.initialize();
const settings = settingsManager.getSettings();
// Check for updates on startup (non-blocking)
const { checkAndAutoUpdate } = await import("./updater/auto-update");
checkAndAutoUpdate().catch(() => {
// Silently ignore update failures
});
// set LETTA_API_KEY from environment if available
if (process.env.LETTA_API_KEY && !settings.env?.LETTA_API_KEY) {
settings.env = settings.env || {};
@@ -165,7 +174,7 @@ async function main() {
}
// Check for subcommands
const _command = positionals[2]; // First positional after node and script
const command = positionals[2]; // First positional after node and script
// Handle help flag first
if (values.help) {
@@ -180,6 +189,14 @@ async function main() {
process.exit(0);
}
// Handle update command
if (command === "update") {
const { manualUpdate } = await import("./updater/auto-update");
const result = await manualUpdate();
console.log(result.message);
process.exit(result.success ? 0 : 1);
}
const shouldContinue = (values.continue as boolean | undefined) ?? false;
const forceNew = (values.new as boolean | undefined) ?? false;
const freshBlocks = (values["fresh-blocks"] as boolean | undefined) ?? false;

115
src/updater/auto-update.ts Normal file
View File

@@ -0,0 +1,115 @@
import { exec } from "node:child_process";
import { promisify } from "node:util";
import { getVersion } from "../version";
const execAsync = promisify(exec);
interface UpdateCheckResult {
updateAvailable: boolean;
latestVersion?: string;
currentVersion: string;
}
function isAutoUpdateEnabled(): boolean {
return process.env.DISABLE_AUTOUPDATER !== "1";
}
function isRunningLocally(): boolean {
const argv = process.argv[1] || "";
// 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");
}
async function checkForUpdate(): Promise<UpdateCheckResult> {
const currentVersion = getVersion();
try {
const { stdout } = await execAsync(
"npm view @letta-ai/letta-code version",
{ timeout: 5000 },
);
const latestVersion = stdout.trim();
if (latestVersion !== currentVersion) {
return {
updateAvailable: true,
latestVersion,
currentVersion,
};
}
} catch (_error) {
// Silently fail
}
return {
updateAvailable: false,
currentVersion,
};
}
async function performUpdate(): Promise<{ success: boolean; error?: string }> {
try {
await execAsync("npm install -g @letta-ai/letta-code@latest", {
timeout: 60000,
});
return { success: true };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
}
export async function checkAndAutoUpdate() {
if (!isAutoUpdateEnabled() || isRunningLocally()) {
return;
}
const result = await checkForUpdate();
if (result.updateAvailable) {
await performUpdate();
}
}
export async function manualUpdate(): Promise<{
success: boolean;
message: string;
}> {
if (isRunningLocally()) {
return {
success: false,
message: "Manual updates are disabled in development mode",
};
}
const result = await checkForUpdate();
if (!result.updateAvailable) {
return {
success: true,
message: `Already on latest version (${result.currentVersion})`,
};
}
console.log(
`Updating from ${result.currentVersion} to ${result.latestVersion}...`,
);
const updateResult = await performUpdate();
if (updateResult.success) {
return {
success: true,
message: `Updated to ${result.latestVersion}. Restart Letta Code to use the new version.`,
};
}
return {
success: false,
message: `Update failed: ${updateResult.error}`,
};
}