/** * UpdateService — the composition root for the cadre-host update flow. * * Background check cadence: once at construction time (`check()` called * explicitly by `cadre-host start` after the management API binds) and a * 24-hour interval timer thereafter. Failures are silent — UI consumers see * `lastChecked` only when a fetch succeeds. * * Apply flow: re-fetch + re-verify the manifest (never trust the cached * `available`), record `applyInProgress`, run `npm install -g`, restart the * service via the optional `ServiceRestarter`, and roll back on failure. */ import { type NpmExecutor, type ServiceRestarter } from './apply.js'; import { type UpdateApplyResult, type UpdateSettings, type UpdateState } from './types.js'; export interface UpdateServiceOptions { /** cadre-host data directory; UpdateState file lives here. */ dataDir: string; /** Currently-running package version (from package.json). */ currentVersion: string; /** Manifest URL. Defaults: env CADRE_HOST_UPDATE_MANIFEST_URL → DEFAULT_MANIFEST_URL. */ manifestUrl?: string; /** Fetcher (test injection). */ fetcher?: typeof fetch; /** npm runner (test injection). */ npmExecutor?: NpmExecutor; /** Service-host restart hook (test injection / wiring from cadre-host start). */ restart?: ServiceRestarter; /** Initial settings (autoApply etc) — typically sourced from host.config.json. */ settings?: UpdateSettings; /** Periodic-check interval; defaults to 24h. */ checkIntervalMs?: number; /** npm install timeout; defaults to 5m. */ npmTimeoutMs?: number; } /** Handlers for the management-API listener. */ export interface UpdateHandlers { getState(): Promise; postApply(): Promise; getSettings(): Promise; putSettings(body: { autoApply?: boolean; manifestUrl?: string; }): Promise; } export declare class UpdateService { private readonly store; private readonly currentVersion; private readonly fetcher; private readonly npmExecutor; private readonly restart; private readonly checkIntervalMs; private readonly npmTimeoutMs; private manifestUrl; private settings; private timer; private applying; /** Persisted settings changes are bubbled out via this callback (wired in cadre-host start). */ private onSettingsChange?; constructor(opts: UpdateServiceOptions); /** * If `update-state.json` still records an in-flight apply at construction * time, the previous cadre-host process died mid-apply (OOM, power loss, * sigkill). Without intervention, every subsequent `apply()` would short- * circuit on `apply_in_progress` forever. We clear the marker and surface a * `lastError` so the UI banner can flag that an apply was interrupted — * the npm install may or may not have completed, so the operator should * verify with `npm ls -g`. */ private recoverInterruptedApply; /** Read the persisted state for UI consumption. */ getState(): Promise; /** Current settings (in-memory). */ getSettings(): UpdateSettings; /** * Update settings (autoApply / manifestUrl). The optional `onSettingsChange` * hook is invoked so the caller can persist to host.config.json. */ putSettings(patch: { autoApply?: boolean; manifestUrl?: string; }): Promise; /** Register the persistence hook called after every settings mutation. */ onSettingsChanged(handler: (next: UpdateSettings) => void | Promise): void; /** * Background manifest check. Never throws — fetch/parse failures log at * debug; signature failures are recorded as `lastError` so the UI can warn. */ check(): Promise; /** * Trigger an apply. Re-verifies the manifest, runs npm install -g, attempts * a service restart, and rolls back on failure. Throws UpdateErrorException * on any hard failure; success returns the new state. */ apply(): Promise; /** Start the daily-check timer. `start()` ensures we don't double-arm. */ start(): void; /** Stop the daily-check timer. */ stop(): void; } /** * Typed handler facade — same shape as `createNatHandlers`. The local-UI * server consumes these. */ export declare function createUpdateHandlers(service: UpdateService): UpdateHandlers; export type { UpdateState, UpdateApplyResult, UpdateSettings, UpdateManifest, SignedManifest } from './types.js'; export { UpdateErrorException, type UpdateErrorCode } from './types.js'; export { compareVersions, parseVersion } from './version.js'; export { fetchManifest, verifyManifest, canonicalJson, signManifest, validateManifestFields } from './manifest.js'; export { UpdateStateStore } from './store.js'; export { applyUpdate, defaultNpmExecutor, type NpmExecutor, type ServiceRestarter } from './apply.js'; export { getReleasePublicKey, getReleasePublicKeyBase64, ed25519FromRaw, isPlaceholderReleaseKey, } from './release-key.js'; export { buildManifest, derivePublicKeyBase64, signAndSelfVerify, type ManifestFields } from './sign.js';