/** * Operator process injection, shipped beside the agent command. * * `fz run -- ` — hand secrets to a process that cannot ask for them. * * This is the FALLBACK, and it is worth saying so at the top of the file. The * agent serves secrets over a unix socket and anything using `@forgezero/vault` * reads them there, which means a rotation reaches a running process. An * injected environment cannot rotate: a process started before a rotation runs * on the old value until somebody restarts it, and nothing tells them. * * It exists because a great deal of software will never read a socket, and * "rewrite your build tool" is not an adoption path. * * ## What an environment variable costs * * On Linux `/proc//environ` is readable by the same user, and any child * inherits everything. That is the standard trade and it is not hidden here: * the whole point of the socket path is that it does not make that trade. * * Values never reach argv, because argv is world-readable in `ps` — which is a * different and much worse exposure than the environment. */ export declare class RunError extends Error { readonly code: 'NO_COMMAND' | 'NO_SEPARATOR' | 'BAD_NAME'; constructor(code: 'NO_COMMAND' | 'NO_SEPARATOR' | 'BAD_NAME', message: string); } /** * Split fz's own arguments from the child's. * * `--` is the boundary and it is not optional. Without it `fz run node --watch` * would have fz eat `--watch`, and the failure is a flag that silently does not * reach the program somebody is debugging. * * Everything after the FIRST separator belongs to the child, separators * included: `fz run -- sh -c 'x -- y'` has to pass the inner one through. */ export declare function splitAtSeparator(argv: readonly string[]): { own: string[]; command: string[]; }; export interface MergeOptions { /** Keep an existing value when both sides have the name. Default false. */ preserveEnv?: boolean; } export interface MergeResult { env: Record; /** Names present in both. Reported so a collision is never silent. */ collisions: string[]; /** Names the shell cannot express, refused rather than mangled. */ refused: string[]; } /** * Merge vault values into an environment. * * The vault wins by default: a caller running `fz run` is asking for vault * values, and quietly preferring a stale variable already in the shell is how * somebody debugs a wrong credential for an hour. * * `--preserve-env` inverts it for the case where a wrapper genuinely wants to * override. Either way the COLLISIONS are returned so the caller can say which * names were affected. Silence is what makes this confusing, not the direction. */ export declare function mergeEnvironment(base: Record, secrets: Record, options?: MergeOptions): MergeResult; /** * What to print before handing over. * * Names only. A wrapper that echoed values would put every secret into whatever * captured the build log, which is usually the one place they are kept longest. */ export declare function describeInjection(result: MergeResult, count: number): string; /** * Turn a child's exit into this process's exit. * * A wrapper that always exits 0 breaks every CI pipeline it is put in front of, * silently, by turning a failed build into a passing one. A signal death is * reported the way a shell reports it — 128 plus the signal number — so * `fz run -- make` behaves like `make` for anything reading the code. */ export declare function exitCodeFor(status: { code: number | null; signal: string | null; }): number; /** * Start the child and become its exit code. * * Separated from the command handler so it can be driven by a test with real * processes. The interesting behaviour is entirely here — inherited stdio, * forwarded signals, propagated status — and none of it is provable against a * mock. */ export declare function spawnWith(command: readonly string[], env: Record, report?: (line: string) => void, options?: { cwd?: string; }): Promise;