scoutline 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +11 -3
  2. package/dist/capabilities/quota.d.ts +11 -0
  3. package/dist/capabilities/quota.d.ts.map +1 -1
  4. package/dist/capabilities/quota.js.map +1 -1
  5. package/dist/capabilities/search.d.ts +11 -1
  6. package/dist/capabilities/search.d.ts.map +1 -1
  7. package/dist/commands/crawl.js +1 -1
  8. package/dist/commands/doctor.d.ts.map +1 -1
  9. package/dist/commands/doctor.js +13 -10
  10. package/dist/commands/doctor.js.map +1 -1
  11. package/dist/commands/map.js +1 -1
  12. package/dist/commands/quota.d.ts +23 -0
  13. package/dist/commands/quota.d.ts.map +1 -1
  14. package/dist/commands/quota.js +21 -4
  15. package/dist/commands/quota.js.map +1 -1
  16. package/dist/commands/read.js +1 -1
  17. package/dist/commands/repo.js +1 -1
  18. package/dist/commands/research.js +1 -1
  19. package/dist/commands/search.d.ts +2 -1
  20. package/dist/commands/search.d.ts.map +1 -1
  21. package/dist/commands/search.js +21 -12
  22. package/dist/commands/search.js.map +1 -1
  23. package/dist/index.d.ts +7 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +35 -4
  26. package/dist/index.js.map +1 -1
  27. package/dist/lib/redact.d.ts +2 -2
  28. package/dist/lib/redact.d.ts.map +1 -1
  29. package/dist/lib/redact.js +8 -2
  30. package/dist/lib/redact.js.map +1 -1
  31. package/dist/providers/brave/adapter.d.ts +42 -0
  32. package/dist/providers/brave/adapter.d.ts.map +1 -0
  33. package/dist/providers/brave/adapter.js +504 -0
  34. package/dist/providers/brave/adapter.js.map +1 -0
  35. package/dist/providers/brave/client.d.ts +161 -0
  36. package/dist/providers/brave/client.d.ts.map +1 -0
  37. package/dist/providers/brave/client.js +297 -0
  38. package/dist/providers/brave/client.js.map +1 -0
  39. package/dist/providers/brave/credentials.d.ts +33 -0
  40. package/dist/providers/brave/credentials.d.ts.map +1 -0
  41. package/dist/providers/brave/credentials.js +55 -0
  42. package/dist/providers/brave/credentials.js.map +1 -0
  43. package/dist/providers/brave/diagnostics.d.ts +46 -0
  44. package/dist/providers/brave/diagnostics.d.ts.map +1 -0
  45. package/dist/providers/brave/diagnostics.js +76 -0
  46. package/dist/providers/brave/diagnostics.js.map +1 -0
  47. package/dist/providers/brave/quota.d.ts +86 -0
  48. package/dist/providers/brave/quota.d.ts.map +1 -0
  49. package/dist/providers/brave/quota.js +247 -0
  50. package/dist/providers/brave/quota.js.map +1 -0
  51. package/dist/providers/exa/adapter.d.ts +58 -0
  52. package/dist/providers/exa/adapter.d.ts.map +1 -0
  53. package/dist/providers/exa/adapter.js +892 -0
  54. package/dist/providers/exa/adapter.js.map +1 -0
  55. package/dist/providers/exa/client.d.ts +143 -0
  56. package/dist/providers/exa/client.d.ts.map +1 -0
  57. package/dist/providers/exa/client.js +328 -0
  58. package/dist/providers/exa/client.js.map +1 -0
  59. package/dist/providers/exa/credentials.d.ts +34 -0
  60. package/dist/providers/exa/credentials.d.ts.map +1 -0
  61. package/dist/providers/exa/credentials.js +56 -0
  62. package/dist/providers/exa/credentials.js.map +1 -0
  63. package/dist/providers/exa/diagnostics.d.ts +42 -0
  64. package/dist/providers/exa/diagnostics.d.ts.map +1 -0
  65. package/dist/providers/exa/diagnostics.js +69 -0
  66. package/dist/providers/exa/diagnostics.js.map +1 -0
  67. package/dist/providers/minimax/adapter.js +1 -1
  68. package/dist/providers/minimax/adapter.js.map +1 -1
  69. package/dist/providers/registry.d.ts.map +1 -1
  70. package/dist/providers/registry.js +4 -0
  71. package/dist/providers/registry.js.map +1 -1
  72. package/dist/providers/tavily/adapter.d.ts.map +1 -1
  73. package/dist/providers/tavily/adapter.js +5 -0
  74. package/dist/providers/tavily/adapter.js.map +1 -1
  75. package/dist/providers/types.d.ts +1 -1
  76. package/dist/providers/types.d.ts.map +1 -1
  77. package/dist/providers/types.js +1 -1
  78. package/dist/providers/types.js.map +1 -1
  79. package/dist/providers/zai/adapter.d.ts.map +1 -1
  80. package/dist/providers/zai/adapter.js +7 -1
  81. package/dist/providers/zai/adapter.js.map +1 -1
  82. package/package.json +1 -1
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Brave direct HTTP transport skeleton (brave-tech-plan §5, §6).
3
+ *
4
+ * Performs direct GETs against the Brave Search REST API with an
5
+ * `X-Subscription-Token: <apiKey>` header. There is NO internal retry —
6
+ * shared execution owns retry policy. Fetch and timers are injectable
7
+ * for tests.
8
+ *
9
+ * Mirrors `providers/tavily/client.ts` in structure, but SIMPLER in
10
+ * T1: the foundation only ships the GET transport and a thin
11
+ * HTTP-status → Scoutline error map. Capability-specific request body
12
+ * mapping arrives in later tickets (search, quota, etc.) once those
13
+ * Capability Modules land.
14
+ *
15
+ * The fetch dep's response type is the header-bearing
16
+ * {@link ProviderImageFetchResponse} (not the narrower
17
+ * {@link ProviderQuotaFetchResponse}) so the future Brave quota
18
+ * Capability can read `X-RateLimit-*` headers off responses without a
19
+ * second transport seam. T1 never reads those headers, but reusing the
20
+ * existing type keeps the seam stable.
21
+ *
22
+ * Boundary rules (ARCHITECTURE.md §2):
23
+ * - May import Adapter-local config and normalized errors.
24
+ * - May import {@link ProviderImageFetchResponse} from `providers/types.ts`.
25
+ * - Must NOT import command presentation, capability contracts, or
26
+ * another Provider's Adapter.
27
+ * - Must NOT perform response field normalization — the Adapter owns
28
+ * that. This module declares Provider-native request paths and
29
+ * params only; it never imports a capability contract.
30
+ */
31
+ import type { ProviderImageFetchResponse } from "../types.js";
32
+ /** Injectable transport dependencies (fetch, timers, env). */
33
+ export interface BraveTransportDeps {
34
+ readonly fetch?: (input: string, init: Record<string, unknown>) => Promise<ProviderImageFetchResponse>;
35
+ readonly setTimeout?: typeof setTimeout;
36
+ readonly clearTimeout?: typeof clearTimeout;
37
+ readonly env?: NodeJS.ProcessEnv;
38
+ }
39
+ /**
40
+ * Provider-native search request query params (Brave API field names).
41
+ * The Adapter maps the Provider-neutral `SearchControls` into these
42
+ * before calling {@link fetchBraveSearch}; the transport never imports
43
+ * a capability contract.
44
+ *
45
+ * NOTE: `count` is intentionally ABSENT here. Result-count projection
46
+ * is a client-side concern owned by shared execution (`applyCount`)
47
+ * AFTER `invoke`+cache; `SearchRequest`/`SearchControls` carry NO
48
+ * `count` field, so the transport sends only `q` plus the mapped
49
+ * controls below (`country`/`freshness`). Brave therefore returns its
50
+ * default page size; `--count` above that is silently capped (no
51
+ * pagination in T2).
52
+ */
53
+ export interface BraveSearchParams {
54
+ readonly country?: string;
55
+ readonly freshness?: string;
56
+ }
57
+ /**
58
+ * Perform ONE GET against the Brave Search API. No retry; no response
59
+ * body in public errors. Returns the parsed JSON body (raw; the
60
+ * Adapter post-processes into a normalized shape).
61
+ *
62
+ * The transport carries `X-Subscription-Token` (NOT
63
+ * `Authorization: Bearer`) and a `User-Agent: scoutline/<version>`
64
+ * string read from `package.json` at module load.
65
+ */
66
+ export declare function getBraveJson(apiKey: string, path: string, params?: Readonly<Record<string, unknown>>, deps?: BraveTransportDeps): Promise<unknown>;
67
+ /**
68
+ * Perform ONE GET against the Brave `/res/v1/web/search` endpoint. No
69
+ * retry; no response body in public errors. Returns the parsed JSON
70
+ * body (raw; the Adapter post-processes into normalized search
71
+ * sources).
72
+ *
73
+ * `params` carries Brave-native API fields already mapped from
74
+ * `SearchControls` by the Adapter (`country`/`freshness`). The query
75
+ * is sent as the `q` query-string parameter.
76
+ */
77
+ export declare function fetchBraveSearch(apiKey: string, query: string, params?: BraveSearchParams, deps?: BraveTransportDeps): Promise<unknown>;
78
+ /**
79
+ * Perform ONE GET against the Brave `/res/v1/news/search` endpoint. No
80
+ * retry; no response body in public errors. Returns the parsed JSON
81
+ * body (raw; the Adapter post-processes into normalized search
82
+ * sources).
83
+ *
84
+ * Dispatched when `controls.topic === "news"`; the web endpoint is the
85
+ * default. `params` carries the same Brave-native fields as
86
+ * {@link fetchBraveSearch}.
87
+ */
88
+ export declare function fetchBraveNewsSearch(apiKey: string, query: string, params?: BraveSearchParams, deps?: BraveTransportDeps): Promise<unknown>;
89
+ /**
90
+ * Perform ONE GET against the Brave `/res/v1/videos/search` endpoint. No
91
+ * retry; no response body in public errors. Returns the parsed JSON
92
+ * body (raw; the Adapter post-processes into normalized search
93
+ * sources).
94
+ *
95
+ * Dispatched when `controls.type === "video"`; precedence is
96
+ * `video > news > web`. `params` carries the same Brave-native fields
97
+ * as {@link fetchBraveSearch}.
98
+ *
99
+ * NOTE: Brave documents the videos endpoint as a POST body, but GET
100
+ * with query params covers every field we send (`q`/`country`/
101
+ * `freshness`) — confirm live, fall back to POST only if GET is
102
+ * rejected. `count` is intentionally absent (see
103
+ * {@link BraveSearchParams}); video results are client-side-truncated
104
+ * via `applyCount` like every provider, and the video endpoint's
105
+ * default page-size cap is unconfirmed (live gate).
106
+ */
107
+ export declare function fetchBraveVideoSearch(apiKey: string, query: string, params?: BraveSearchParams, deps?: BraveTransportDeps): Promise<unknown>;
108
+ /**
109
+ * Perform ONE GET against the Brave LLM Context endpoint
110
+ * (`/res/v1/llm/context`). No retry; no response body in public errors.
111
+ * Returns the parsed JSON body (raw; the Adapter post-processes into
112
+ * normalized search sources).
113
+ *
114
+ * Unlike the web/news/video endpoints, LLM Context is **query-keyed** —
115
+ * it is a richer search that returns extracted passages
116
+ * (`grounding.generic[]`), NOT a URL-keyed summarizer. It is dispatched
117
+ * when `controls.contentSize === "high"` (the accepted 4th-meaning
118
+ * overload of `--content-size`); precedence is `video > high > news >
119
+ * web`. `high` overrides `topic`, so this path must receive a CLEAN
120
+ * query (no `topic` keyword appendage); the Adapter is responsible for
121
+ * that suppression.
122
+ *
123
+ * The mapped `country`/`freshness` controls are forwarded alongside
124
+ * `q` (consistent with the web/news/video paths) so a `high` search
125
+ * honors `--recency`/`--location` rather than silently dropping them.
126
+ * Whether LLM Context honors `country`/`freshness` is a live gate; if
127
+ * it rejects them the failure surfaces as a mapped error rather than a
128
+ * silent filter drop. `count` is intentionally never forwarded
129
+ * (`--count` is applied client-side by shared execution); LLM
130
+ * Context's source-count/token-budget param name is UNCONFIRMED.
131
+ */
132
+ export declare function fetchBraveLlmContext(apiKey: string, query: string, params?: BraveSearchParams, deps?: BraveTransportDeps): Promise<unknown>;
133
+ /**
134
+ * The four Brave `X-RateLimit-*` response headers, read as raw strings.
135
+ * Each is `null` when the header is absent. The quota normalizer parses
136
+ * these into windows; this transport layer only collects them.
137
+ */
138
+ export interface BraveRateLimitHeaders {
139
+ readonly limit: string | null;
140
+ readonly policy: string | null;
141
+ readonly remaining: string | null;
142
+ readonly reset: string | null;
143
+ }
144
+ /**
145
+ * Perform ONE GET against `/res/v1/web/search` with a stub query and
146
+ * return the four `X-RateLimit-*` response headers. Brave has NO
147
+ * `/usage` endpoint, so quota is read from these headers on a 1-query
148
+ * probe. The probe costs exactly ONE request and sends ONLY `q` — no
149
+ * `count` (shared execution applies count client-side) and no other
150
+ * controls.
151
+ *
152
+ * Mirrors {@link getBraveJson}'s transport setup (X-Subscription-Token/
153
+ * Accept/User-Agent headers, AbortController timeout via `BRAVE_TIMEOUT`,
154
+ * the same HTTP-status → error {@link mapStatusError}, and
155
+ * {@link normalizeTransportError}). On 2xx the body is DRAINED
156
+ * (`res.text()`) and discarded — only the headers are needed. On non-2xx
157
+ * the body is drained and dropped before throwing the mapped error; no
158
+ * raw Brave body ever reaches an error message.
159
+ */
160
+ export declare function fetchBraveRateLimit(apiKey: string, deps?: BraveTransportDeps): Promise<BraveRateLimitHeaders>;
161
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/providers/brave/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAKH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAW9D,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,CACf,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,UAAU,CAAC;IACxC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,YAAY,CAAC;IAC5C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAwED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1C,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,OAAO,CAAC,CA8ClB;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,iBAAiB,EAC1B,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,iBAAiB,EAC1B,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,iBAAiB,EAC1B,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,iBAAiB,EAC1B,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAElB;AAMD;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAKD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CAgDhC"}
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Brave direct HTTP transport skeleton (brave-tech-plan §5, §6).
3
+ *
4
+ * Performs direct GETs against the Brave Search REST API with an
5
+ * `X-Subscription-Token: <apiKey>` header. There is NO internal retry —
6
+ * shared execution owns retry policy. Fetch and timers are injectable
7
+ * for tests.
8
+ *
9
+ * Mirrors `providers/tavily/client.ts` in structure, but SIMPLER in
10
+ * T1: the foundation only ships the GET transport and a thin
11
+ * HTTP-status → Scoutline error map. Capability-specific request body
12
+ * mapping arrives in later tickets (search, quota, etc.) once those
13
+ * Capability Modules land.
14
+ *
15
+ * The fetch dep's response type is the header-bearing
16
+ * {@link ProviderImageFetchResponse} (not the narrower
17
+ * {@link ProviderQuotaFetchResponse}) so the future Brave quota
18
+ * Capability can read `X-RateLimit-*` headers off responses without a
19
+ * second transport seam. T1 never reads those headers, but reusing the
20
+ * existing type keeps the seam stable.
21
+ *
22
+ * Boundary rules (ARCHITECTURE.md §2):
23
+ * - May import Adapter-local config and normalized errors.
24
+ * - May import {@link ProviderImageFetchResponse} from `providers/types.ts`.
25
+ * - Must NOT import command presentation, capability contracts, or
26
+ * another Provider's Adapter.
27
+ * - Must NOT perform response field normalization — the Adapter owns
28
+ * that. This module declares Provider-native request paths and
29
+ * params only; it never imports a capability contract.
30
+ */
31
+ import { createRequire } from "node:module";
32
+ import { ApiError, AuthError, NetworkError, TimeoutError } from "../../lib/errors.js";
33
+ const require = createRequire(import.meta.url);
34
+ const { version: VERSION } = require("../../../package.json");
35
+ const BASE_URL = "https://api.search.brave.com";
36
+ const DEFAULT_TIMEOUT_MS = 30000;
37
+ const USER_AGENT = `scoutline/${VERSION}`;
38
+ const TIMEOUT_HELP_TEXT = "Try again or increase timeout with BRAVE_TIMEOUT env var";
39
+ function resolveTimeoutMs(env) {
40
+ const raw = parseInt(env.BRAVE_TIMEOUT || String(DEFAULT_TIMEOUT_MS), 10);
41
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_TIMEOUT_MS;
42
+ }
43
+ /**
44
+ * Layer 1 — HTTP-status mapping. Runs BEFORE the body is parsed; on a
45
+ * non-200 response we discard the body and throw a typed error.
46
+ *
47
+ * `timeoutMs` is forwarded so 408/504 can throw `TimeoutError`
48
+ * carrying the configured duration (and the `BRAVE_TIMEOUT` help text).
49
+ * The transport never embeds credential material or raw response bodies
50
+ * in any error message.
51
+ */
52
+ function mapStatusError(status, timeoutMs) {
53
+ if (status === 401 || status === 403) {
54
+ return new AuthError("Brave authentication failed", "BRAVE_SEARCH_API_KEY");
55
+ }
56
+ if (status === 408 || status === 504) {
57
+ return new TimeoutError(timeoutMs, TIMEOUT_HELP_TEXT);
58
+ }
59
+ if (status === 429) {
60
+ return new ApiError("Brave rate limit exceeded", 429);
61
+ }
62
+ if (status === 400 || status === 404 || status === 410 || status === 422) {
63
+ return new ApiError("Brave request failed", status);
64
+ }
65
+ if (status >= 500) {
66
+ return new ApiError("Brave request failed", status);
67
+ }
68
+ return new ApiError("Brave request failed", status);
69
+ }
70
+ function normalizeTransportError(err, timeoutMs) {
71
+ if (err instanceof AuthError || err instanceof ApiError || err instanceof TimeoutError) {
72
+ return err;
73
+ }
74
+ if (err instanceof Error) {
75
+ if (err.name === "AbortError") {
76
+ return new TimeoutError(timeoutMs, TIMEOUT_HELP_TEXT);
77
+ }
78
+ const lower = err.message.toLowerCase();
79
+ if (lower.includes("fetch") ||
80
+ lower.includes("econnrefused") ||
81
+ lower.includes("econnreset") ||
82
+ lower.includes("enotfound") ||
83
+ lower.includes("network")) {
84
+ return new NetworkError("Brave network error");
85
+ }
86
+ }
87
+ return new ApiError("Brave request failed", 500);
88
+ }
89
+ /**
90
+ * Build the query string for a GET. Skips `undefined`/`null` values.
91
+ * Uses `encodeURIComponent` so callers can pass raw strings without
92
+ * pre-encoding.
93
+ */
94
+ function buildQueryString(params) {
95
+ if (!params)
96
+ return "";
97
+ const parts = [];
98
+ for (const [key, value] of Object.entries(params)) {
99
+ if (value === undefined || value === null)
100
+ continue;
101
+ parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
102
+ }
103
+ return parts.length === 0 ? "" : `?${parts.join("&")}`;
104
+ }
105
+ /**
106
+ * Perform ONE GET against the Brave Search API. No retry; no response
107
+ * body in public errors. Returns the parsed JSON body (raw; the
108
+ * Adapter post-processes into a normalized shape).
109
+ *
110
+ * The transport carries `X-Subscription-Token` (NOT
111
+ * `Authorization: Bearer`) and a `User-Agent: scoutline/<version>`
112
+ * string read from `package.json` at module load.
113
+ */
114
+ export async function getBraveJson(apiKey, path, params, deps = {}) {
115
+ const f = deps.fetch ??
116
+ fetch;
117
+ const setT = deps.setTimeout ?? setTimeout;
118
+ const clearT = deps.clearTimeout ?? clearTimeout;
119
+ const env = deps.env ?? process.env;
120
+ const timeoutMs = resolveTimeoutMs(env);
121
+ const url = `${BASE_URL}${path}${buildQueryString(params)}`;
122
+ const controller = new AbortController();
123
+ const timeoutId = setT(() => controller.abort(), timeoutMs);
124
+ try {
125
+ const res = await f(url, {
126
+ method: "GET",
127
+ headers: {
128
+ "X-Subscription-Token": apiKey,
129
+ Accept: "application/json",
130
+ "User-Agent": USER_AGENT,
131
+ },
132
+ signal: controller.signal,
133
+ });
134
+ if (!res.ok) {
135
+ // Drain the body to free the socket, then drop it. The body must
136
+ // NEVER reach the error message (NFR-006).
137
+ await res.text().catch(() => { });
138
+ throw mapStatusError(res.status, timeoutMs);
139
+ }
140
+ let parsed;
141
+ try {
142
+ parsed = await res.json();
143
+ }
144
+ catch {
145
+ throw new ApiError("Brave returned a malformed response", 500);
146
+ }
147
+ return parsed;
148
+ }
149
+ catch (err) {
150
+ throw normalizeTransportError(err, timeoutMs);
151
+ }
152
+ finally {
153
+ // Clear the timeout only after body consumption so the AbortController
154
+ // timeout still covers a stalled/slow response body read.
155
+ clearT(timeoutId);
156
+ controller.abort();
157
+ }
158
+ }
159
+ /**
160
+ * Perform ONE GET against the Brave `/res/v1/web/search` endpoint. No
161
+ * retry; no response body in public errors. Returns the parsed JSON
162
+ * body (raw; the Adapter post-processes into normalized search
163
+ * sources).
164
+ *
165
+ * `params` carries Brave-native API fields already mapped from
166
+ * `SearchControls` by the Adapter (`country`/`freshness`). The query
167
+ * is sent as the `q` query-string parameter.
168
+ */
169
+ export async function fetchBraveSearch(apiKey, query, params, deps = {}) {
170
+ return getBraveJson(apiKey, "/res/v1/web/search", { q: query, ...(params ?? {}) }, deps);
171
+ }
172
+ /**
173
+ * Perform ONE GET against the Brave `/res/v1/news/search` endpoint. No
174
+ * retry; no response body in public errors. Returns the parsed JSON
175
+ * body (raw; the Adapter post-processes into normalized search
176
+ * sources).
177
+ *
178
+ * Dispatched when `controls.topic === "news"`; the web endpoint is the
179
+ * default. `params` carries the same Brave-native fields as
180
+ * {@link fetchBraveSearch}.
181
+ */
182
+ export async function fetchBraveNewsSearch(apiKey, query, params, deps = {}) {
183
+ return getBraveJson(apiKey, "/res/v1/news/search", { q: query, ...(params ?? {}) }, deps);
184
+ }
185
+ /**
186
+ * Perform ONE GET against the Brave `/res/v1/videos/search` endpoint. No
187
+ * retry; no response body in public errors. Returns the parsed JSON
188
+ * body (raw; the Adapter post-processes into normalized search
189
+ * sources).
190
+ *
191
+ * Dispatched when `controls.type === "video"`; precedence is
192
+ * `video > news > web`. `params` carries the same Brave-native fields
193
+ * as {@link fetchBraveSearch}.
194
+ *
195
+ * NOTE: Brave documents the videos endpoint as a POST body, but GET
196
+ * with query params covers every field we send (`q`/`country`/
197
+ * `freshness`) — confirm live, fall back to POST only if GET is
198
+ * rejected. `count` is intentionally absent (see
199
+ * {@link BraveSearchParams}); video results are client-side-truncated
200
+ * via `applyCount` like every provider, and the video endpoint's
201
+ * default page-size cap is unconfirmed (live gate).
202
+ */
203
+ export async function fetchBraveVideoSearch(apiKey, query, params, deps = {}) {
204
+ return getBraveJson(apiKey, "/res/v1/videos/search", { q: query, ...(params ?? {}) }, deps);
205
+ }
206
+ /**
207
+ * Perform ONE GET against the Brave LLM Context endpoint
208
+ * (`/res/v1/llm/context`). No retry; no response body in public errors.
209
+ * Returns the parsed JSON body (raw; the Adapter post-processes into
210
+ * normalized search sources).
211
+ *
212
+ * Unlike the web/news/video endpoints, LLM Context is **query-keyed** —
213
+ * it is a richer search that returns extracted passages
214
+ * (`grounding.generic[]`), NOT a URL-keyed summarizer. It is dispatched
215
+ * when `controls.contentSize === "high"` (the accepted 4th-meaning
216
+ * overload of `--content-size`); precedence is `video > high > news >
217
+ * web`. `high` overrides `topic`, so this path must receive a CLEAN
218
+ * query (no `topic` keyword appendage); the Adapter is responsible for
219
+ * that suppression.
220
+ *
221
+ * The mapped `country`/`freshness` controls are forwarded alongside
222
+ * `q` (consistent with the web/news/video paths) so a `high` search
223
+ * honors `--recency`/`--location` rather than silently dropping them.
224
+ * Whether LLM Context honors `country`/`freshness` is a live gate; if
225
+ * it rejects them the failure surfaces as a mapped error rather than a
226
+ * silent filter drop. `count` is intentionally never forwarded
227
+ * (`--count` is applied client-side by shared execution); LLM
228
+ * Context's source-count/token-budget param name is UNCONFIRMED.
229
+ */
230
+ export async function fetchBraveLlmContext(apiKey, query, params, deps = {}) {
231
+ return getBraveJson(apiKey, "/res/v1/llm/context", { q: query, ...(params ?? {}) }, deps);
232
+ }
233
+ /** Cheapest-credible probe query (same stub as diagnostics). */
234
+ const RATE_LIMIT_PROBE_QUERY = "scoutline-quota-probe";
235
+ /**
236
+ * Perform ONE GET against `/res/v1/web/search` with a stub query and
237
+ * return the four `X-RateLimit-*` response headers. Brave has NO
238
+ * `/usage` endpoint, so quota is read from these headers on a 1-query
239
+ * probe. The probe costs exactly ONE request and sends ONLY `q` — no
240
+ * `count` (shared execution applies count client-side) and no other
241
+ * controls.
242
+ *
243
+ * Mirrors {@link getBraveJson}'s transport setup (X-Subscription-Token/
244
+ * Accept/User-Agent headers, AbortController timeout via `BRAVE_TIMEOUT`,
245
+ * the same HTTP-status → error {@link mapStatusError}, and
246
+ * {@link normalizeTransportError}). On 2xx the body is DRAINED
247
+ * (`res.text()`) and discarded — only the headers are needed. On non-2xx
248
+ * the body is drained and dropped before throwing the mapped error; no
249
+ * raw Brave body ever reaches an error message.
250
+ */
251
+ export async function fetchBraveRateLimit(apiKey, deps = {}) {
252
+ const f = deps.fetch ??
253
+ fetch;
254
+ const setT = deps.setTimeout ?? setTimeout;
255
+ const clearT = deps.clearTimeout ?? clearTimeout;
256
+ const env = deps.env ?? process.env;
257
+ const timeoutMs = resolveTimeoutMs(env);
258
+ const url = `${BASE_URL}/res/v1/web/search${buildQueryString({ q: RATE_LIMIT_PROBE_QUERY })}`;
259
+ const controller = new AbortController();
260
+ const timeoutId = setT(() => controller.abort(), timeoutMs);
261
+ try {
262
+ const res = await f(url, {
263
+ method: "GET",
264
+ headers: {
265
+ "X-Subscription-Token": apiKey,
266
+ Accept: "application/json",
267
+ "User-Agent": USER_AGENT,
268
+ },
269
+ signal: controller.signal,
270
+ });
271
+ if (!res.ok) {
272
+ // Drain the body to free the socket, then drop it. The body must
273
+ // NEVER reach the error message (NFR-006).
274
+ await res.text().catch(() => { });
275
+ throw mapStatusError(res.status, timeoutMs);
276
+ }
277
+ // Only the headers are needed; drain the body to free the socket.
278
+ await res.text().catch(() => { });
279
+ const headers = res.headers;
280
+ return {
281
+ limit: headers.get("X-RateLimit-Limit"),
282
+ policy: headers.get("X-RateLimit-Policy"),
283
+ remaining: headers.get("X-RateLimit-Remaining"),
284
+ reset: headers.get("X-RateLimit-Reset"),
285
+ };
286
+ }
287
+ catch (err) {
288
+ throw normalizeTransportError(err, timeoutMs);
289
+ }
290
+ finally {
291
+ // Clear the timeout only after body consumption so the AbortController
292
+ // timeout still covers a stalled/slow response body read.
293
+ clearT(timeoutId);
294
+ controller.abort();
295
+ }
296
+ }
297
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/providers/brave/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Brave credential resolution (brave-tech-plan §2, §6).
3
+ *
4
+ * Single source of truth for the Brave Search API key. Whitespace-only
5
+ * values are treated as absent, matching the descriptor's `isConfigured`
6
+ * contract.
7
+ *
8
+ * Boundary rules (ARCHITECTURE.md §2):
9
+ * - May import the normalized-error contract only.
10
+ * - Must NOT import transport, command presentation, or another
11
+ * Provider's Adapter.
12
+ *
13
+ * Missing credentials are surfaced as {@link ConfigurationError}
14
+ * (exit 3), distinct from {@link AuthError} (exit 1) which means the
15
+ * Provider REJECTED a presented credential.
16
+ */
17
+ /**
18
+ * Resolve the Brave API key without throwing. Returns `undefined` when
19
+ * no non-blank value is present.
20
+ */
21
+ export declare function resolveBraveApiKey(env: NodeJS.ProcessEnv): string | undefined;
22
+ /**
23
+ * Resolve the Brave API key or throw {@link ConfigurationError} (exit 3)
24
+ * when it is missing. Call this at every Capability invocation gate.
25
+ */
26
+ export declare function requireBraveApiKey(env: NodeJS.ProcessEnv): string;
27
+ /**
28
+ * True when a non-blank Brave API key is configured. Metadata-only:
29
+ * performs no transport construction and reads no other Provider's
30
+ * credentials.
31
+ */
32
+ export declare function isBraveConfigured(env: NodeJS.ProcessEnv): boolean;
33
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../src/providers/brave/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAgBH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,GAAG,SAAS,CAE7E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CASjE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAEjE"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Brave credential resolution (brave-tech-plan §2, §6).
3
+ *
4
+ * Single source of truth for the Brave Search API key. Whitespace-only
5
+ * values are treated as absent, matching the descriptor's `isConfigured`
6
+ * contract.
7
+ *
8
+ * Boundary rules (ARCHITECTURE.md §2):
9
+ * - May import the normalized-error contract only.
10
+ * - Must NOT import transport, command presentation, or another
11
+ * Provider's Adapter.
12
+ *
13
+ * Missing credentials are surfaced as {@link ConfigurationError}
14
+ * (exit 3), distinct from {@link AuthError} (exit 1) which means the
15
+ * Provider REJECTED a presented credential.
16
+ */
17
+ import { ConfigurationError } from "../../lib/errors.js";
18
+ const MISSING_KEY_HELP = 'export BRAVE_SEARCH_API_KEY="your-api-key"';
19
+ /**
20
+ * Pick a non-blank raw value from the environment. Returns the original
21
+ * (untrimmed) string when it contains at least one non-whitespace
22
+ * character, otherwise `undefined`.
23
+ */
24
+ function pickNonBlank(raw) {
25
+ if (typeof raw !== "string")
26
+ return undefined;
27
+ return raw.trim().length > 0 ? raw : undefined;
28
+ }
29
+ /**
30
+ * Resolve the Brave API key without throwing. Returns `undefined` when
31
+ * no non-blank value is present.
32
+ */
33
+ export function resolveBraveApiKey(env) {
34
+ return pickNonBlank(env.BRAVE_SEARCH_API_KEY);
35
+ }
36
+ /**
37
+ * Resolve the Brave API key or throw {@link ConfigurationError} (exit 3)
38
+ * when it is missing. Call this at every Capability invocation gate.
39
+ */
40
+ export function requireBraveApiKey(env) {
41
+ const key = resolveBraveApiKey(env);
42
+ if (key === undefined) {
43
+ throw new ConfigurationError("BRAVE_SEARCH_API_KEY environment variable is required", MISSING_KEY_HELP);
44
+ }
45
+ return key;
46
+ }
47
+ /**
48
+ * True when a non-blank Brave API key is configured. Metadata-only:
49
+ * performs no transport construction and reads no other Provider's
50
+ * credentials.
51
+ */
52
+ export function isBraveConfigured(env) {
53
+ return resolveBraveApiKey(env) !== undefined;
54
+ }
55
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../../src/providers/brave/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,gBAAgB,GAAG,4CAA4C,CAAC;AAEtE;;;;GAIG;AACH,SAAS,YAAY,CAAC,GAAY;IAChC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC9C,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAsB;IACvD,OAAO,YAAY,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAsB;IACvD,MAAM,GAAG,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,kBAAkB,CAC1B,uDAAuD,EACvD,gBAAgB,CACjB,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAsB;IACtD,OAAO,kBAAkB,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Brave Diagnostics Capability (brave-tech-plan §3, §7; T5).
3
+ *
4
+ * Probes Brave connectivity with a single, cheapest-possible request
5
+ * — one `/res/v1/web/search` GET with a stub query — so the doctor
6
+ * command can verify a credential authenticates without a generative
7
+ * request. Brave has no `search_depth`-style knob (unlike Tavily), so
8
+ * the probe sends a bare `q` and no other params.
9
+ *
10
+ * The probe performs exactly ONE attempt. Shared execution owns the
11
+ * retry policy; this transport never retries. The doctor command
12
+ * catches the throw on failure and records a redacted error entry.
13
+ *
14
+ * When `options.probe` is false, `invoke` resolves immediately without
15
+ * touching the network — the doctor command skips probing unconfigured
16
+ * Providers (passing `probe: false`) before reaching this Capability.
17
+ *
18
+ * Boundary rules (ARCHITECTURE.md §2):
19
+ * - May import the diagnostics capability contract, Adapter-local
20
+ * credentials, Adapter-local search transport, and normalized errors.
21
+ * - Must NOT import command presentation or another Provider's Adapter.
22
+ */
23
+ import type { DiagnosticsCapability } from "../../capabilities/diagnostics.js";
24
+ import { type BraveTransportDeps } from "./client.js";
25
+ /**
26
+ * Options for the Brave DiagnosticsCapability. The API key is resolved
27
+ * from `env`; transport dependencies (`fetch`, timer) are injectable
28
+ * for deterministic tests through the unified `transport` seam.
29
+ */
30
+ export interface BraveDiagnosticsCapabilityOptions {
31
+ readonly env: NodeJS.ProcessEnv;
32
+ readonly transport?: BraveTransportDeps;
33
+ }
34
+ /**
35
+ * Build the Brave DiagnosticsCapability. The probe performs ONE
36
+ * `/res/v1/web/search` request with a stub query (`scoutline-doctor-
37
+ * probe`) and no params — the cheapest credible probe. Shared
38
+ * execution owns the retry policy; this transport performs exactly one
39
+ * attempt per invocation.
40
+ *
41
+ * When `options.probe` is false, `invoke` resolves immediately without
42
+ * touching the network — the doctor command skips probing unconfigured
43
+ * Providers before reaching this Capability.
44
+ */
45
+ export declare function createBraveDiagnosticsCapability(options: BraveDiagnosticsCapabilityOptions): DiagnosticsCapability;
46
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../src/providers/brave/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAqB,MAAM,mCAAmC,CAAC;AASlG,OAAO,EAAoB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AA+BxE;;;;GAIG;AACH,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC;CACzC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,iCAAiC,GACzC,qBAAqB,CAevB"}