/** * Service-account access verification for `hikoutei setup`. * * After the spreadsheet is shared with the service account, the setup flow * verifies that the freshly created key can actually read the spreadsheet * (`spreadsheets.get`) before writing `.env`. Newly created keys and ACL * changes propagate asynchronously, so the check retries up to eight times * with the schedule 2, 4, 8, 16, 30, 30, 30 seconds between attempts (the * first attempt is immediate). Only propagation-class failures are retried, * and only when the corresponding resource was actually created this run: * invalid JWT signature is retried only when the key is fresh * (`keyFresh`), and 403/404 only when the writer permission was created or * upgraded this run (`shareFresh`). 429 quota and 5xx server errors always * retry. Any other 4xx, reused-key/reused-share propagation failures, * malformed or mismatched success payloads, and network failures fail * immediately with `sa_access_verify_failed`. * * The Sheets client is created through an injectable factory so tests can * script failures without network access or credentials. The verifier * authenticates with the validated key credential IN MEMORY (promoted by * the secure key read; never a `keyFile` path reopen), so a mid-run * replacement of the key file cannot redirect verification; the private * key exists only in process memory for the run. */ /** Context describing which resources were freshly created this run. */ export interface VerifyFreshness { /** True when the service-account key file was created during this run. */ readonly keyFresh: boolean; /** True when the writer permission was created or upgraded during this run. */ readonly shareFresh: boolean; } /** * In-memory, validated service-account credentials for one verify run. * * The setup flow promotes the validated key file into these credentials at * the secure descriptor boundary and the verifier NEVER reopens the key * pathname (no `keyFile` auth): a replacement of the key file after * validation cannot redirect verification. The `private_key` exists only * in process memory for the run; it is never stored, logged, or included * in any result. */ export interface SaAccessCredentials { readonly client_email: string; readonly private_key: string; } /** Verifies the service account can access the spreadsheet; throws on failure. */ export interface SaAccessVerifier { verify(request: { keyPath: string; spreadsheetId: string; } & VerifyFreshness & { /** In-memory validated credentials; the verifier never reopens the key path. */ readonly credentials: SaAccessCredentials; /** Optional reporter for the bounded verify attempts and waits; never affects the result. */ readonly onVerifyProgress?: SaVerifyProgressReporter; }): Promise; } /** * Delays in milliseconds between the eight verify attempts. * * The first attempt is immediate; seven delays follow (2, 4, 8, 16, 30, 30, * 30 seconds) for a total of eight attempts and a worst-case ~2 minutes of * waiting for propagation. */ export declare const SA_VERIFY_RETRY_DELAYS_MS: readonly [2000, 4000, 8000, 16000, 30000, 30000, 30000]; /** Injectable timer used between retry attempts. */ export interface Sleeper { sleep(ms: number): Promise; } /** * Events reported by the bounded service-account access verify attempts. * * `check_started` / `check_completed` bracket one verify attempt (1-based * attempt within the bounded window: the immediate attempt is 1/8 and * each retry after a retryable failure advances 2/8..8/8). `wait_started` * precedes a scheduled sleep with the delay before the NEXT attempt and * carries the 1-based index of the attempt that just failed. Only numbers * are reported — never credentials, paths, ids, or raw provider payloads. * The reporter is decoupled from the progress UI so this module never * imports the CLI renderer; `setupFlow` wires it to the progress sink. */ export type SaVerifyProgressEvent = { readonly type: "check_started"; readonly attempt: number; readonly maxAttempts: number; } | { readonly type: "check_completed"; readonly attempt: number; readonly maxAttempts: number; } | { readonly type: "wait_started"; readonly attempt: number; readonly maxAttempts: number; readonly delayMs: number; }; /** * Optional reporter for the bounded verify attempts and waits. A throwing * callback is swallowed so it can never affect the verification result. */ export interface SaVerifyProgressReporter { (event: SaVerifyProgressEvent): void; } /** Total verify attempts the access check performs (immediate + the scheduled delays). */ export declare const SA_VERIFY_MAX_ATTEMPTS: number; /** The minimal Sheets surface the verifier needs (injectable in tests). */ export interface SpreadsheetGetClient { get(request: { spreadsheetId: string; }): Promise<{ readonly data: unknown; }>; } /** Options for the production verifier factory. */ export interface SaAccessVerifierOptions { readonly sleeper: Sleeper; /** * Builds the Sheets client for in-memory validated credentials; defaults * to the real SDK. The client must NEVER reopen the key pathname. */ readonly getClient?: (credentials: SaAccessCredentials) => SpreadsheetGetClient; } /** Production factory: verifies with the real Sheets SDK and an injected sleeper. */ export declare function createSaAccessVerifier(options: SaAccessVerifierOptions): SaAccessVerifier; /** * True when the failure is propagation-class for THIS run and deserves * another attempt. * * Retried: 429 quota and 5xx server errors (always); invalid JWT signature * but only when the key was created this run; 403/404 but only when the * writer permission was created or upgraded this run. A generic * `invalid_grant`/expired-credentials error is not treated as propagation. * All other failures are permanent. */ export declare function isRetryableVerifyError(error: unknown, freshness: VerifyFreshness): boolean; /** * Validates the raw `spreadsheets.get` success payload. * * A successful response must carry a non-empty URL-safe `spreadsheetId` * equal to the requested id; a missing, empty, malformed, or mismatched id * is a malformed payload and fails the verification immediately. */ export declare function requireSpreadsheetId(data: unknown, expectedSpreadsheetId: string): void; //# sourceMappingURL=saVerify.d.ts.map