UNPKG

@capgo/cli

Version:
87 lines (86 loc) 4.06 kB
/** Stable shape written to disk; bump schemaVersion if breaking changes are made. */ export interface BuildOutputRecord { schemaVersion: 1; jobId: string; appId: string; platform: 'ios' | 'android'; buildMode: 'debug' | 'release'; status: string; outputUrl: string | null; qrCodeAscii: string | null; qrCodePngPath: string | null; finishedAt: string; } export interface WriteBuildOutputRecordInput { jobId: string; appId: string; platform: 'ios' | 'android'; buildMode: 'debug' | 'release'; status: string; outputUrl: string | null; } /** * Write a build-output record to `recordPath` (JSON) and, when a URL is available, * a PNG QR code to `<recordPath>.qr.png`. Returns the parsed record exactly as * written so callers can log a summary without re-reading the file. * * Failures rendering the PNG are non-fatal — the JSON is always written. The * record's `qrCodePngPath` field is null when the PNG could not be produced. * * Hardening (hostile-review 2026-06-12): the record and PNG paths are unlinked * before writing so a pre-planted symlink is replaced instead of followed; the * record is written with mode 0600 (outputUrl is a signed download URL); a * created parent directory gets mode 0700; and when the record lives under the * shared tmpdir, a parent directory that is a symlink or owned by another user * is refused. */ export declare function writeBuildOutputRecord(recordPath: string, input: WriteBuildOutputRecordInput, onWarn?: (msg: string) => void): Promise<BuildOutputRecord>; /** * Returns a deterministic temp-file path for the build output record for a * given (appId, platform) pair. Both the build hand-off (command emit) and * the confirm (record read) call this helper so that the path is never passed * back and forth across an MCP boundary. * * appId is sanitized: all `/` and `\` characters are replaced with `_`, and * any `..` sequences are replaced with `_`, to prevent path traversal. * * The record lives in a per-user `capgo-build-records-<uid>` subdirectory * (created with mode 0700 by `writeBuildOutputRecord`) so that on shared-tmp * systems another user can neither pre-create nor read the record file * (hostile-review 2026-06-12). */ export declare function defaultBuildRecordPath(appId: string, platform: 'ios' | 'android'): string; /** * Remove a build output record and its companion QR PNG. Absent paths are a * no-op. Called before a new build hand-off so a record left behind by an * earlier build can never be read as the new build's result. */ export declare function removeBuildOutputRecord(recordPath: string): Promise<void>; /** * Thrown by `readBuildOutputRecord` when the record file EXISTS but cannot be * used: unreadable (permissions), a symbolic link, not valid JSON (e.g. a * truncated write), or an unexpected shape. Distinct from the `null` return * (no record yet) so callers polling for a build result can surface the * failure instead of waiting forever. */ export declare class BuildRecordReadError extends Error { readonly recordPath: string; constructor(message: string, recordPath: string); } /** * Read a build output record from `path`. * * Returns `null` ONLY when the file does not exist yet (ENOENT — the build has * not finished). Every other failure mode (unreadable file, a symlink at the * record path, malformed JSON, missing/wrong-type fields) throws * `BuildRecordReadError`: a present-but-corrupt record is a surfaced failure, * never "still waiting". * * Shape rules (hostile-review 2026-06-12): * - every record owes the `jobId`/`status`/`outputUrl` trio (forward-tolerant * baseline for future schemaVersions); * - a `schemaVersion: 1` record is validated strictly against the full * `BuildOutputRecord` shape — the v1 writer has always emitted it, and * checkBuild correlates `appId`/`platform` against the build it launched. */ export declare function readBuildOutputRecord(path: string): Promise<BuildOutputRecord | null>;