import type { BuildOutputRecord } from '../../output-record.js';
import type { Platform } from './contract.js';
export type BuildJobStatus = 'running' | 'completed' | 'failed' | 'cancelled' | 'unknown';
export interface BuildJobResult {
    jobId: string;
    status: BuildJobStatus;
    platform: Platform;
    appId: string;
    /** Absolute path to the local log file the user can tail. NEVER read by the agent directly. */
    logsPath: string;
    outputUrl?: string;
    qrCodeAscii?: string;
    error?: string;
    /** True when start found an in-flight build for this target and returned it instead of starting a second. */
    alreadyRunning?: boolean;
}
/** A handle to the spawned build process (injectable so tests never spawn anything). */
export interface BuildChild {
    pid: number;
    kill: (signal?: NodeJS.Signals) => void;
    /** Resolves with the exit code (or null on signal) when the process exits. */
    exited: Promise<number | null>;
}
export interface BuildJobDeps {
    /** Spawn the build command, streaming stdout+stderr to `logPath`. Returns a handle. */
    spawnBuild: (args: {
        appId: string;
        platform: Platform;
        recordPath: string;
        logPath: string;
    }) => BuildChild;
    buildRecordPath: (appId: string, platform: Platform) => string;
    /** Read the build record; resolves null when absent, THROWS on a present-but-corrupt record. */
    readBuildRecord: (path: string) => Promise<BuildOutputRecord | null>;
    /** Remove a stale record before a fresh build so wait can't read last run's result as this one's. */
    clearBuildRecord?: (path: string) => Promise<void>;
    /** Absolute path of the local log file for a target. */
    logPath: (appId: string, platform: Platform) => string;
    /** Read `logPath` from byte offset `cursor`; returns new text, the next cursor, and whether at EOF. */
    readLogSlice: (logPath: string, cursor: number) => Promise<{
        text: string;
        nextCursor: number;
        eof: boolean;
    }>;
    /** Best-effort cloud cancel by the cloud jobId (POST /build/cancel/:jobId). Optional. */
    cancelCloud?: (cloudJobId: string) => Promise<void>;
    /** Injected so the bounded wait loop is deterministic in tests. */
    sleep: (ms: number) => Promise<void>;
    now: () => number;
}
/** Bounded wait window (seconds), kept under the ~60s client tool-call timeout. */
export declare const DEFAULT_WAIT_SECONDS = 40;
export declare const MAX_WAIT_SECONDS = 59;
/** Drop every tracked build job (test isolation only). */
export declare function clearAllBuildJobs(): void;
/**
 * Start (or re-attach to) the cloud build for a target. Idempotent: if a build
 * for this (appId, platform) is already running this session, returns that job
 * instead of spawning a second.
 */
export declare function startBuild(deps: BuildJobDeps, args: {
    appId: string;
    platform: Platform;
}): Promise<BuildJobResult>;
/**
 * Bounded wait: block up to `timeoutSeconds` (default 40, max 59 — kept under the
 * client tool-call timeout), returning the instant the build reaches a terminal
 * state, otherwise 'running' for the caller to re-call.
 */
export declare function waitBuild(deps: BuildJobDeps, args: {
    jobId: string;
    timeoutSeconds?: number;
}): Promise<BuildJobResult>;
/** Non-blocking status peek. */
export declare function statusBuild(deps: BuildJobDeps, args: {
    jobId: string;
}): Promise<BuildJobResult>;
/**
 * Drain new log output since `cursor`. `eof` is true only once the build is
 * terminal AND the read reached the end of the file, so a streamer knows to stop.
 */
export declare function buildLogs(deps: BuildJobDeps, args: {
    jobId: string;
    cursor?: number;
}): Promise<{
    jobId: string;
    text: string;
    nextCursor: number;
    eof: boolean;
}>;
/** Cancel a running build: kill the local child + best-effort cloud cancel. */
export declare function cancelBuild(deps: BuildJobDeps, args: {
    jobId: string;
}): Promise<BuildJobResult>;
