import { type DeviceCredentialPathOptions, type DeviceCredentials } from './device.js'; export declare const DEVICE_ENROLL_ENDPOINTS: Readonly<{ begin: "/api/devices/enroll"; poll: "/api/devices/enroll/poll"; refresh: "/api/devices/refresh"; }>; export interface DeviceEnrollEndpoints { begin: string; poll: string; refresh: string; } /** * Wire contract consumed by the open client. The closed-source server owns * implementation details but must return these fields before the CLI is * enabled end-to-end. */ export interface DeviceEnrollmentGrant { grantId: string; pollSecret: string; /** Absolute grant expiry as Unix epoch ms. */ expiresAt: number; /** Server-requested minimum polling delay. Optional; Retry-After also works. */ pollIntervalMs?: number; } export interface DeviceTokenPair { accessToken: string; /** Absolute access-token expiry as Unix epoch ms; used for proactive lease renewal. */ accessExpiresAt: number; refreshToken: string; /** Absolute device expiry as Unix epoch ms. */ deviceExp: number; } export type DeviceEnrollmentPoll = { kind: 'pending'; retryAfterMs: number; } | { kind: 'issued'; credentials: DeviceTokenPair; } | { kind: 'denied'; } | { kind: 'expired'; }; export interface DeviceEnrollmentApi { readonly issuer: string; beginEnrollment(input: BeginDeviceEnrollmentInput): Promise; waitForEnrollment(grant: DeviceEnrollmentGrant, options?: WaitForDeviceEnrollmentOptions): Promise; } export interface BeginDeviceEnrollmentInput { machineToken: string; deviceName: string; deviceKind?: 'desktop-ide'; signal?: AbortSignal; } export interface WaitForDeviceEnrollmentOptions { /** Local upper bound; server grant expiry can shorten it. Defaults to 5min. */ timeoutMs?: number; signal?: AbortSignal; onPending?: () => void; } export interface RefreshDeviceOptions { /** Durable idempotency key; required by the safe stored-refresh path. */ requestId: string; signal?: AbortSignal; } export interface StoredDeviceRefreshOptions extends DeviceCredentialPathOptions { signal?: AbortSignal; now?: () => Date; requestIdFactory?: () => string; sleep?: (ms: number, signal?: AbortSignal) => Promise; createClient?: (issuer: string) => Pick; } export interface StoredDeviceMutationOptions extends DeviceCredentialPathOptions { now?: () => Date; } export interface DeviceHttpRequest { url: string; method: 'POST'; headers: Readonly>; body: unknown; timeoutMs: number; signal?: AbortSignal; } export interface DeviceHttpResponse { status: number; body: unknown; /** Header names must be lowercase. */ headers?: Readonly>; } export type DeviceHttpTransport = (request: DeviceHttpRequest) => Promise; export interface DeviceEnrollmentClientOptions { transport?: DeviceHttpTransport; endpoints?: Partial; timeoutMs?: number; now?: () => number; sleep?: (ms: number, signal?: AbortSignal) => Promise; } export declare class DeviceProtocolError extends Error { readonly code: 'network_error' | 'invalid_response' | 'request_rejected' | 'enrollment_timeout' | 'enrollment_denied' | 'enrollment_expired' | 'aborted'; readonly status?: number | undefined; readonly serverCode?: DeviceProtocolServerCode | undefined; /** Parsed and bounded Retry-After delay for a transient HTTP response. */ readonly retryAfterMs?: number | undefined; constructor(message: string, code: 'network_error' | 'invalid_response' | 'request_rejected' | 'enrollment_timeout' | 'enrollment_denied' | 'enrollment_expired' | 'aborted', status?: number | undefined, serverCode?: DeviceProtocolServerCode | undefined, /** Parsed and bounded Retry-After delay for a transient HTTP response. */ retryAfterMs?: number | undefined); } export type DeviceProtocolServerCode = 'bad_request' | 'invalid_refresh' | 'device_gone' | 'device_revoked' | 'device_expired' | 'recovery_required' | 'refresh_in_progress' | 'store_unavailable'; /** * Small JSON transport with a bounded response and no redirect following. * Redirects are surfaced as a rejection so a bearer can never cross origins. */ export declare const nodeDeviceHttpTransport: DeviceHttpTransport; export declare class DeviceEnrollmentClient { readonly issuer: string; private readonly transport; private readonly endpoints; private readonly timeoutMs; private readonly now; private readonly sleep; constructor(issuer: string, options?: DeviceEnrollmentClientOptions); private normalizeEndpoint; private post; beginEnrollment(input: BeginDeviceEnrollmentInput): Promise; pollEnrollment(grant: Pick, signal?: AbortSignal): Promise; waitForEnrollment(grant: DeviceEnrollmentGrant, options?: WaitForDeviceEnrollmentOptions): Promise; /** * Send one refresh attempt. The stored-credential coordinator performs * bounded same-key retries for ambiguous failures inside the replay window; * keeping retry policy outside this wire primitive also keeps direct callers * from accidentally retrying with a new key. */ refresh(refreshToken: string, options: RefreshDeviceOptions): Promise; } export { DEVICE_ENROLLMENT_JOURNAL_FILE } from './device-paths.js'; export interface StoredDeviceEnrollmentOptions extends StoredDeviceMutationOptions { client: DeviceEnrollmentApi; machineToken: string; deviceName: string; signal?: AbortSignal; onPending?: () => void; onGrantReady?: () => void; } export declare function deviceEnrollmentJournalPath(options?: DeviceCredentialPathOptions): string; /** * Crash-safe F1 enrollment coordinator. * * The grant/poll secret is durably journaled before polling. A later host * process resumes the same grant, and an issued response is installed together * with journal cleanup under the same device lock. */ export declare function enrollStoredDeviceCredentials(options: StoredDeviceEnrollmentOptions): Promise; /** Serialize logout with refresh/install so a completed refresh cannot revive it. */ export declare function clearStoredDeviceCredentials(options?: DeviceCredentialPathOptions): Promise; export declare function refreshStoredDeviceCredentials(options?: StoredDeviceRefreshOptions, foreignJournalDepth?: number): Promise; //# sourceMappingURL=device-enroll.d.ts.map