/** * bake-runner PURE protocol layer (IMAGE-API-DESIGN.md §P2.12) — the line→frame mapping + the small policy * helpers, factored free of I/O so they are unit-testable WITHOUT the privileged build-host build host (the runner * itself can only be exercised live; this is the part a unit test pins). * * The runner spawns `build.sh --json` and forwards each STDOUT line to image-api's ingest. build.sh * emits a small set of JSON object lines (`resolved | state | step | image | manifest | done`) on stdout and tees * the raw `docker build` output to `$BUILD_DIR/build.log` (NOT onto stdout). On failure it does `tail -40 build.log * >&2` — that is STDERR, the failure tail, NOT JSON; we parse JSON ONLY from stdout (a naive JSON.parse over both * crashes the parser — §P2.5). Each forwarded frame carries an incrementing `lineOrd` (at-least-once ingest dedupe; * image-api's `uq_line` ignores a re-sent ordinal — §P2.10). */ /** One build.sh stdout line, already JSON-parsed into an object (or a marker for an unparseable line). */ export type BuildShEvent = { event: "resolved"; [k: string]: unknown; } | { event: "state"; state: string; [k: string]: unknown; } | { event: "step"; step: string; status: string; [k: string]: unknown; } | { event: "image"; tag?: string; digest?: string; localId?: string; [k: string]: unknown; } | { event: "manifest"; path?: string; sha256?: string; [k: string]: unknown; } | { event: "done"; status: string; [k: string]: unknown; } | { event: string; [k: string]: unknown; }; /** The frame the runner POSTs to `…/ingest` (image-api maps it to an event-log row). `lineOrd` is the dedupe key. */ export interface IngestFrame { /** build.sh's `event` (or "log" for an unparseable/raw line). */ event: string; /** Monotonic per-bake ordinal — image-api dedupes on (bakeId, lineOrd) so an at-least-once re-send is a no-op. */ lineOrd: number; /** The remaining build.sh fields (event already lifted out), passed through verbatim. */ [k: string]: unknown; } /** * Parse ONE raw stdout line into a build.sh event, or null when it is not a JSON object line (a blank line, a * stray non-JSON print). build.sh's protocol is strictly one JSON OBJECT per stdout line (`jq -nc`), so anything * that is not a `{…}` object is treated as non-protocol noise the caller forwards as a `log` frame (NOT crashed on). */ export declare function parseBuildShLine(raw: string): BuildShEvent | null; /** * Map ONE stdout line → the ingest frame the runner POSTs (the PURE core a unit test pins). A parseable protocol * line is forwarded under its own `event`; an unparseable stdout line becomes an `info` log frame (build.sh's * structured stdout is naturally quiet, so this is rare — usually a generator print). The raw `docker build` tail * never reaches here: it is on stderr (the runner only feeds stdout through this) — §P2.5. */ export declare function lineToFrame(raw: string, lineOrd: number): IngestFrame; /** * Synthesize the `docker-build status:running elapsedSec` heartbeat step (§P2.2) — emitted every ~30s during the * long, silent `docker build` (with logs off) so a resuming reader sees forward motion instead of a dead spinner. * It is a synthesized frame, so it still carries a `lineOrd` (image-api dedupes the same way). */ export declare function dockerBuildTickFrame(lineOrd: number, elapsedSec: number): IngestFrame; /** * Synthesize the terminal `done` the runner sends when build.sh did NOT emit one (a hard cancel/timeout kill — * build.sh has no SIGTERM trap, so a killed process never prints `done:CANCELLED`/`done:FAILED`; the runner MUST * synthesize it — §P2.11). `cancelled` → `CANCELLED`; a timeout → `FAILED exitCode:124`. */ export declare function synthDoneFrame(lineOrd: number, kind: "cancelled" | "timeout", detail?: { error?: string; }): IngestFrame; /** * The runner-side terminal `done` for a build.sh CHILD that exited non-zero WITHOUT printing its own `done` (build.sh * normally dies via `die()` which DOES print a `done:FAILED`, but a SIGKILL or a crash before the trap can leave the * child exiting non-zero silently). The runner backstops with a FAILED done carrying the child exit code + the * captured stderr failure tail (the `tail -40 build.log` build.sh prints on failure), redacted by image-api on store. */ export declare function exitDoneFrame(lineOrd: number, exitCode: number, stderrTail: string): IngestFrame; /** Keep only the last `n` lines of a (possibly long) stderr tail — bounds the error string we forward. */ export declare function lastLines(text: string, n: number): string; /** * The profile-aware HARD DEADLINE (§P2.7/§P2.11) the runner enforces by killing the process group. flutter/full are * the heavy profiles (~60min); everything else ~45min. A bands build ("custom") is treated as heavy if it includes * a heavy band. The reaper backstops this; the runner is the primary enforcer (it owns the child). */ export declare function deadlineMsForProfile(profile: string, bands: string[] | null | undefined): number; /** Parse `df -P -BG /data` (POSIX, 1G blocks) → free GiB on the build-host data fs. Returns null if unparseable * (the caller then fails OPEN — a df parse miss must not block a bake; the reaper still guards a mid-build fill). */ export declare function parseDfFreeGb(dfOutput: string): number | null; /** The path build.sh's `done`/`manifest` line points at (a build-host FS path). The done carries `manifest:`, the * manifest event carries `path` — the runner reads whichever is present off disk for the auto-register body. */ export declare function manifestPathFromLine(doneLine: Record, lastManifestPath: string | null): string | null; /** Pull the post-push runtime digest the auto-register should key on. build.sh rewrites DIGEST from RepoDigests[0] * AFTER a push, so the `done`/`image` line's `digest` is already the post-push registry RepoDigest on a push bake and the * LOCAL_ID on a no-push bake — the runner forwards it verbatim; this just normalizes the field name (§P2.10). */ export declare function digestFromDoneLine(doneLine: Record, lastImageDigest: string | null): string | null; //# sourceMappingURL=protocol.d.ts.map