import type { UpdateConfig, NpmVersionInfo } from '../types/update.js'; export declare class NpmAutoUpdateService { private checkInterval; private initialCheckTimeout; private stagedRolloutTimeout; private readonly config; constructor(config?: Partial); /** * Start the auto-update service. * Performs initial check after 1 minute, then checks periodically. */ start(): void; /** * Stop the auto-update service. */ stop(): void; /** * Check for updates without installing. */ checkForUpdates(): Promise; /** * Trigger an immediate update (bypasses staged rollout). * Returns update result with version info. * * @param opts.force - When true (operator clicked "force update"), proceed * to (re)install the channel tag even if `checkForUpdates` reports * `updateAvailable:false`. This repairs/reinstalls an agent already at * latest. `force` only changes the "proceed despite updateAvailable:false" * decision — the install mechanism (in-process `npm install -g` when root, * or the root-owned updater unit via `sudo systemctl start` when non-root) * is unchanged. */ triggerUpdate(opts?: { force?: boolean; }): Promise<{ success: boolean; previousVersion: string; newVersion: string; willRestart: boolean; message: string; }>; /** * Check for updates and install if available. * Includes staged rollout delay and health check with rollback. */ private checkAndUpdate; /** * Calculate random delay for staged rollout. * Uses crypto-grade randomness for better distribution. */ calculateStagedDelay(): number; /** * Sleep for specified milliseconds. */ private sleep; /** * Get current installed version from package.json. */ getCurrentVersion(): string; /** * Get latest version from npm registry. */ private getLatestVersion; /** * Compare semver versions using the semver package. * Returns true if `latest` is newer than `current`. * Properly handles pre-releases (e.g., 1.0.0-beta.1 < 1.0.0) * and build metadata (ignored per semver spec). */ isNewer(latest: string, current: string): boolean; /** * Acquire update lock file atomically using O_EXCL. * Returns false if another agent is updating. */ private acquireLock; /** * Release update lock file. */ private releaseLock; /** * Check if running as root user. * If not root, we'll need sudo for npm global installs. */ private isRoot; /** * Get the sudo prefix if running as non-root. * Returns 'sudo ' for non-root users, empty string for root. */ private getSudoPrefix; /** * Detect whether the root-owned updater `.path` unit is installed (the * sudo-free trigger mechanism). Mirrors hasUpdaterUnit(). */ private hasUpdaterPathUnit; /** * Trigger an update by atomically creating the trigger file. The root-owned * `.path` unit activates the updater oneshot, which reads + deletes the * trigger and installs the target. The agent only ever CREATES the trigger; * the root wrapper is the sole deleter. */ private installViaTriggerFile; /** * Detect whether the root-owned updater unit is installed on this host. * `systemctl cat ` exits 0 when the unit exists, non-zero otherwise, * so a resolved exec means the unit is available. */ private hasUpdaterUnit; /** * Install the update by starting the root-owned updater unit AS the agent * user via sudo. * * The agent runs as the unprivileged `zn-vault-agent` user. A bare * `systemctl start` is denied by polkit ("Interactive authentication * required"), but the provisioned sudoers rule permits exactly: * `sudo /usr/bin/systemctl start zn-vault-agent-updater.service` * (NOPASSWD). The oneshot unit runs `npm install -g @latest` and * then `systemctl try-restart zn-vault-agent` (ExecStartPost) as root, in its * OWN namespace where /usr/bin is writable. Because the unit already restarts * the agent, callers MUST NOT also self-restart (see performUpdate's return * contract). * * The command string is `sudo /usr/bin/systemctl start ` for the * non-root agent; this is the only privilege path used here. The absolute * systemctl path is required so it matches the sudoers rule exactly. * * NOTE: the unit installs `@latest`, not the resolved target version. That is * acceptable for operator-initiated updates today. Follow-up: parameterize * the unit with the target version so forced/targeted versions are honored * end-to-end. */ private installViaUpdaterUnit; /** * Clear npm cache to ensure clean install. * This helps prevent issues from interrupted previous installs. */ private clearNpmCache; /** * Perform the update, choosing the install strategy by privilege/environment. * * Live testing (INC-2026-06-12-01) PROVED that an in-process `sudo npm * install` cannot write `/usr/bin` for a sandboxed agent: `ProtectSystem=strict` * makes `/usr` read-only IN THE AGENT'S MOUNT NAMESPACE, and `sudo` only * changes the uid — it does NOT escape the namespace — so the install fails * `EROFS`. The only working path for a non-root sandboxed agent is the * root-owned updater unit, which runs in its OWN clean namespace. * * Strategy: * - **root** → `npm install -g @` directly (no sudo). Root is * not sandboxed the same way, and the caller still verifies + restarts. * - **non-root + updater unit present** → `sudo /usr/bin/systemctl start * ` (permitted by the provisioned sudoers rule). The unit installs + * restarts the agent outside the sandbox, so this returns `true` (restart * handled externally) and the caller MUST NOT self-verify/self-restart * (avoids a double restart, which would kill the agent mid-verify). * - **non-root + no updater unit** → best-effort `sudo npm install -g`. This * fails `EROFS` under a strict sandbox but works in dev / on non-systemd * hosts where there is no `ProtectSystem`. Caller keeps verify + restart. * * @returns `restartHandledExternally` — `true` when the updater unit was used * (the unit's ExecStartPost restarts the agent), `false` for the root and * sudo-npm fallback paths (caller verifies the install and triggers restart). */ private performUpdate; /** * Run the in-process `npm install -g` with cache clearing and retries. * Uses sudo for non-root users (requires appropriate sudoers config). * Used by the root path and the non-root/no-unit best-effort fallback. */ private performNpmInstall; /** * Verify the update was successful by checking the installed version. */ private verifyUpdate; /** * Perform real health check by spawning new binary and verifying it responds. * This catches issues like missing dependencies, corrupted installs, etc. */ private performHealthCheck; /** * Find the path to the globally installed binary. */ private findInstalledBinaryPath; /** * Run a health check command on the binary. */ private runBinaryHealthCheck; /** * Rollback to previous version after failed update. * Uses sudo for non-root users. */ private rollback; /** * Request daemon restart via SIGTERM. * systemd will restart us with the new version. * Ensures logs are flushed before sending signal. */ private requestRestart; } /** * Load update config from environment or use defaults. */ export declare function loadUpdateConfig(): UpdateConfig; //# sourceMappingURL=npm-auto-update.d.ts.map