/** * An RFC 9457 problem body, as returned by the authorization boundary * (Project #263). Only these known members are carried; anything else the * server sends is dropped rather than forwarded, so a denial can never become a * channel for internal detail. * * A concealed 404 (hidden domain, missing resource, another tenant's id) carries * ONLY type/title/status/detail - no domain, capability, or profile facts - and * that shape must survive the client intact, never be "enriched" locally. */ export interface ProblemDetails { type?: string; title?: string; status: number; detail?: string; code?: string; resource_family?: string; required_capability?: string; effective_result?: string; retryable?: boolean; next_step?: string; instance?: string; } export interface ApiError { status: number; detail: string; retry_after_seconds?: number; detailStructured?: Record; problem?: ProblemDetails; } /** * Feature #814: remaining quota from the most recent response that reported any. * * The CLI's metadata budget was previously invisible until it ran out, and the * only signal was a 429 that arrived after the budget was already gone. The * server now publishes RateLimit-* on success; this records the latest reading * so a command can warn before the next call fails. * * Deliberately last-response-wins and never cleared by a response that omits the * headers: buckets are per-route, so an unrelated call in between must not erase * a reading, and a fail-open check that published nothing must not read as "full * budget". Absent stays absent. */ export interface QuotaReading { limit: number; remaining: number; } export declare function getLastQuota(): QuotaReading | undefined; declare class ApiClient { request(method: string, path: string, body?: unknown, opts?: { timeoutMs?: number; }): Promise; postUnauthed(path: string, body: unknown): Promise; get(path: string): Promise; post(path: string, body: unknown, opts?: { timeoutMs?: number; }): Promise; put(path: string, body: unknown): Promise; patch(path: string, body: unknown): Promise; delete(path: string): Promise; /** * POST a multipart/form-data body (e.g. a file upload). Replicates the auth + * telemetry headers from _fetch but must NOT set Content-Type - fetch derives * the multipart boundary from the FormData body itself (Feature #645). */ postMultipart(path: string, form: FormData, opts?: { timeoutMs?: number; }): Promise; downloadBinary(method: string, path: string, body?: unknown, opts?: { timeoutMs?: number; }): Promise<{ buffer: Buffer; contentType: string; filename: string | null; }>; private _fetch; /** * Build an ApiError from a non-ok Response: read the body once, humanize the * detail (string passthrough or 422-validation humanization), and attach the * 429 Retry-After hint. Shared by every fetch path so all surface clean errors. */ private _errorFromResponse; } /** * Extract an RFC 9457 problem from an error body, or undefined for a legacy * FastAPI `{"detail": ...}` body (Project #263, ARCH-11 - both shapes are on the * wire, so the client parses both). * * The discriminator is the presence of a problem-only member (`type` or a * body-level `status`), which a FastAPI HTTPException body never carries. Only * whitelisted members are copied - an unknown key stays out of the CLI's error * surface entirely rather than being forwarded to a caller as if it were part of * the contract. `status` falls back to the HTTP status when the body omits it. */ export declare function parseProblem(httpStatus: number, body: unknown): ProblemDetails | undefined; /** * Render a backend error body into a single clean, human-readable string. * * The app's normal error convention is `{"detail": ""}` (from * HTTPException), which passes straight through. FastAPI request-validation * (HTTP 422) instead returns `{"detail": [ {loc, msg, type, ctx}, ... ]}` - a * machine-readable array. Without this, the CLI would print that raw structure * verbatim (e.g. `{"error":[{"type":"less_than_equal",...}]}`). We key off the * array SHAPE (not the status, so the string passthrough is never touched) and * humanize each item from its bound context, e.g. "limit must be at most 500". */ export declare function formatApiErrorDetail(status: number, body: unknown): string; export declare const apiClient: ApiClient; export {};