scoutline 0.6.4 → 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.
- package/README.md +19 -12
- package/bin/scoutline.js +2 -3
- package/dist/capabilities/crawl.d.ts +93 -0
- package/dist/capabilities/crawl.d.ts.map +1 -0
- package/dist/capabilities/crawl.js +62 -0
- package/dist/capabilities/crawl.js.map +1 -0
- package/dist/capabilities/diagnostics.d.ts +45 -43
- package/dist/capabilities/diagnostics.d.ts.map +1 -1
- package/dist/capabilities/diagnostics.js +58 -71
- package/dist/capabilities/diagnostics.js.map +1 -1
- package/dist/capabilities/map.d.ts +81 -0
- package/dist/capabilities/map.d.ts.map +1 -0
- package/dist/capabilities/map.js +60 -0
- package/dist/capabilities/map.js.map +1 -0
- package/dist/capabilities/quota.d.ts +11 -0
- package/dist/capabilities/quota.d.ts.map +1 -1
- package/dist/capabilities/quota.js +1 -1
- package/dist/capabilities/quota.js.map +1 -1
- package/dist/capabilities/research.d.ts +98 -0
- package/dist/capabilities/research.d.ts.map +1 -0
- package/dist/capabilities/research.js +71 -0
- package/dist/capabilities/research.js.map +1 -0
- package/dist/capabilities/search.d.ts +19 -1
- package/dist/capabilities/search.d.ts.map +1 -1
- package/dist/commands/crawl.d.ts +46 -0
- package/dist/commands/crawl.d.ts.map +1 -0
- package/dist/commands/crawl.js +170 -0
- package/dist/commands/crawl.js.map +1 -0
- package/dist/commands/doctor.d.ts +7 -8
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +40 -34
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/map.d.ts +39 -0
- package/dist/commands/map.d.ts.map +1 -0
- package/dist/commands/map.js +121 -0
- package/dist/commands/map.js.map +1 -0
- package/dist/commands/quota.d.ts +23 -0
- package/dist/commands/quota.d.ts.map +1 -1
- package/dist/commands/quota.js +21 -4
- package/dist/commands/quota.js.map +1 -1
- package/dist/commands/read.d.ts.map +1 -1
- package/dist/commands/read.js +7 -1
- package/dist/commands/read.js.map +1 -1
- package/dist/commands/repo.js +1 -1
- package/dist/commands/research.d.ts +62 -0
- package/dist/commands/research.d.ts.map +1 -0
- package/dist/commands/research.js +314 -0
- package/dist/commands/research.js.map +1 -0
- package/dist/commands/search.d.ts +3 -1
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/search.js +26 -10
- package/dist/commands/search.js.map +1 -1
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +291 -13
- package/dist/index.js.map +1 -1
- package/dist/lib/cache.d.ts +13 -0
- package/dist/lib/cache.d.ts.map +1 -1
- package/dist/lib/cache.js +15 -0
- package/dist/lib/cache.js.map +1 -1
- package/dist/lib/execution.d.ts +105 -1
- package/dist/lib/execution.d.ts.map +1 -1
- package/dist/lib/execution.js +73 -1
- package/dist/lib/execution.js.map +1 -1
- package/dist/lib/redact.d.ts +2 -1
- package/dist/lib/redact.d.ts.map +1 -1
- package/dist/lib/redact.js +14 -1
- package/dist/lib/redact.js.map +1 -1
- package/dist/lib/research-state.d.ts +104 -0
- package/dist/lib/research-state.d.ts.map +1 -0
- package/dist/lib/research-state.js +214 -0
- package/dist/lib/research-state.js.map +1 -0
- package/dist/lib/search-topic.d.ts +21 -0
- package/dist/lib/search-topic.d.ts.map +1 -0
- package/dist/lib/search-topic.js +36 -0
- package/dist/lib/search-topic.js.map +1 -0
- package/dist/providers/brave/adapter.d.ts +42 -0
- package/dist/providers/brave/adapter.d.ts.map +1 -0
- package/dist/providers/brave/adapter.js +504 -0
- package/dist/providers/brave/adapter.js.map +1 -0
- package/dist/providers/brave/client.d.ts +161 -0
- package/dist/providers/brave/client.d.ts.map +1 -0
- package/dist/providers/brave/client.js +297 -0
- package/dist/providers/brave/client.js.map +1 -0
- package/dist/providers/brave/credentials.d.ts +33 -0
- package/dist/providers/brave/credentials.d.ts.map +1 -0
- package/dist/providers/brave/credentials.js +55 -0
- package/dist/providers/brave/credentials.js.map +1 -0
- package/dist/providers/brave/diagnostics.d.ts +46 -0
- package/dist/providers/brave/diagnostics.d.ts.map +1 -0
- package/dist/providers/brave/diagnostics.js +76 -0
- package/dist/providers/brave/diagnostics.js.map +1 -0
- package/dist/providers/brave/quota.d.ts +86 -0
- package/dist/providers/brave/quota.d.ts.map +1 -0
- package/dist/providers/brave/quota.js +247 -0
- package/dist/providers/brave/quota.js.map +1 -0
- package/dist/providers/exa/adapter.d.ts +58 -0
- package/dist/providers/exa/adapter.d.ts.map +1 -0
- package/dist/providers/exa/adapter.js +892 -0
- package/dist/providers/exa/adapter.js.map +1 -0
- package/dist/providers/exa/client.d.ts +143 -0
- package/dist/providers/exa/client.d.ts.map +1 -0
- package/dist/providers/exa/client.js +328 -0
- package/dist/providers/exa/client.js.map +1 -0
- package/dist/providers/exa/credentials.d.ts +34 -0
- package/dist/providers/exa/credentials.d.ts.map +1 -0
- package/dist/providers/exa/credentials.js +56 -0
- package/dist/providers/exa/credentials.js.map +1 -0
- package/dist/providers/exa/diagnostics.d.ts +42 -0
- package/dist/providers/exa/diagnostics.d.ts.map +1 -0
- package/dist/providers/exa/diagnostics.js +69 -0
- package/dist/providers/exa/diagnostics.js.map +1 -0
- package/dist/providers/minimax/adapter.d.ts.map +1 -1
- package/dist/providers/minimax/adapter.js +7 -2
- package/dist/providers/minimax/adapter.js.map +1 -1
- package/dist/providers/minimax/vision-conformance.d.ts +2 -2
- package/dist/providers/minimax/vision-conformance.js +2 -2
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +6 -0
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/tavily/adapter.d.ts +62 -0
- package/dist/providers/tavily/adapter.d.ts.map +1 -0
- package/dist/providers/tavily/adapter.js +975 -0
- package/dist/providers/tavily/adapter.js.map +1 -0
- package/dist/providers/tavily/client.d.ts +184 -0
- package/dist/providers/tavily/client.d.ts.map +1 -0
- package/dist/providers/tavily/client.js +486 -0
- package/dist/providers/tavily/client.js.map +1 -0
- package/dist/providers/tavily/credentials.d.ts +34 -0
- package/dist/providers/tavily/credentials.d.ts.map +1 -0
- package/dist/providers/tavily/credentials.js +56 -0
- package/dist/providers/tavily/credentials.js.map +1 -0
- package/dist/providers/tavily/diagnostics.d.ts +44 -0
- package/dist/providers/tavily/diagnostics.d.ts.map +1 -0
- package/dist/providers/tavily/diagnostics.js +70 -0
- package/dist/providers/tavily/diagnostics.js.map +1 -0
- package/dist/providers/tavily/quota.d.ts +60 -0
- package/dist/providers/tavily/quota.d.ts.map +1 -0
- package/dist/providers/tavily/quota.js +186 -0
- package/dist/providers/tavily/quota.js.map +1 -0
- package/dist/providers/types.d.ts +14 -2
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js +1 -1
- package/dist/providers/types.js.map +1 -1
- package/dist/providers/zai/adapter.d.ts.map +1 -1
- package/dist/providers/zai/adapter.js +13 -2
- package/dist/providers/zai/adapter.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,76 @@
|
|
|
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 { ApiError, AuthError, ConfigurationError, NetworkError, TimeoutError, } from "../../lib/errors.js";
|
|
24
|
+
import { requireBraveApiKey } from "./credentials.js";
|
|
25
|
+
import { fetchBraveSearch } from "./client.js";
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Failure normalization (mirror the Adapter's search-path mapping)
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
/**
|
|
30
|
+
* Probe failure wrapper. Mirrors the Adapter's `normalizeBraveError`
|
|
31
|
+
* for the subset of errors the probe can surface. The probe throws on
|
|
32
|
+
* failure; the doctor command catches the throw and records a redacted
|
|
33
|
+
* error entry. The Brave transport already drains/discards response
|
|
34
|
+
* bodies, and these typed errors carry only curated messages, so no
|
|
35
|
+
* raw Brave body ever crosses this boundary.
|
|
36
|
+
*/
|
|
37
|
+
function normalizeProbeError(error) {
|
|
38
|
+
if (error instanceof AuthError ||
|
|
39
|
+
error instanceof ApiError ||
|
|
40
|
+
error instanceof NetworkError ||
|
|
41
|
+
error instanceof TimeoutError ||
|
|
42
|
+
error instanceof ConfigurationError) {
|
|
43
|
+
return error;
|
|
44
|
+
}
|
|
45
|
+
return new ApiError("Brave diagnostics probe failed", 500);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build the Brave DiagnosticsCapability. The probe performs ONE
|
|
49
|
+
* `/res/v1/web/search` request with a stub query (`scoutline-doctor-
|
|
50
|
+
* probe`) and no params — the cheapest credible probe. Shared
|
|
51
|
+
* execution owns the retry policy; this transport performs exactly one
|
|
52
|
+
* attempt per invocation.
|
|
53
|
+
*
|
|
54
|
+
* When `options.probe` is false, `invoke` resolves immediately without
|
|
55
|
+
* touching the network — the doctor command skips probing unconfigured
|
|
56
|
+
* Providers before reaching this Capability.
|
|
57
|
+
*/
|
|
58
|
+
export function createBraveDiagnosticsCapability(options) {
|
|
59
|
+
const { env, transport } = options;
|
|
60
|
+
return {
|
|
61
|
+
async invoke(diagOptions) {
|
|
62
|
+
if (!diagOptions.probe)
|
|
63
|
+
return;
|
|
64
|
+
const apiKey = requireBraveApiKey(env);
|
|
65
|
+
try {
|
|
66
|
+
// One web-search GET, bare `q`, no params — cheapest credible
|
|
67
|
+
// probe (Brave has no search_depth knob).
|
|
68
|
+
await fetchBraveSearch(apiKey, "scoutline-doctor-probe", undefined, transport);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
throw normalizeProbeError(error);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../../src/providers/brave/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EACL,QAAQ,EACR,SAAS,EACT,kBAAkB,EAClB,YAAY,EACZ,YAAY,GACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAA2B,MAAM,aAAa,CAAC;AAExE,8EAA8E;AAC9E,mEAAmE;AACnE,8EAA8E;AAE9E;;;;;;;GAOG;AACH,SAAS,mBAAmB,CAAC,KAAc;IACzC,IACE,KAAK,YAAY,SAAS;QAC1B,KAAK,YAAY,QAAQ;QACzB,KAAK,YAAY,YAAY;QAC7B,KAAK,YAAY,YAAY;QAC7B,KAAK,YAAY,kBAAkB,EACnC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;AAC7D,CAAC;AAgBD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gCAAgC,CAC9C,OAA0C;IAE1C,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IACnC,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,WAA8B;YACzC,IAAI,CAAC,WAAW,CAAC,KAAK;gBAAE,OAAO;YAC/B,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;YACvC,IAAI,CAAC;gBACH,8DAA8D;gBAC9D,0CAA0C;gBAC1C,MAAM,gBAAgB,CAAC,MAAM,EAAE,wBAAwB,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;YACjF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,mBAAmB,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brave Quota Capability (brave-tech-plan §3.5, §7, §10 #5; T6).
|
|
3
|
+
*
|
|
4
|
+
* Brave has NO `/usage` endpoint, so quota is read from the four
|
|
5
|
+
* `X-RateLimit-*` response headers on a 1-query `/web/search` probe.
|
|
6
|
+
* The normalizer is pure; the capability factory owns configuration
|
|
7
|
+
* resolution, the single direct transport attempt, and failure
|
|
8
|
+
* normalization. Shared execution owns retry policy.
|
|
9
|
+
*
|
|
10
|
+
* Brave rate-limit mapping (brave-tech-plan §10):
|
|
11
|
+
* - `X-RateLimit-Policy` declares the windows as `"<limit>;w=<sec>"`
|
|
12
|
+
* entries separated by `,` (e.g. `"1;w=1, 15000;w=2592000"`).
|
|
13
|
+
* - `X-RateLimit-Limit` / `-Remaining` / `-Reset` are comma-separated
|
|
14
|
+
* arrays ALIGNED with Policy by index.
|
|
15
|
+
*
|
|
16
|
+
* The LARGEST window (≈ monthly) is surfaced as a single "monthly"
|
|
17
|
+
* category; the per-second window is DROPPED — a rate cap does not fit
|
|
18
|
+
* the used/limit shape and there is no numeric metadata slot in
|
|
19
|
+
* {@link ProviderQuotaSuccess} (critique H3). Because the number is a
|
|
20
|
+
* rate-limit window, NOT spend or credits consumed under Brave's
|
|
21
|
+
* metered billing, a prominent caveat is attached via the generic
|
|
22
|
+
* `warnings` channel so the quota command can render it to stderr
|
|
23
|
+
* without learning Brave's billing model.
|
|
24
|
+
*
|
|
25
|
+
* Boundary rules (ARCHITECTURE.md §2):
|
|
26
|
+
* - May import the quota capability contract, Adapter-local config,
|
|
27
|
+
* Adapter-local quota client, and normalized errors.
|
|
28
|
+
* - Must NOT import command presentation or another Provider's
|
|
29
|
+
* Adapter.
|
|
30
|
+
*/
|
|
31
|
+
import type { ProviderQuotaSuccess, QuotaCapability } from "../../capabilities/quota.js";
|
|
32
|
+
import { type BraveTransportDeps } from "./client.js";
|
|
33
|
+
/**
|
|
34
|
+
* Caveat attached to every Brave quota result: the number is a
|
|
35
|
+
* rate-limit window (requests remaining this period), NOT spend or
|
|
36
|
+
* credits consumed. Brave uses metered billing, so this is not a budget
|
|
37
|
+
* signal. Surfaced to stderr by the provider-neutral quota command.
|
|
38
|
+
*/
|
|
39
|
+
export declare const BRAVE_QUOTA_CAVEAT = "Brave quota reflects a rate-limit window (requests remaining this period), not spend or credits consumed. Brave uses metered billing \u2014 this is not a budget signal.";
|
|
40
|
+
/**
|
|
41
|
+
* Normalize Brave `X-RateLimit-*` headers into the shared Interface.
|
|
42
|
+
*
|
|
43
|
+
* Selects the LARGEST `windowSeconds` window declared by `Policy` (≈
|
|
44
|
+
* monthly) and surfaces it as a single category named for that window
|
|
45
|
+
* (monthly/weekly/daily, else `rate_limit`); the per-second window is
|
|
46
|
+
* dropped. For the selected window: `used = limit −
|
|
47
|
+
* remaining` (clamped to `[0, limit]` when remaining is out of range),
|
|
48
|
+
* `resetsAt = now + reset*1000ms`, `durationSeconds = windowSeconds`.
|
|
49
|
+
*
|
|
50
|
+
* Throws `QUOTA_ERROR` when the headers are missing/malformed, when no
|
|
51
|
+
* window parses, or when the selected window's limit/remaining/reset
|
|
52
|
+
* values are indeterminate (e.g. the arrays do not align by index).
|
|
53
|
+
* Never guesses; never crashes.
|
|
54
|
+
*/
|
|
55
|
+
export declare function normalizeBraveQuota(headers: {
|
|
56
|
+
readonly limit: string | null;
|
|
57
|
+
readonly policy: string | null;
|
|
58
|
+
readonly remaining: string | null;
|
|
59
|
+
readonly reset: string | null;
|
|
60
|
+
},
|
|
61
|
+
/**
|
|
62
|
+
* Injectable clock for the `resetsAt` derivation (`now + reset·s`).
|
|
63
|
+
* Defaults to `Date.now`; tests pass a fixed value to assert the exact
|
|
64
|
+
* ISO timestamp rather than a non-deterministic "now"-relative value.
|
|
65
|
+
*/
|
|
66
|
+
now?: () => number): ProviderQuotaSuccess;
|
|
67
|
+
/**
|
|
68
|
+
* Options for the Brave QuotaCapability. The API key is resolved from
|
|
69
|
+
* `env`; transport dependencies (`fetch`, timer) are injectable for
|
|
70
|
+
* deterministic tests through the unified `transport` seam.
|
|
71
|
+
*/
|
|
72
|
+
export interface BraveQuotaCapabilityOptions {
|
|
73
|
+
readonly env: NodeJS.ProcessEnv;
|
|
74
|
+
readonly transport?: BraveTransportDeps;
|
|
75
|
+
/** Optional injectable clock for deterministic `resetsAt` in tests. */
|
|
76
|
+
readonly now?: () => number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Build the Brave QuotaCapability. `invoke` resolves the API key,
|
|
80
|
+
* performs one direct `/web/search` probe (costs exactly ONE request),
|
|
81
|
+
* reads the `X-RateLimit-*` headers, and normalizes the largest window
|
|
82
|
+
* into the shared Interface. Shared execution wraps this in the retry
|
|
83
|
+
* policy; quota never uses the response cache.
|
|
84
|
+
*/
|
|
85
|
+
export declare function createBraveQuotaCapability(options: BraveQuotaCapabilityOptions): QuotaCapability;
|
|
86
|
+
//# sourceMappingURL=quota.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota.d.ts","sourceRoot":"","sources":["../../../src/providers/brave/quota.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EAEhB,MAAM,6BAA6B,CAAC;AAWrC,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE3E;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,6KACwI,CAAC;AA8DxK;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE;IACP,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;AACD;;;;GAIG;AACH,GAAG,GAAE,MAAM,MAAiB,GAC3B,oBAAoB,CAiGtB;AA6BD;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC;IACxC,uEAAuE;IACvE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,2BAA2B,GAAG,eAAe,CAahG"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brave Quota Capability (brave-tech-plan §3.5, §7, §10 #5; T6).
|
|
3
|
+
*
|
|
4
|
+
* Brave has NO `/usage` endpoint, so quota is read from the four
|
|
5
|
+
* `X-RateLimit-*` response headers on a 1-query `/web/search` probe.
|
|
6
|
+
* The normalizer is pure; the capability factory owns configuration
|
|
7
|
+
* resolution, the single direct transport attempt, and failure
|
|
8
|
+
* normalization. Shared execution owns retry policy.
|
|
9
|
+
*
|
|
10
|
+
* Brave rate-limit mapping (brave-tech-plan §10):
|
|
11
|
+
* - `X-RateLimit-Policy` declares the windows as `"<limit>;w=<sec>"`
|
|
12
|
+
* entries separated by `,` (e.g. `"1;w=1, 15000;w=2592000"`).
|
|
13
|
+
* - `X-RateLimit-Limit` / `-Remaining` / `-Reset` are comma-separated
|
|
14
|
+
* arrays ALIGNED with Policy by index.
|
|
15
|
+
*
|
|
16
|
+
* The LARGEST window (≈ monthly) is surfaced as a single "monthly"
|
|
17
|
+
* category; the per-second window is DROPPED — a rate cap does not fit
|
|
18
|
+
* the used/limit shape and there is no numeric metadata slot in
|
|
19
|
+
* {@link ProviderQuotaSuccess} (critique H3). Because the number is a
|
|
20
|
+
* rate-limit window, NOT spend or credits consumed under Brave's
|
|
21
|
+
* metered billing, a prominent caveat is attached via the generic
|
|
22
|
+
* `warnings` channel so the quota command can render it to stderr
|
|
23
|
+
* without learning Brave's billing model.
|
|
24
|
+
*
|
|
25
|
+
* Boundary rules (ARCHITECTURE.md §2):
|
|
26
|
+
* - May import the quota capability contract, Adapter-local config,
|
|
27
|
+
* Adapter-local quota client, and normalized errors.
|
|
28
|
+
* - Must NOT import command presentation or another Provider's
|
|
29
|
+
* Adapter.
|
|
30
|
+
*/
|
|
31
|
+
import { buildQuotaWindow } from "../../capabilities/quota.js";
|
|
32
|
+
import { ApiError, AuthError, ConfigurationError, NetworkError, ScoutlineError, TimeoutError, } from "../../lib/errors.js";
|
|
33
|
+
import { requireBraveApiKey } from "./credentials.js";
|
|
34
|
+
import { fetchBraveRateLimit } from "./client.js";
|
|
35
|
+
/**
|
|
36
|
+
* Caveat attached to every Brave quota result: the number is a
|
|
37
|
+
* rate-limit window (requests remaining this period), NOT spend or
|
|
38
|
+
* credits consumed. Brave uses metered billing, so this is not a budget
|
|
39
|
+
* signal. Surfaced to stderr by the provider-neutral quota command.
|
|
40
|
+
*/
|
|
41
|
+
export const BRAVE_QUOTA_CAVEAT = "Brave quota reflects a rate-limit window (requests remaining this period), not spend or credits consumed. Brave uses metered billing — this is not a budget signal.";
|
|
42
|
+
/**
|
|
43
|
+
* Error thrown when the rate-limit headers cannot be parsed into a
|
|
44
|
+
* usable window. Brave's header format is the only quota signal, so a
|
|
45
|
+
* malformed/missing/unaligned set is unrecoverable: never guess, never
|
|
46
|
+
* crash — surface `QUOTA_ERROR` instead.
|
|
47
|
+
*/
|
|
48
|
+
function braveQuotaParseError() {
|
|
49
|
+
return new ScoutlineError("Brave quota headers could not be parsed", "QUOTA_ERROR", {
|
|
50
|
+
exitCode: 1,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Split a comma-separated header value into trimmed entries. Returns
|
|
55
|
+
* `null` when the value is absent or blank (no windows to parse).
|
|
56
|
+
*/
|
|
57
|
+
function readHeaderCsv(raw) {
|
|
58
|
+
if (raw === null || raw.trim() === "")
|
|
59
|
+
return null;
|
|
60
|
+
return raw.split(",").map((s) => s.trim());
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parse a header slot as a finite nonnegative number. Returns `NaN`
|
|
64
|
+
* (falsy for `Number.isFinite`) when the value is absent or non-numeric
|
|
65
|
+
* so callers can treat it uniformly as "indeterminate".
|
|
66
|
+
*/
|
|
67
|
+
function parseFiniteNumber(value) {
|
|
68
|
+
if (value === undefined)
|
|
69
|
+
return NaN;
|
|
70
|
+
const n = Number(value);
|
|
71
|
+
return Number.isFinite(n) ? n : NaN;
|
|
72
|
+
}
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Normalizer
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
const ONE_DAY_SECONDS = 86400;
|
|
77
|
+
/**
|
|
78
|
+
* Derive a category name from the selected window's duration so the
|
|
79
|
+
* label cannot drift from the window it represents (the largest window
|
|
80
|
+
* is usually ~30 days, but Brave's tiers are not guaranteed monthly).
|
|
81
|
+
* Falls back to a neutral `rate_limit` for unrecognized window sizes.
|
|
82
|
+
*/
|
|
83
|
+
function rateLimitWindowName(windowSeconds) {
|
|
84
|
+
if (windowSeconds >= 28 * ONE_DAY_SECONDS)
|
|
85
|
+
return "monthly";
|
|
86
|
+
if (windowSeconds >= 6 * ONE_DAY_SECONDS)
|
|
87
|
+
return "weekly";
|
|
88
|
+
if (windowSeconds >= ONE_DAY_SECONDS)
|
|
89
|
+
return "daily";
|
|
90
|
+
return "rate_limit";
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Normalize Brave `X-RateLimit-*` headers into the shared Interface.
|
|
94
|
+
*
|
|
95
|
+
* Selects the LARGEST `windowSeconds` window declared by `Policy` (≈
|
|
96
|
+
* monthly) and surfaces it as a single category named for that window
|
|
97
|
+
* (monthly/weekly/daily, else `rate_limit`); the per-second window is
|
|
98
|
+
* dropped. For the selected window: `used = limit −
|
|
99
|
+
* remaining` (clamped to `[0, limit]` when remaining is out of range),
|
|
100
|
+
* `resetsAt = now + reset*1000ms`, `durationSeconds = windowSeconds`.
|
|
101
|
+
*
|
|
102
|
+
* Throws `QUOTA_ERROR` when the headers are missing/malformed, when no
|
|
103
|
+
* window parses, or when the selected window's limit/remaining/reset
|
|
104
|
+
* values are indeterminate (e.g. the arrays do not align by index).
|
|
105
|
+
* Never guesses; never crashes.
|
|
106
|
+
*/
|
|
107
|
+
export function normalizeBraveQuota(headers,
|
|
108
|
+
/**
|
|
109
|
+
* Injectable clock for the `resetsAt` derivation (`now + reset·s`).
|
|
110
|
+
* Defaults to `Date.now`; tests pass a fixed value to assert the exact
|
|
111
|
+
* ISO timestamp rather than a non-deterministic "now"-relative value.
|
|
112
|
+
*/
|
|
113
|
+
now = Date.now) {
|
|
114
|
+
// Parse Policy into windows: "1;w=1, 15000;w=2592000" → [{1,1},{15000,2592000}]
|
|
115
|
+
const policyParts = readHeaderCsv(headers.policy);
|
|
116
|
+
if (policyParts === null) {
|
|
117
|
+
throw braveQuotaParseError();
|
|
118
|
+
}
|
|
119
|
+
const windows = [];
|
|
120
|
+
for (const part of policyParts) {
|
|
121
|
+
if (part === "") {
|
|
122
|
+
throw braveQuotaParseError();
|
|
123
|
+
}
|
|
124
|
+
// Each entry is "<limit>;w=<seconds>".
|
|
125
|
+
const segs = part.split(";").map((s) => s.trim());
|
|
126
|
+
if (segs.length !== 2 || segs[0] === "" || segs[1] === "") {
|
|
127
|
+
throw braveQuotaParseError();
|
|
128
|
+
}
|
|
129
|
+
const limit = parseFiniteNumber(segs[0]);
|
|
130
|
+
const wMatch = /^w=(\d+)$/.exec(segs[1]);
|
|
131
|
+
const windowSeconds = wMatch ? parseFiniteNumber(wMatch[1]) : NaN;
|
|
132
|
+
if (!Number.isFinite(limit) ||
|
|
133
|
+
limit < 0 ||
|
|
134
|
+
!Number.isFinite(windowSeconds) ||
|
|
135
|
+
windowSeconds <= 0) {
|
|
136
|
+
throw braveQuotaParseError();
|
|
137
|
+
}
|
|
138
|
+
windows.push({ limit, windowSeconds });
|
|
139
|
+
}
|
|
140
|
+
if (windows.length === 0) {
|
|
141
|
+
throw braveQuotaParseError();
|
|
142
|
+
}
|
|
143
|
+
// Align Limit/Remaining/Reset arrays by index with Policy windows.
|
|
144
|
+
const limitParts = readHeaderCsv(headers.limit);
|
|
145
|
+
const remainingParts = readHeaderCsv(headers.remaining);
|
|
146
|
+
const resetParts = readHeaderCsv(headers.reset);
|
|
147
|
+
// Select the LARGEST window with a NON-ZERO limit. A limit of 0 means
|
|
148
|
+
// "no fixed cap on that period" (e.g. a metered plan's monthly window
|
|
149
|
+
// reports limit=0/remaining=0), which yields no usable used/remaining
|
|
150
|
+
// and would otherwise throw — skip such windows and fall back to the
|
|
151
|
+
// next-largest window that carries a real cap (do NOT hardcode 2592000).
|
|
152
|
+
let selectedIndex = -1;
|
|
153
|
+
for (let i = 0; i < windows.length; i++) {
|
|
154
|
+
if (windows[i].limit <= 0)
|
|
155
|
+
continue;
|
|
156
|
+
if (selectedIndex === -1 || windows[i].windowSeconds > windows[selectedIndex].windowSeconds) {
|
|
157
|
+
selectedIndex = i;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (selectedIndex === -1) {
|
|
161
|
+
throw braveQuotaParseError();
|
|
162
|
+
}
|
|
163
|
+
const selectedLimit = parseFiniteNumber(limitParts?.[selectedIndex]);
|
|
164
|
+
const selectedRemaining = parseFiniteNumber(remainingParts?.[selectedIndex]);
|
|
165
|
+
const selectedReset = parseFiniteNumber(resetParts?.[selectedIndex]);
|
|
166
|
+
// The selected window's counts must all be finite nonnegative numbers;
|
|
167
|
+
// an unaligned/indeterminate set is unrecoverable.
|
|
168
|
+
if (!Number.isFinite(selectedLimit) ||
|
|
169
|
+
selectedLimit < 0 ||
|
|
170
|
+
!Number.isFinite(selectedRemaining) ||
|
|
171
|
+
selectedRemaining < 0 ||
|
|
172
|
+
!Number.isFinite(selectedReset) ||
|
|
173
|
+
selectedReset < 0) {
|
|
174
|
+
throw braveQuotaParseError();
|
|
175
|
+
}
|
|
176
|
+
// used = limit − remaining; clamp to [0, limit] when remaining is out
|
|
177
|
+
// of range so the derived percentage stays meaningful.
|
|
178
|
+
let used = selectedLimit - selectedRemaining;
|
|
179
|
+
if (used < 0)
|
|
180
|
+
used = 0;
|
|
181
|
+
if (used > selectedLimit)
|
|
182
|
+
used = selectedLimit;
|
|
183
|
+
const resetsAtEpochMs = now() + selectedReset * 1000;
|
|
184
|
+
const current = buildQuotaWindow({
|
|
185
|
+
used,
|
|
186
|
+
limit: selectedLimit,
|
|
187
|
+
resetsAtEpochMs,
|
|
188
|
+
durationSeconds: windows[selectedIndex].windowSeconds,
|
|
189
|
+
});
|
|
190
|
+
const category = {
|
|
191
|
+
name: rateLimitWindowName(windows[selectedIndex].windowSeconds),
|
|
192
|
+
unit: "requests",
|
|
193
|
+
current,
|
|
194
|
+
};
|
|
195
|
+
return {
|
|
196
|
+
provider: "brave",
|
|
197
|
+
status: "ok",
|
|
198
|
+
categories: [category],
|
|
199
|
+
warnings: [BRAVE_QUOTA_CAVEAT],
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// Capability factory
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
/**
|
|
206
|
+
* Map a thrown error into a normalized Brave quota failure. Typed
|
|
207
|
+
* transport errors (Auth/Api/Network/Timeout/Configuration) pass
|
|
208
|
+
* through verbatim, as does any {@link ScoutlineError} — notably the
|
|
209
|
+
* `QUOTA_ERROR` thrown by {@link normalizeBraveQuota} on malformed
|
|
210
|
+
* headers. Unknown errors become a generic `ApiError 500`. The Brave
|
|
211
|
+
* transport already drains/discards response bodies, so no raw Brave
|
|
212
|
+
* body ever crosses this boundary.
|
|
213
|
+
*/
|
|
214
|
+
function normalizeBraveQuotaError(error) {
|
|
215
|
+
if (error instanceof ScoutlineError ||
|
|
216
|
+
error instanceof AuthError ||
|
|
217
|
+
error instanceof ApiError ||
|
|
218
|
+
error instanceof NetworkError ||
|
|
219
|
+
error instanceof TimeoutError ||
|
|
220
|
+
error instanceof ConfigurationError) {
|
|
221
|
+
return error;
|
|
222
|
+
}
|
|
223
|
+
return new ApiError("Brave quota request failed", 500);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Build the Brave QuotaCapability. `invoke` resolves the API key,
|
|
227
|
+
* performs one direct `/web/search` probe (costs exactly ONE request),
|
|
228
|
+
* reads the `X-RateLimit-*` headers, and normalizes the largest window
|
|
229
|
+
* into the shared Interface. Shared execution wraps this in the retry
|
|
230
|
+
* policy; quota never uses the response cache.
|
|
231
|
+
*/
|
|
232
|
+
export function createBraveQuotaCapability(options) {
|
|
233
|
+
const { env, transport, now } = options;
|
|
234
|
+
return {
|
|
235
|
+
async invoke() {
|
|
236
|
+
const apiKey = requireBraveApiKey(env);
|
|
237
|
+
try {
|
|
238
|
+
const headers = await fetchBraveRateLimit(apiKey, transport);
|
|
239
|
+
return normalizeBraveQuota(headers, now);
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
throw normalizeBraveQuotaError(error);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=quota.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota.js","sourceRoot":"","sources":["../../../src/providers/brave/quota.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exa Provider Adapter (tech-plan §7, Control Mapping, Field Normalization,
|
|
3
|
+
* Failure Normalization).
|
|
4
|
+
*
|
|
5
|
+
* Implements the Exa Provider Descriptor with Search, Reader, Research,
|
|
6
|
+
* and Diagnostics capabilities on top of the direct-HTTP transport
|
|
7
|
+
* (`./client.ts`). The Adapter owns credentials, transport lifecycle,
|
|
8
|
+
* Provider field mapping, and failure normalization; shared execution
|
|
9
|
+
* owns cache and retry policy.
|
|
10
|
+
*
|
|
11
|
+
* Boundary rules (ARCHITECTURE.md §2):
|
|
12
|
+
* - May import capability types, normalized errors, Provider identity
|
|
13
|
+
* types, and the Adapter-local credential and transport Modules.
|
|
14
|
+
* - Must NOT import command presentation, output mode, or another
|
|
15
|
+
* Provider's Adapter.
|
|
16
|
+
*
|
|
17
|
+
* Field mapping (tech-plan §7 Exa mapping):
|
|
18
|
+
* Search results[].title -> title
|
|
19
|
+
* Search results[].url -> url
|
|
20
|
+
* Search results[].highlights[] -> summary (join with " ")
|
|
21
|
+
* Search results[].author -> source
|
|
22
|
+
* Search results[].publishedDate -> date
|
|
23
|
+
* Search results[].score -> (dropped)
|
|
24
|
+
*
|
|
25
|
+
* Control mapping (SearchControls → Exa-native API params):
|
|
26
|
+
* domain -> includeDomains: [domain]
|
|
27
|
+
* recency -> startPublishedDate (oneDay→now-1d ISO, etc.)
|
|
28
|
+
* contentSize -> type (medium/omitted→"auto", high→"deep")
|
|
29
|
+
* topic -> category (general→omit, news→"news",
|
|
30
|
+
* finance→"financial report")
|
|
31
|
+
* location -> REJECTED (UnsupportedOptionError)
|
|
32
|
+
*/
|
|
33
|
+
import type { ProviderDescriptor } from "../types.js";
|
|
34
|
+
import type { ResearchStateFile } from "../../lib/research-state.js";
|
|
35
|
+
import { type ExaTransportDeps } from "./client.js";
|
|
36
|
+
/**
|
|
37
|
+
* Dependencies the Exa Adapter accepts. The unified `transport` seam
|
|
38
|
+
* carries `fetch` and timer injection. Production defaults to the global
|
|
39
|
+
* `fetch` and timers inside the transport Module.
|
|
40
|
+
*/
|
|
41
|
+
export interface ExaAdapterDependencies {
|
|
42
|
+
/** Optional transport injection (fetch, timers, env). */
|
|
43
|
+
readonly transport?: ExaTransportDeps;
|
|
44
|
+
/** Optional Research state-file port (tech-plan §3). */
|
|
45
|
+
readonly researchStateFile?: ResearchStateFile;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build the Exa Provider Descriptor. The descriptor advertises the Exa
|
|
49
|
+
* capability set (search, reader, research, diagnostics) and constructs
|
|
50
|
+
* an Adapter whose Capabilities own credentials, transport, Provider
|
|
51
|
+
* field mapping, and failure normalization. Construction is
|
|
52
|
+
* side-effect-free; the transport is invoked per Capability call. Tests
|
|
53
|
+
* pass `transport` (typically a fake-fetch wrapper); production uses
|
|
54
|
+
* the no-argument factory which resolves to the global `fetch` and
|
|
55
|
+
* timers inside the transport Module.
|
|
56
|
+
*/
|
|
57
|
+
export declare function createExaDescriptor(dependencies?: ExaAdapterDependencies): ProviderDescriptor;
|
|
58
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/providers/exa/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAIH,OAAO,KAAK,EAIV,kBAAkB,EAEnB,MAAM,aAAa,CAAC;AAyBrB,OAAO,KAAK,EAAiB,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAkBpF,OAAO,EASL,KAAK,gBAAgB,EACtB,MAAM,aAAa,CAAC;AAGrB;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC;IACtC,wDAAwD;IACxD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CAChD;AAi7BD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,CAAC,EAAE,sBAAsB,GAAG,kBAAkB,CAiC7F"}
|