/** * Brave direct HTTP transport skeleton (brave-tech-plan §5, §6). * * Performs direct GETs against the Brave Search REST API with an * `X-Subscription-Token: ` header. There is NO internal retry — * shared execution owns retry policy. Fetch and timers are injectable * for tests. * * Mirrors `providers/tavily/client.ts` in structure, but SIMPLER in * T1: the foundation only ships the GET transport and a thin * HTTP-status → Scoutline error map. Capability-specific request body * mapping arrives in later tickets (search, quota, etc.) once those * Capability Modules land. * * The fetch dep's response type is the header-bearing * {@link ProviderImageFetchResponse} (not the narrower * {@link ProviderQuotaFetchResponse}) so the future Brave quota * Capability can read `X-RateLimit-*` headers off responses without a * second transport seam. T1 never reads those headers, but reusing the * existing type keeps the seam stable. * * Boundary rules (ARCHITECTURE.md §2): * - May import Adapter-local config and normalized errors. * - May import {@link ProviderImageFetchResponse} from `providers/types.ts`. * - Must NOT import command presentation, capability contracts, or * another Provider's Adapter. * - Must NOT perform response field normalization — the Adapter owns * that. This module declares Provider-native request paths and * params only; it never imports a capability contract. */ import type { ProviderImageFetchResponse } from "../types.js"; /** Injectable fetch signature for Brave transport (duck-typed port). */ export type BraveFetch = (input: string, init: Record) => Promise; /** Injectable transport dependencies (fetch, timers, env). */ export interface BraveTransportDeps { readonly fetch?: BraveFetch; readonly setTimeout?: typeof setTimeout; readonly clearTimeout?: typeof clearTimeout; readonly env?: NodeJS.ProcessEnv; } /** * Provider-native search request query params (Brave API field names). * The Adapter maps the Provider-neutral `SearchControls` into these * before calling {@link fetchBraveSearch}; the transport never imports * a capability contract. * * NOTE: `count` is intentionally ABSENT here. Result-count projection * is a client-side concern owned by shared execution (`applyCount`) * AFTER `invoke`+cache; `SearchRequest`/`SearchControls` carry NO * `count` field, so the transport sends only `q` plus the mapped * controls below (`country`/`freshness`). Brave therefore returns its * default page size; `--count` above that is silently capped (no * pagination in T2). */ export interface BraveSearchParams { readonly country?: string; readonly freshness?: string; } export declare function resolveTimeoutMs(env: NodeJS.ProcessEnv): number; /** * Optional callback fired after Brave returns a 2xx response, BEFORE * the body is parsed. Used by the passive harvest (PB-T1): the Brave * search capability reads the four `X-RateLimit-*` headers off the * live response and writes a quota snapshot. The callback receives the * raw header accessor (same shape as `fetchBraveRateLimit`'s header * reader) so the caller can read whatever headers it needs. * * Contract: * - Fires ONLY on 2xx (a non-2xx drains the body and throws before * the callback runs). * - Errors thrown by the callback are CAUGHT AND SWALLOWED by the * transport so a harvest failure can never convert a search * success into a provider fallback. * - The callback is AWAITED so a store write survives the immediate * `process.exit` the bin performs after `main` returns. * - Not wired through the news/video/LLM-context sibling wrappers — * only `fetchBraveSearch` passes it (PB-T1 harvest scope). */ export type BraveResponseHeaderCallback = (headers: { get(name: string): string | null; }) => Promise | void; /** * Perform ONE GET against the Brave Search API. No retry; no response * body in public errors. Returns the parsed JSON body (raw; the * Adapter post-processes into a normalized shape). * * The transport carries `X-Subscription-Token` (NOT * `Authorization: Bearer`) and a `User-Agent: scoutline/` * string read from `package.json` at module load. * * `onResponseHeaders` (PB-T1) is an optional callback fired after a * 2xx response, before body parsing. It shares the header reader with * `fetchBraveRateLimit`. Only `fetchBraveSearch` currently passes it * (the passive-harvest path); the news/video/LLM-context sibling * wrappers do not. */ export declare function getBraveJson(apiKey: string, path: string, params?: Readonly>, deps?: BraveTransportDeps, onResponseHeaders?: BraveResponseHeaderCallback): Promise; /** * Perform ONE GET against the Brave `/res/v1/web/search` endpoint. No * retry; no response body in public errors. Returns the parsed JSON * body (raw; the Adapter post-processes into normalized search * sources). * * `params` carries Brave-native API fields already mapped from * `SearchControls` by the Adapter (`country`/`freshness`). The query * is sent as the `q` query-string parameter. * * `onRateLimitHeaders` (PB-T1) is an optional callback that receives * the four `X-RateLimit-*` response headers as a * {@link BraveRateLimitHeaders} value (same shape as * `fetchBraveRateLimit`'s return). It fires only on a 2xx response; * errors are caught by the transport so a harvest write failure never * converts a search success into a fallback. The callback is awaited * so the store write survives the bin's immediate `process.exit`. * * Only `fetchBraveSearch` carries the harvest callback. * `fetchBraveNewsSearch`, `fetchBraveVideoSearch`, and * `fetchBraveLlmContext` share `getBraveJson` but do NOT pass the * callback, so news/video/LLM-context responses are NOT harvested * (documented scope — the Brave rate-limit headers are present on * every search-family endpoint, but PB-T1 harvests only the web path; * expanding to siblings is a future-ticket decision). */ export declare function fetchBraveSearch(apiKey: string, query: string, params?: BraveSearchParams, deps?: BraveTransportDeps, onRateLimitHeaders?: (headers: BraveRateLimitHeaders) => Promise | void): Promise; /** * Perform ONE GET against the Brave `/res/v1/news/search` endpoint. No * retry; no response body in public errors. Returns the parsed JSON * body (raw; the Adapter post-processes into normalized search * sources). * * Dispatched when `controls.topic === "news"`; the web endpoint is the * default. `params` carries the same Brave-native fields as * {@link fetchBraveSearch}. */ export declare function fetchBraveNewsSearch(apiKey: string, query: string, params?: BraveSearchParams, deps?: BraveTransportDeps): Promise; /** * Perform ONE GET against the Brave `/res/v1/videos/search` endpoint. No * retry; no response body in public errors. Returns the parsed JSON * body (raw; the Adapter post-processes into normalized search * sources). * * Dispatched when `controls.type === "video"`; precedence is * `video > news > web`. `params` carries the same Brave-native fields * as {@link fetchBraveSearch}. * * NOTE: Brave documents the videos endpoint as a POST body, but GET * with query params covers every field we send (`q`/`country`/ * `freshness`) — confirm live, fall back to POST only if GET is * rejected. `count` is intentionally absent (see * {@link BraveSearchParams}); video results are client-side-truncated * via `applyCount` like every provider, and the video endpoint's * default page-size cap is unconfirmed (live gate). */ export declare function fetchBraveVideoSearch(apiKey: string, query: string, params?: BraveSearchParams, deps?: BraveTransportDeps): Promise; /** * Perform ONE GET against the Brave LLM Context endpoint * (`/res/v1/llm/context`). No retry; no response body in public errors. * Returns the parsed JSON body (raw; the Adapter post-processes into * normalized search sources). * * Unlike the web/news/video endpoints, LLM Context is **query-keyed** — * it is a richer search that returns extracted passages * (`grounding.generic[]`), NOT a URL-keyed summarizer. It is dispatched * when `controls.contentSize === "high"` (the accepted 4th-meaning * overload of `--content-size`); precedence is `video > high > news > * web`. `high` overrides `topic`, so this path must receive a CLEAN * query (no `topic` keyword appendage); the Adapter is responsible for * that suppression. * * The mapped `country`/`freshness` controls are forwarded alongside * `q` (consistent with the web/news/video paths) so a `high` search * honors `--recency`/`--location` rather than silently dropping them. * Whether LLM Context honors `country`/`freshness` is a live gate; if * it rejects them the failure surfaces as a mapped error rather than a * silent filter drop. `count` is intentionally never forwarded * (`--count` is applied client-side by shared execution); LLM * Context's source-count/token-budget param name is UNCONFIRMED. */ export declare function fetchBraveLlmContext(apiKey: string, query: string, params?: BraveSearchParams, deps?: BraveTransportDeps): Promise; /** * The four Brave `X-RateLimit-*` response headers, read as raw strings. * Each is `null` when the header is absent. The quota normalizer parses * these into windows; this transport layer only collects them. */ export interface BraveRateLimitHeaders { readonly limit: string | null; readonly policy: string | null; readonly remaining: string | null; readonly reset: string | null; } /** * Perform ONE GET against `/res/v1/web/search` with a stub query and * return the four `X-RateLimit-*` response headers. Brave has NO * `/usage` endpoint, so quota is read from these headers on a 1-query * probe. The probe costs exactly ONE request and sends ONLY `q` — no * `count` (shared execution applies count client-side) and no other * controls. * * Mirrors {@link getBraveJson}'s transport setup (X-Subscription-Token/ * Accept/User-Agent headers, AbortController timeout via `BRAVE_TIMEOUT`, * the same HTTP-status → error {@link mapStatusError}, and * {@link normalizeTransportError}). On 2xx the body is DRAINED * (`res.text()`) and discarded — only the headers are needed. On non-2xx * the body is drained and dropped before throwing the mapped error; no * raw Brave body ever reaches an error message. */ export declare function fetchBraveRateLimit(apiKey: string, deps?: BraveTransportDeps): Promise; //# sourceMappingURL=client.d.ts.map