/** * `atomic auth print-api-key` / `atomic auth print-bearer-token` and the * explicit `atomic auth check --credentials` export path. * * The only door in Atomic whose purpose is emitting a secret. Everything here * exists to keep that egress narrow: * - the secret leaves as a `Secret`, which cannot be interpolated, serialized, * or inspected, and is consumed exactly once by the stdout writer; * - every export has an explicit target: the print commands require `--model`, * and auth checks require `--provider` or an exact `--model` plus * `--credentials`; * - there is no `--output ` and no clipboard path — stdout only; * - every failure is a distinct exit code, and stdout stays empty on all of * them but one — `CredentialTruncated` (exit 9) exists to report the case * where it cannot be, because bytes already left and cannot be recalled; * - a failed OAuth refresh never mutates the stored credential. * * One residual, and it is upstream: when a model infers several eligible * providers, each is asked for its credential before the ambiguity is known, so * a *successful* refresh can rotate and persist a credential this command then * declines to emit (exit 3). See the note on the `getAuth()` call in the * candidate loop. */ import { inspect } from "node:util"; import { ModelsError } from "@bastani/pi-ai"; import type { ModelRuntime } from "../core/model-runtime.js"; import type { Args } from "./args.ts"; export type CredentialPrintKind = "api_key" | "bearer_token"; /** Bearer tokens are refreshed unless they still have this much life left. */ export declare const DEFAULT_BEARER_TOKEN_MIN_EXPIRY_MS: number; /** * Failure taxonomy. Each member owns one exit code, so a caller can branch on * the status without parsing stderr. */ export type CredentialPrintErrorCode = "Usage" | "NoCredentialConfigured" | "ProviderAmbiguous" | "KindUnsupportedForProvider" | "RefreshFailed" | "MinValidityUnreachable" | "OAuthUnavailable" | "CredentialNotEmitted" | "CredentialTruncated"; /** * Exported so a test can walk the whole taxonomy: every declared code has to * name an exercised path, and the record type makes a new member without an * exit code a compile error rather than a review miss. * * Every one of these leaves stdout empty except `CredentialTruncated`, which * exists to report the one case where it cannot be: bytes already went out and * cannot be recalled. `STDOUT_EMPTY_ON_EXIT` names that split so the taxonomy * test asserts it rather than assuming it. */ export declare const EXIT_CODES: Record; /** The codes whose contract is an empty stdout. See `EXIT_CODES`. */ export declare const STDOUT_EMPTY_ON_EXIT: ReadonlySet; export declare class CredentialPrintError extends Error { readonly code: CredentialPrintErrorCode; readonly exitCode: number; constructor(code: CredentialPrintErrorCode, message: string, options?: { cause?: unknown; }); } /** * A credential in transit. * * Every path that would copy the value into a string is closed: `toString`, * `toJSON`, and `Symbol.toPrimitive` throw rather than return, so a template * literal, `JSON.stringify`, or string concatenation fails loudly instead of * leaking into a log line, a transcript, or an error message. `inspect` — which * `console.log` and Node's error formatting use — renders a placeholder. * * `take()` is the single exit, and it works once. */ export declare class Secret { #private; constructor(value: string); /** Consume the secret. A second call is a bug, not a second read. */ take(): string; toString(): never; toJSON(): never; [Symbol.toPrimitive](): never; [inspect.custom](): string; } /** Public readiness fields emitted with a credential under `auth check --json --credentials`. */ export interface CredentialJsonFields { status: string; provider: string; authType?: string; } /** * The single credential egress in `src`. * * `credentialPayload` opens the secret only while building this one payload, * and this guarded write is the only path that hands it to real stdout. A * caller — `main.ts` included — receives a `Secret` it cannot read, print, or * serialize, so it cannot add a second export path without crossing this door. * `test/credential-print.test.ts` pins both the `take()` sites and every * data-bearing real-stdout write in `packages/coding-agent/src`. * * A failed payload write is answered from the stream rather than from the shape * of the error, because `bytesWritten` says how much of the credential actually * left and the right report differs for each amount. * * Nothing left — the write call threw, or the callback failed with the counter * unmoved — is `CredentialNotEmitted` (exit 8) with stdout provably empty, * which is what that exit promises. It must not fall through to the caller's * credential-resolution catch, which would report exit 2. * * All of it left and the failure came after: the caller holds the credential it * asked for, so this is reported on stderr with the exit code left at 0, for * the same reason the trailing drain is. A non-zero exit over a stream carrying * a whole credential is the thing this door never produces. * * Part of it left is neither. Those bytes cannot be recalled, so stdout is not * empty and cannot be made empty; but they are a fragment, not a credential, * and reporting success over them would hand a caller a truncated secret it * cannot tell from a whole one. That case gets `CredentialTruncated` (exit 9), * the one code in this taxonomy whose contract is *not* an empty stdout — * stated as such in `EXIT_CODES` and asserted through `STDOUT_EMPTY_ON_EXIT`, * rather than left for a caller to discover. */ export declare function emitCredential(secret: Secret, jsonFields: CredentialJsonFields | undefined): Promise; /** * The single place a thrown value becomes an exit code for this door. * * `main.ts` routes every failure through here. Every code it can produce leaves * stdout empty, so an exit code from this door never contradicts the stream. */ export declare function toCredentialPrintError(error: unknown): CredentialPrintError; export declare function validateCredentialPrintArgs(args: Args): void; export { redactCredentialShapes } from "../utils/credential-redaction.js"; /** * The literal phrase pi-ai throws when a refresh itself failed, from inside * `credentials.modify` and therefore before anything is persisted. This is the * only oauth-coded error that can claim the stored credential was left alone. */ export declare const OAUTH_REFRESH_FAILED_PHRASE = "OAuth refresh failed for"; /** * The literal phrase pi-ai throws when a refresh succeeded but the rotated * token still expires inside the requested window. It is the only signal that * separates exit 6 from exit 5. */ export declare const OAUTH_EXPIRES_TOO_SOON_PHRASE = "expires too soon"; /** * pi-ai raises three different `oauth`-coded failures from * `dist/auth/resolve.js`, and only one of them is a failed refresh. * * `OAuth auth derivation failed for ` is thrown after * `credentials.modify` has already persisted a rotated credential, and also on * the branch where no refresh was attempted at all — so it gets its own code * and a message that makes no claim about the stored credential. Treating it as * `RefreshFailed` asserted a rollback the command never verified. * * Classification reads the raw message, because redaction could rewrite the * phrase it matches on; only the text that becomes a user-facing message is * redacted. The unredacted error stays reachable as `cause`, which this door * never logs. * * `test/credential-print.test.ts` pins both phrases against the installed * pi-ai build, so an upstream rewording fails the suite rather than silently * collapsing exit 6 into exit 5. */ export declare function classifyOAuthFailure(error: ModelsError): CredentialPrintError; /** * Resolve exactly one credential for a named provider/model pair. * * Goes through `ModelRuntime.getAuth()`, the same request-auth path a real * model call uses, so an OAuth credential is refreshed and persisted by the one * implementation that already handles concurrent refresh under a lock. */ export declare function resolveCredentialForPrint(args: Args, modelRuntime: ModelRuntime, kind: CredentialPrintKind, minExpiryMs?: number): Promise; //# sourceMappingURL=credential-print.d.ts.map