import { Settings } from "../config/settings"; import { type UpdateChannel } from "../config/update-channel"; import { type NotificationProvider } from "../sdk/bus/config"; import { type GithubReleaseLookupOptions } from "./github-release"; export interface UpdateCommandOptions { force: boolean; check: boolean; channel?: UpdateChannel; } interface ReleaseInfo { tag: string; version: string; /** Registry the version came from. Release binaries still come from GitHub. */ registry: string; /** Config problems that did not stop the lookup but changed its outcome. */ warnings: string[]; } /** Result from running the installed binary and parsing its reported version. */ export interface InstalledVersionVerification { ok: boolean; actual?: string; path?: string; smokeTestFailed?: boolean; smokeTestOutput?: string; cleanupWarning?: string; } export interface PackageManagerUpdateResult { exitCode: number | null; text: () => string; } export type PackageManagerUpdateRunner = (expectedVersion: string) => Promise; export interface PackageManagerUpdateOptions { managerName: string; expectedVersion: string; runInstall: PackageManagerUpdateRunner; verifyInstalledRuntime: (expectedVersion: string) => Promise; printRecoveredVerification?: (expectedVersion: string) => void; } /** Paths and verifier used while replacing a downloaded binary update. */ export interface BinaryReplacementOptions { targetPath: string; tempPath: string; backupPath: string; expectedVersion: string; verifyInstalledVersion: (expectedVersion: string) => Promise; } /** * Parse update subcommand arguments. * Returns undefined if not an update command. */ export declare function parseUpdateArgs(args: string[]): UpdateCommandOptions | undefined; export type PackageManagerTarget = { manager: "npm"; packageName: string; }; export type UpdateTarget = { method: "bun"; } | { method: "npm"; packageName: string; } | { method: "binary"; path: string; } | { method: "migrate"; path: string; previousPath?: string; }; type PathPlatform = NodeJS.Platform; type PackageExists = (packageName: string, packageRoot: string) => boolean; export declare function resolveUpdateMethodForTest(ompPath: string, bunBinDir: string | undefined): "bun" | "npm" | "binary"; export declare function resolveNpmManagedTargetForTest(ompPath: string, platform: PathPlatform, packageExists: PackageExists): PackageManagerTarget | undefined; export declare function defaultUserBinaryPath(platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): string; export declare function isProtectedSourcePathForTest(filePath: string): boolean; export declare function defaultUserBinaryPathForTest(platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): string; /** Lookup options for the GitHub release check. */ export interface LatestReleaseLookupOptions extends GithubReleaseLookupOptions { channel?: UpdateChannel; } export declare function getLatestReleaseForTest(options: LatestReleaseLookupOptions): Promise; export declare function compareVersionsForTest(a: string, b: string): number; export declare function resolveGjcPathForTest(options: { compiled: boolean; execPath: string; whichPath: string | undefined; }): string | undefined; export declare function parseReportedVersionForTest(output: string): string | undefined; export declare function formatBinaryDownloadFailureMessageForTest(binaryName: string, url: string, status: string | number, platform?: NodeJS.Platform, registryNote?: string): string; export declare function buildReleaseBinaryUrlForTest(version: string, platform?: NodeJS.Platform, arch?: string): string; export declare function formatManualUpdateInstructionsForTest(platform?: NodeJS.Platform): string; export declare function formatVerificationFailureForTest(result: InstalledVersionVerification, expectedVersion: string): string; export declare function recoverWindowsUpdateJournal(journalPath: string): Promise; /** * Atomically replace the installed binary and roll back if version verification fails. */ export declare function replaceBinaryForUpdate(options: BinaryReplacementOptions): Promise; export declare function runPackageManagerUpdateForTest(options: PackageManagerUpdateOptions): Promise; export declare function fsyncFileForTest(filePath: string): Promise; /** Injectable steps of the binary update flow (seams for testing ordering). */ export interface BinaryUpdateFlow { download(url: string, tempPath: string): Promise; fsync(filePath: string): Promise; replace(options: BinaryReplacementOptions): Promise; verifyInstalledVersion(expectedVersion: string): Promise; /** Best-effort cleanup of the temp file when the flow aborts before replace. */ removeTemp?(filePath: string): Promise; /** Called once fsync has succeeded, right before replacement begins. */ beforeReplace?(): void; } /** * Orchestrate download → fsync → replace → verify with a strict ordering * contract: the downloaded temp binary MUST be flushed to stable storage * before it is published (renamed into place) or exec'd for verification. * * If fsync fails the temp bytes are not durable, so we abort before * replacement/verification and clean up the temp file rather than installing a * possibly-truncated binary. */ export declare function runBinaryUpdateFlow(targetPath: string, url: string, expectedVersion: string, flow: BinaryUpdateFlow): Promise; /** * Run the update command. */ export interface UpdateCommandDependencies { getLatestRelease?: (options?: LatestReleaseLookupOptions) => Promise; resolveUpdateTarget?: () => Promise; performUpdate?: (target: UpdateTarget, expectedVersion: string, registry?: string) => Promise; refreshInstalledDefaultSkills?: () => Promise; settings?: () => Promise; stopDaemon?: (settings: Settings) => Promise; restartDaemon?: (settings: Settings) => Promise; recoverNotifications?: (settings: Settings) => Promise; runPostUpdateRecovery?: (runtimePath: string) => Promise; exit?: (code: number) => never; } export type PostUpdateRecoverySpawn = (argv: string[]) => Promise; export type PostUpdateRecoverySupportCheck = (runtimePath: string) => Promise; export type LegacyRecoveryDaemonKinds = () => Promise; /** * A complete, non-quarantined provider with provider-level desired intent is a * durable managed-notify setup. The global switch is deliberately excluded: * disabling delivery must not leave credential-backed daemon locks unrecovered. */ export declare function hasManagedNotifySetup(settings: Settings): boolean; export declare function runPostUpdateRecoveryForTest(runtimePath: string, spawn?: PostUpdateRecoverySpawn, supportsRecovery?: PostUpdateRecoverySupportCheck, managedKinds?: LegacyRecoveryDaemonKinds): Promise; export declare function runManagedNotifyRecovery(deps: Pick): Promise; /** How the update command should proceed after comparing versions. */ export interface UpdateDecision { install: boolean; kind: "up-to-date" | "new-version" | "switch-back" | "force" | "migrate"; } /** * Decide whether to install after comparing the channel's release with the * installed version. * * A nightly install is semver-newer than every stable release (nightlies * version as stable-max-patch+1), so a plain comparison would pin the user on * nightly forever: switching back to stable must install even though the * target is semver-lower. Only a stable lookup from a nightly build is an * intentional switch-back — the reverse (a same-core nightly behind the * installed stable) still requires --force. */ export declare function resolveUpdateDecision(options: { comparison: number; force: boolean; channel: UpdateChannel; currentVersion: string; migrate?: boolean; }): UpdateDecision; export declare function runUpdateCommand(opts: UpdateCommandOptions, deps?: UpdateCommandDependencies): Promise; /** * Print update command help. */ export declare function printUpdateHelp(): void; export {};