@capgo/cli
Version:
A CLI to upload to capgo servers
95 lines (94 loc) • 4.01 kB
TypeScript
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>;