/** * `spectral update` — standalone binary self-update. * * Fetches the latest release manifest from api.aexol.ai, compares versions, * downloads the new artifacts, verifies their SHA256 digests, and installs them * over the current installation. * * Two manifest shapes are supported: * * - binary only (every manifest ever published): `url` + `sha256` pin the * single-file binary. Only the binary is replaced — the companion assets an * `install.sh` user has next to it (`data/systems/`, `mcp/`, * `app-bridge.bundle.js`) keep the previous version, which is called out * with a warning telling the user to re-run `curl | sh`. * - release zip (ADDITIVE `zipUrl` + `zipSha256`, emitted by * cli-node/scripts/generate-cli-manifest.sh once scripts/package-standalone.sh * produced the archive): the zip carries the binary *and* the * `-data/` companion directory. Both are installed with the same * replace-don't-merge semantics as scripts/install.sh, so files deleted * between releases do not linger. * * Nothing is installed unverified: the zip digest is checked before unpacking, * the inner binary digest before swapping, and any mismatch aborts without * touching the current installation. * * Non-standalone install modes (npm global install) are detected and the user * is pointed at the correct update path instead. */ /** Shape of the per-platform latest release manifest. */ export interface ReleaseManifest { version: string; platform: string; url: string; sha256: string; /** * Additive fields: manifests published before the release-zip scheme do not * have them, so every reader must treat them as optional. When both are * present, the release zip (binary + `-data/` companion assets) can * be verified and installed instead of the raw binary alone. */ zipUrl?: string; zipSha256?: string; } /** * Spawn a helper process (`unzip`, `tar`) and resolve with its exit code. * Rejects when the executable cannot be started at all (ENOENT), which is how * callers tell "tool missing" apart from "tool failed". */ export type RunCommand = (cmd: string, args: string[]) => Promise; /** * Everything I/O-shaped in the update flow, injectable for tests: the flow * (verify → unpack → swap → stamp) is then covered without network access, real * archives, or a real standalone binary. */ export interface UpdateDeps { out?: (t: string) => void; err?: (t: string) => void; fetchManifest?: (platform: string) => Promise; download?: (url: string, destPath: string) => Promise; run?: RunCommand; /** Executable to update (defaults to `process.execPath`). */ execPath?: string; /** Current version (defaults to `getVersion()`). */ version?: string; /** Standalone detection (defaults to `isStandaloneBinary()`). */ standalone?: boolean; /** Release platform slug (defaults to `detectPlatform()`). */ platform?: string | null; } /** * Replace the companion assets next to the binary with the ones from the zip. * * Semantics are those of scripts/install.sh: the `-data/` directory is * placed next to the binary and *then* its contents are copied alongside it, * with the previous `data/`, `mcp/` and `app-bridge.bundle.js` removed first — * replace, not merge, so files deleted between releases do not linger. * * SAFETY: `installDir` is `dirname(process.execPath)` — the directory that * *holds* the running binary. Only the explicitly named companion paths inside * it are ever removed, and only for a real absolute path, so a bad `execPath` * can never wipe a directory that contains the binary. */ export declare function swapCompanionAssets(params: { stageDir: string; installDir: string; }): Promise<{ dataDirName: string | null; replaced: string[]; }>; /** * Run the `spectral update` subcommand. * * @param args remaining CLI args after the `update` subcommand * @param deps injectable I/O for tests (network, spawns, exec path, version) */ export declare function runUpdate(args: string[], deps?: UpdateDeps): Promise; //# sourceMappingURL=update.d.ts.map