feat: add systemd auto-update service for agents on cloud VMs (#2728)

Installs a systemd timer + oneshot service that updates the agent binary
and system packages every 6 hours without disrupting running instances.

Agent update safety:
- Binary agents (Go, Rust): Linux keeps old inode in memory; safe to replace
- npm agents: Node.js caches modules at startup; running processes unaffected
- New version takes effect on next restart via the existing restart loop

System update safety:
- Disables Ubuntu's unattended-upgrades to prevent dpkg lock contention
- Uses flock -w 300 on /var/lib/dpkg/lock-frontend before apt operations
- DEBIAN_FRONTEND=noninteractive with --force-confdef/--force-confold

User-facing:
- "Auto-update" option in setup multiselect (default on, user can uncheck)
- Skipped for local cloud and non-systemd systems
- Non-fatal: setup failure doesn't block agent launch
- Logs to /var/log/spawn-auto-update.log

Timer: 15min after boot, then every 6h with 30min random jitter.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ahmed Abushagur 2026-03-17 17:34:12 -07:00 committed by GitHub
parent 66b16d8651
commit 6e92cc832b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 502 additions and 1 deletions

View file

@ -10,7 +10,7 @@ import { readFileSync } from "node:fs";
import { getErrorMessage } from "@openrouter/spawn-shared";
import * as v from "valibot";
import { generateSpawnId, saveLaunchCmd, saveMetadata, saveSpawnRecord } from "../history.js";
import { offerGithubAuth, wrapSshCall } from "./agent-setup";
import { offerGithubAuth, setupAutoUpdate, wrapSshCall } from "./agent-setup";
import { tryTarballInstall } from "./agent-tarball";
import { generateEnvConfig } from "./agents";
import { getOrPromptApiKey } from "./oauth";
@ -254,6 +254,11 @@ export async function runOrchestration(
await offerGithubAuth(cloud.runner, enabledSteps?.has("github"));
}
// 10c. Auto-update service (systemd timer — cloud VMs only, default on)
if (cloud.cloudName !== "local" && agent.updateCmd && (!enabledSteps || enabledSteps.has("auto-update"))) {
await setupAutoUpdate(cloud.runner, agentName, agent.updateCmd);
}
// 11. Pre-launch hooks (e.g. OpenClaw gateway)
if (agent.preLaunch) {
await agent.preLaunch();