scoutline 0.10.2 → 0.12.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 +99 -44
- package/dist/capabilities/diagnostics.d.ts +75 -0
- package/dist/capabilities/diagnostics.d.ts.map +1 -1
- package/dist/capabilities/diagnostics.js.map +1 -1
- package/dist/capabilities/quota.d.ts +64 -1
- package/dist/capabilities/quota.d.ts.map +1 -1
- package/dist/capabilities/quota.js.map +1 -1
- package/dist/capabilities/vision.d.ts +4 -3
- package/dist/capabilities/vision.d.ts.map +1 -1
- package/dist/capabilities/vision.js +7 -5
- package/dist/capabilities/vision.js.map +1 -1
- package/dist/commands/code.d.ts +9 -1
- package/dist/commands/code.d.ts.map +1 -1
- package/dist/commands/code.js +4 -4
- package/dist/commands/code.js.map +1 -1
- package/dist/commands/crawl.d.ts.map +1 -1
- package/dist/commands/crawl.js +14 -3
- package/dist/commands/crawl.js.map +1 -1
- package/dist/commands/doctor.d.ts +71 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +96 -15
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts +186 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1221 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/map.d.ts.map +1 -1
- package/dist/commands/map.js +16 -3
- package/dist/commands/map.js.map +1 -1
- package/dist/commands/quota.d.ts +32 -0
- package/dist/commands/quota.d.ts.map +1 -1
- package/dist/commands/quota.js +165 -24
- package/dist/commands/quota.js.map +1 -1
- package/dist/commands/read.d.ts.map +1 -1
- package/dist/commands/read.js +9 -3
- package/dist/commands/read.js.map +1 -1
- package/dist/commands/repo.d.ts.map +1 -1
- package/dist/commands/repo.js +5 -3
- package/dist/commands/repo.js.map +1 -1
- package/dist/commands/research.d.ts +35 -7
- package/dist/commands/research.d.ts.map +1 -1
- package/dist/commands/research.js +93 -15
- package/dist/commands/research.js.map +1 -1
- package/dist/commands/tools.d.ts +8 -0
- package/dist/commands/tools.d.ts.map +1 -1
- package/dist/commands/tools.js +4 -4
- package/dist/commands/tools.js.map +1 -1
- package/dist/commands/vision.d.ts +10 -0
- package/dist/commands/vision.d.ts.map +1 -1
- package/dist/commands/vision.js +25 -2
- package/dist/commands/vision.js.map +1 -1
- package/dist/index.d.ts +136 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +894 -298
- package/dist/index.js.map +1 -1
- package/dist/lib/api-client.d.ts +1 -1
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +6 -2
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/cache.d.ts +9 -2
- package/dist/lib/cache.d.ts.map +1 -1
- package/dist/lib/cache.js +9 -2
- package/dist/lib/cache.js.map +1 -1
- package/dist/lib/code-mode.d.ts +8 -0
- package/dist/lib/code-mode.d.ts.map +1 -1
- package/dist/lib/code-mode.js +3 -1
- package/dist/lib/code-mode.js.map +1 -1
- package/dist/lib/config-store.d.ts +165 -0
- package/dist/lib/config-store.d.ts.map +1 -0
- package/dist/lib/config-store.js +332 -0
- package/dist/lib/config-store.js.map +1 -0
- package/dist/lib/config.d.ts +2 -2
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +19 -11
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/consumption.d.ts +144 -0
- package/dist/lib/consumption.d.ts.map +1 -0
- package/dist/lib/consumption.js +161 -0
- package/dist/lib/consumption.js.map +1 -0
- package/dist/lib/errors.d.ts +11 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +14 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/execution.d.ts +32 -4
- package/dist/lib/execution.d.ts.map +1 -1
- package/dist/lib/execution.js +156 -13
- package/dist/lib/execution.js.map +1 -1
- package/dist/lib/mcp-client.d.ts +12 -0
- package/dist/lib/mcp-client.d.ts.map +1 -1
- package/dist/lib/mcp-client.js +16 -7
- package/dist/lib/mcp-client.js.map +1 -1
- package/dist/lib/mcp-config.d.ts +10 -0
- package/dist/lib/mcp-config.d.ts.map +1 -1
- package/dist/lib/mcp-config.js +13 -7
- package/dist/lib/mcp-config.js.map +1 -1
- package/dist/lib/monitor-client.d.ts +19 -3
- package/dist/lib/monitor-client.d.ts.map +1 -1
- package/dist/lib/monitor-client.js +21 -5
- package/dist/lib/monitor-client.js.map +1 -1
- package/dist/lib/provider-fallback.d.ts +150 -0
- package/dist/lib/provider-fallback.d.ts.map +1 -0
- package/dist/lib/provider-fallback.js +510 -0
- package/dist/lib/provider-fallback.js.map +1 -0
- package/dist/lib/quota-mapping.d.ts +402 -0
- package/dist/lib/quota-mapping.d.ts.map +1 -0
- package/dist/lib/quota-mapping.js +539 -0
- package/dist/lib/quota-mapping.js.map +1 -0
- package/dist/lib/quota-store.d.ts +271 -0
- package/dist/lib/quota-store.d.ts.map +1 -0
- package/dist/lib/quota-store.js +540 -0
- package/dist/lib/quota-store.js.map +1 -0
- package/dist/lib/trigger-detection.d.ts +149 -0
- package/dist/lib/trigger-detection.d.ts.map +1 -0
- package/dist/lib/trigger-detection.js +149 -0
- package/dist/lib/trigger-detection.js.map +1 -0
- package/dist/lib/tty.d.ts +8 -1
- package/dist/lib/tty.d.ts.map +1 -1
- package/dist/lib/tty.js +53 -1
- package/dist/lib/tty.js.map +1 -1
- package/dist/providers/brave/adapter.d.ts +26 -0
- package/dist/providers/brave/adapter.d.ts.map +1 -1
- package/dist/providers/brave/adapter.js +66 -4
- package/dist/providers/brave/adapter.js.map +1 -1
- package/dist/providers/brave/client.d.ts +46 -2
- package/dist/providers/brave/client.d.ts.map +1 -1
- package/dist/providers/brave/client.js +55 -3
- package/dist/providers/brave/client.js.map +1 -1
- package/dist/providers/exa/adapter.d.ts.map +1 -1
- package/dist/providers/exa/adapter.js +10 -1
- package/dist/providers/exa/adapter.js.map +1 -1
- package/dist/providers/firecrawl/adapter.d.ts.map +1 -1
- package/dist/providers/firecrawl/adapter.js +13 -3
- package/dist/providers/firecrawl/adapter.js.map +1 -1
- package/dist/providers/minimax/adapter.d.ts.map +1 -1
- package/dist/providers/minimax/adapter.js +4 -0
- package/dist/providers/minimax/adapter.js.map +1 -1
- package/dist/providers/selection.d.ts +114 -1
- package/dist/providers/selection.d.ts.map +1 -1
- package/dist/providers/selection.js +99 -3
- package/dist/providers/selection.js.map +1 -1
- package/dist/providers/tavily/adapter.d.ts.map +1 -1
- package/dist/providers/tavily/adapter.js +2 -0
- package/dist/providers/tavily/adapter.js.map +1 -1
- package/dist/providers/types.d.ts +33 -1
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js +8 -0
- package/dist/providers/types.js.map +1 -1
- package/dist/providers/zai/adapter.d.ts.map +1 -1
- package/dist/providers/zai/adapter.js +18 -5
- package/dist/providers/zai/adapter.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trigger detection (T3b — Plan A, Option B).
|
|
3
|
+
*
|
|
4
|
+
* When the user runs a credentialed command without ever having gone
|
|
5
|
+
* through `scoutline init`, we want one of three classified outcomes
|
|
6
|
+
* rather than a confusing per-handler stack trace:
|
|
7
|
+
*
|
|
8
|
+
* - **env-only setup**: a key for the effective Provider is present
|
|
9
|
+
* in the injected env, but no Provider key is stored in
|
|
10
|
+
* `config.json`. We emit a ONE-TIME stderr hint pointing at
|
|
11
|
+
* `scoutline init` (so the user learns the wizard exists), persist
|
|
12
|
+
* `config.json.hintShown`, and then run the command normally. The
|
|
13
|
+
* command's natural output and exit code are preserved.
|
|
14
|
+
* - **missing credential everywhere**: the effective Provider has no
|
|
15
|
+
* key in env OR in `config.json`. We emit remediation and surface
|
|
16
|
+
* the existing `CONFIGURATION_ERROR` exit 3. A refused operation
|
|
17
|
+
* is NEVER exit 0 — scripting users must not treat refusal as
|
|
18
|
+
* success.
|
|
19
|
+
* - **credential-free commands** (`--help`, `--version`, `cache`,
|
|
20
|
+
* `init`, `<command> --help`): no config read at all. The T2a
|
|
21
|
+
* short-circuit already covers help/version/cache/init; this
|
|
22
|
+
* module exposes a helper to recognise per-command help so the
|
|
23
|
+
* trigger layer can route around it too (command help must remain
|
|
24
|
+
* usable under a corrupt config — see review item 9).
|
|
25
|
+
*
|
|
26
|
+
* Doctor and quota are observational: they report per-Provider state
|
|
27
|
+
* rather than depending on a single effective credential, so trigger
|
|
28
|
+
* detection does NOT classify them. They keep their existing per-handler
|
|
29
|
+
* configuration-error behavior (Doctor reports skipped/error states;
|
|
30
|
+
* quota surfaces its existing CONFIGURATION_ERROR when nothing is
|
|
31
|
+
* configured).
|
|
32
|
+
*
|
|
33
|
+
* Raw Z.AI commands (`tools`, `tool`, `call`, `code`) ARE credentialed
|
|
34
|
+
* and Z.AI-only, so missing-key remediation applies to them. They do
|
|
35
|
+
* NOT gain Provider selection or fallback (still Z.AI-only).
|
|
36
|
+
*
|
|
37
|
+
* All functions here are pure (they take their inputs as arguments and
|
|
38
|
+
* touch no module-level state). The dispatcher in `src/index.ts` wires
|
|
39
|
+
* the real `HintShownStore`, the injected env, the descriptors, and the
|
|
40
|
+
* stderr sink.
|
|
41
|
+
*/
|
|
42
|
+
import type { ProviderDescriptor } from "../providers/types.js";
|
|
43
|
+
import type { ScoutlineConfig } from "./config-store.js";
|
|
44
|
+
import { ConfigurationError } from "./errors.js";
|
|
45
|
+
/**
|
|
46
|
+
* Commands that are observational rather than credentialed. They are
|
|
47
|
+
* exempt from trigger detection because they already report per-Provider
|
|
48
|
+
* state instead of depending on a single effective credential.
|
|
49
|
+
*/
|
|
50
|
+
export declare const OBSERVATIONAL_COMMANDS: ReadonlySet<string>;
|
|
51
|
+
/**
|
|
52
|
+
* Commands that are credential-free at the dispatcher level (handled
|
|
53
|
+
* before the credentialed config read). Listed here for documentation
|
|
54
|
+
* completeness; the dispatcher short-circuits them before reaching the
|
|
55
|
+
* trigger layer.
|
|
56
|
+
*/
|
|
57
|
+
export declare const DISPATCHER_CREDENTIAL_FREE: ReadonlySet<string>;
|
|
58
|
+
/**
|
|
59
|
+
* Commands that are always Z.AI-only (raw Z.AI tools + Code Mode).
|
|
60
|
+
* Trigger detection treats them as credentialed; their effective
|
|
61
|
+
* Provider is always `zai` regardless of `--provider`.
|
|
62
|
+
*/
|
|
63
|
+
export declare const ZAI_ONLY_COMMANDS: ReadonlySet<string>;
|
|
64
|
+
/**
|
|
65
|
+
* The classified credential state for the command being dispatched.
|
|
66
|
+
* The dispatcher consumes this to decide whether to emit the hint,
|
|
67
|
+
* refuse, or proceed normally. The classification is computed across
|
|
68
|
+
* ALL descriptors (not just the effective Provider) so that Provider
|
|
69
|
+
* fallback can route to a configured candidate even when the effective
|
|
70
|
+
* is unconfigured — "missing" only fires when NO provider can serve
|
|
71
|
+
* the request.
|
|
72
|
+
*/
|
|
73
|
+
export type CredentialState = {
|
|
74
|
+
readonly kind: "env-only";
|
|
75
|
+
} | {
|
|
76
|
+
readonly kind: "file-configured";
|
|
77
|
+
} | {
|
|
78
|
+
readonly kind: "env-and-file";
|
|
79
|
+
} | {
|
|
80
|
+
readonly kind: "missing";
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Inputs to {@link classifyCredentialState}. Kept explicit so the
|
|
84
|
+
* classifier stays a pure function — no module-level reads, no
|
|
85
|
+
* descriptor lookup beyond what the caller passes in.
|
|
86
|
+
*/
|
|
87
|
+
export interface CredentialClassificationInput {
|
|
88
|
+
/** The full provider registry (so fallback candidates are considered). */
|
|
89
|
+
readonly descriptors: readonly ProviderDescriptor[];
|
|
90
|
+
/** The injected environment view (env vars only, no file keys). */
|
|
91
|
+
readonly env: NodeJS.ProcessEnv;
|
|
92
|
+
/**
|
|
93
|
+
* The resolved environment view (env + file keys layered in by
|
|
94
|
+
* `resolveEnvFromConfig`). When this differs from `env`, a file
|
|
95
|
+
* key is present.
|
|
96
|
+
*/
|
|
97
|
+
readonly resolvedEnv: NodeJS.ProcessEnv;
|
|
98
|
+
/** The loaded config (used to detect a file key directly). */
|
|
99
|
+
readonly config: ScoutlineConfig;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Classify the credential state across the whole registry.
|
|
103
|
+
*
|
|
104
|
+
* - **missing**: NO descriptor is configured through env OR file.
|
|
105
|
+
* The command cannot succeed (no fallback candidate exists), so
|
|
106
|
+
* the dispatcher refuses with `CONFIGURATION_ERROR` exit 3.
|
|
107
|
+
* - **env-only**: at least one descriptor is configured through env,
|
|
108
|
+
* and NO descriptor has a file key. The user is running purely on
|
|
109
|
+
* environment variables; the one-time hint points them at `init`.
|
|
110
|
+
* - **env-and-file**: env keys and file keys both exist. Env
|
|
111
|
+
* precedence means the env value wins at runtime for the providers
|
|
112
|
+
* that have both; the file keys cover the rest. No hint needed
|
|
113
|
+
* (the user has been through onboarding).
|
|
114
|
+
* - **file-configured**: keys exist ONLY in the file (the normal
|
|
115
|
+
* post-onboarding steady state). No hint needed.
|
|
116
|
+
*
|
|
117
|
+
* The "env-only" classification is the trigger for the one-time hint;
|
|
118
|
+
* "missing" is the trigger for remediation + exit 3. The other two
|
|
119
|
+
* states proceed normally.
|
|
120
|
+
*/
|
|
121
|
+
export declare function classifyCredentialState(input: CredentialClassificationInput): CredentialState;
|
|
122
|
+
/**
|
|
123
|
+
* Format the one-time env-only hint. Sent to stderr so stdout stays
|
|
124
|
+
* data-only. The hint names the wizard and points at it; it does NOT
|
|
125
|
+
* repeat on subsequent runs because the dispatcher persists
|
|
126
|
+
* `config.json.hintShown` after emitting it.
|
|
127
|
+
*/
|
|
128
|
+
export declare function formatEnvOnlyHint(): string;
|
|
129
|
+
/**
|
|
130
|
+
* Build the missing-credential error. Reuses the existing
|
|
131
|
+
* `CONFIGURATION_ERROR` / exit-3 contract so a refused operation is
|
|
132
|
+
* NEVER confused with success. The help text lists the canonical env
|
|
133
|
+
* vars across all providers so the user can pick one to set, and
|
|
134
|
+
* points at `init` as the interactive alternative.
|
|
135
|
+
*/
|
|
136
|
+
export declare function missingCredentialError(descriptors: readonly ProviderDescriptor[]): ConfigurationError;
|
|
137
|
+
/**
|
|
138
|
+
* Detect whether the command being dispatched should bypass trigger
|
|
139
|
+
* detection because it is rendering command-local help. Command help
|
|
140
|
+
* (`<command> --help` / `-h`) must remain usable even when config.json
|
|
141
|
+
* is corrupt or absent — rendering help never needs a credential.
|
|
142
|
+
*
|
|
143
|
+
* This is a shallow peek at the arg list: it only looks for the help
|
|
144
|
+
* flag, mirroring the per-handler short-circuit (`flags.help ||
|
|
145
|
+
* flags.h`). It does NOT consume the args; the handler still parses
|
|
146
|
+
* them itself.
|
|
147
|
+
*/
|
|
148
|
+
export declare function isCommandHelpInvocation(commandArgs: readonly string[]): boolean;
|
|
149
|
+
//# sourceMappingURL=trigger-detection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger-detection.d.ts","sourceRoot":"","sources":["../../src/lib/trigger-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAc,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAAgC,CAAC;AAExF;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,WAAW,CAAC,MAAM,CAA8B,CAAC;AAE1F;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAA8C,CAAC;AAEjG;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAC7B;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,GACpC;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;CAAE,GACjC;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAEjC;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC5C,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACpD,mEAAmE;IACnE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;IACxC,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,6BAA6B,GAAG,eAAe,CAqB7F;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAO1C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,SAAS,kBAAkB,EAAE,GACzC,kBAAkB,CAapB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAE/E"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trigger detection (T3b — Plan A, Option B).
|
|
3
|
+
*
|
|
4
|
+
* When the user runs a credentialed command without ever having gone
|
|
5
|
+
* through `scoutline init`, we want one of three classified outcomes
|
|
6
|
+
* rather than a confusing per-handler stack trace:
|
|
7
|
+
*
|
|
8
|
+
* - **env-only setup**: a key for the effective Provider is present
|
|
9
|
+
* in the injected env, but no Provider key is stored in
|
|
10
|
+
* `config.json`. We emit a ONE-TIME stderr hint pointing at
|
|
11
|
+
* `scoutline init` (so the user learns the wizard exists), persist
|
|
12
|
+
* `config.json.hintShown`, and then run the command normally. The
|
|
13
|
+
* command's natural output and exit code are preserved.
|
|
14
|
+
* - **missing credential everywhere**: the effective Provider has no
|
|
15
|
+
* key in env OR in `config.json`. We emit remediation and surface
|
|
16
|
+
* the existing `CONFIGURATION_ERROR` exit 3. A refused operation
|
|
17
|
+
* is NEVER exit 0 — scripting users must not treat refusal as
|
|
18
|
+
* success.
|
|
19
|
+
* - **credential-free commands** (`--help`, `--version`, `cache`,
|
|
20
|
+
* `init`, `<command> --help`): no config read at all. The T2a
|
|
21
|
+
* short-circuit already covers help/version/cache/init; this
|
|
22
|
+
* module exposes a helper to recognise per-command help so the
|
|
23
|
+
* trigger layer can route around it too (command help must remain
|
|
24
|
+
* usable under a corrupt config — see review item 9).
|
|
25
|
+
*
|
|
26
|
+
* Doctor and quota are observational: they report per-Provider state
|
|
27
|
+
* rather than depending on a single effective credential, so trigger
|
|
28
|
+
* detection does NOT classify them. They keep their existing per-handler
|
|
29
|
+
* configuration-error behavior (Doctor reports skipped/error states;
|
|
30
|
+
* quota surfaces its existing CONFIGURATION_ERROR when nothing is
|
|
31
|
+
* configured).
|
|
32
|
+
*
|
|
33
|
+
* Raw Z.AI commands (`tools`, `tool`, `call`, `code`) ARE credentialed
|
|
34
|
+
* and Z.AI-only, so missing-key remediation applies to them. They do
|
|
35
|
+
* NOT gain Provider selection or fallback (still Z.AI-only).
|
|
36
|
+
*
|
|
37
|
+
* All functions here are pure (they take their inputs as arguments and
|
|
38
|
+
* touch no module-level state). The dispatcher in `src/index.ts` wires
|
|
39
|
+
* the real `HintShownStore`, the injected env, the descriptors, and the
|
|
40
|
+
* stderr sink.
|
|
41
|
+
*/
|
|
42
|
+
import { ConfigurationError } from "./errors.js";
|
|
43
|
+
/**
|
|
44
|
+
* Commands that are observational rather than credentialed. They are
|
|
45
|
+
* exempt from trigger detection because they already report per-Provider
|
|
46
|
+
* state instead of depending on a single effective credential.
|
|
47
|
+
*/
|
|
48
|
+
export const OBSERVATIONAL_COMMANDS = new Set(["doctor", "quota"]);
|
|
49
|
+
/**
|
|
50
|
+
* Commands that are credential-free at the dispatcher level (handled
|
|
51
|
+
* before the credentialed config read). Listed here for documentation
|
|
52
|
+
* completeness; the dispatcher short-circuits them before reaching the
|
|
53
|
+
* trigger layer.
|
|
54
|
+
*/
|
|
55
|
+
export const DISPATCHER_CREDENTIAL_FREE = new Set(["cache", "init"]);
|
|
56
|
+
/**
|
|
57
|
+
* Commands that are always Z.AI-only (raw Z.AI tools + Code Mode).
|
|
58
|
+
* Trigger detection treats them as credentialed; their effective
|
|
59
|
+
* Provider is always `zai` regardless of `--provider`.
|
|
60
|
+
*/
|
|
61
|
+
export const ZAI_ONLY_COMMANDS = new Set(["tools", "tool", "call", "code"]);
|
|
62
|
+
/**
|
|
63
|
+
* Classify the credential state across the whole registry.
|
|
64
|
+
*
|
|
65
|
+
* - **missing**: NO descriptor is configured through env OR file.
|
|
66
|
+
* The command cannot succeed (no fallback candidate exists), so
|
|
67
|
+
* the dispatcher refuses with `CONFIGURATION_ERROR` exit 3.
|
|
68
|
+
* - **env-only**: at least one descriptor is configured through env,
|
|
69
|
+
* and NO descriptor has a file key. The user is running purely on
|
|
70
|
+
* environment variables; the one-time hint points them at `init`.
|
|
71
|
+
* - **env-and-file**: env keys and file keys both exist. Env
|
|
72
|
+
* precedence means the env value wins at runtime for the providers
|
|
73
|
+
* that have both; the file keys cover the rest. No hint needed
|
|
74
|
+
* (the user has been through onboarding).
|
|
75
|
+
* - **file-configured**: keys exist ONLY in the file (the normal
|
|
76
|
+
* post-onboarding steady state). No hint needed.
|
|
77
|
+
*
|
|
78
|
+
* The "env-only" classification is the trigger for the one-time hint;
|
|
79
|
+
* "missing" is the trigger for remediation + exit 3. The other two
|
|
80
|
+
* states proceed normally.
|
|
81
|
+
*/
|
|
82
|
+
export function classifyCredentialState(input) {
|
|
83
|
+
const { descriptors, env, resolvedEnv, config } = input;
|
|
84
|
+
const anyEnvConfigured = descriptors.some((d) => d.isConfigured(env));
|
|
85
|
+
const anyResolvedConfigured = descriptors.some((d) => d.isConfigured(resolvedEnv));
|
|
86
|
+
const anyFileConfigured = Object.values(config.providers).some((provider) => {
|
|
87
|
+
const key = provider?.apiKey;
|
|
88
|
+
return typeof key === "string" && key.trim().length > 0;
|
|
89
|
+
});
|
|
90
|
+
if (!anyResolvedConfigured) {
|
|
91
|
+
return { kind: "missing" };
|
|
92
|
+
}
|
|
93
|
+
if (anyEnvConfigured && !anyFileConfigured) {
|
|
94
|
+
return { kind: "env-only" };
|
|
95
|
+
}
|
|
96
|
+
if (anyEnvConfigured && anyFileConfigured) {
|
|
97
|
+
return { kind: "env-and-file" };
|
|
98
|
+
}
|
|
99
|
+
// !anyEnvConfigured && anyFileConfigured — the resolved env picked up
|
|
100
|
+
// the file key; this is the normal post-onboarding steady state.
|
|
101
|
+
return { kind: "file-configured" };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Format the one-time env-only hint. Sent to stderr so stdout stays
|
|
105
|
+
* data-only. The hint names the wizard and points at it; it does NOT
|
|
106
|
+
* repeat on subsequent runs because the dispatcher persists
|
|
107
|
+
* `config.json.hintShown` after emitting it.
|
|
108
|
+
*/
|
|
109
|
+
export function formatEnvOnlyHint() {
|
|
110
|
+
return ("scoutline: using credentials from the environment. " +
|
|
111
|
+
"Run `scoutline init` to record them in ~/.scoutline/config.json " +
|
|
112
|
+
"(enables fallback preferences, verification tracking, and re-config). " +
|
|
113
|
+
"This is a one-time hint; it will not repeat.\n");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Build the missing-credential error. Reuses the existing
|
|
117
|
+
* `CONFIGURATION_ERROR` / exit-3 contract so a refused operation is
|
|
118
|
+
* NEVER confused with success. The help text lists the canonical env
|
|
119
|
+
* vars across all providers so the user can pick one to set, and
|
|
120
|
+
* points at `init` as the interactive alternative.
|
|
121
|
+
*/
|
|
122
|
+
export function missingCredentialError(descriptors) {
|
|
123
|
+
const vars = [];
|
|
124
|
+
for (const descriptor of descriptors) {
|
|
125
|
+
if (descriptor.credentialEnvVars && descriptor.credentialEnvVars.length > 0) {
|
|
126
|
+
vars.push(descriptor.credentialEnvVars[0]);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const help = vars.length > 0
|
|
130
|
+
? `Set one of ${vars.join(", ")} (or run \`scoutline init\` to configure interactively).`
|
|
131
|
+
: "Run `scoutline init` to configure a Provider, or set the required API key in the environment.";
|
|
132
|
+
const message = "No Provider credential is configured (neither env nor ~/.scoutline/config.json)";
|
|
133
|
+
return new ConfigurationError(message, help);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Detect whether the command being dispatched should bypass trigger
|
|
137
|
+
* detection because it is rendering command-local help. Command help
|
|
138
|
+
* (`<command> --help` / `-h`) must remain usable even when config.json
|
|
139
|
+
* is corrupt or absent — rendering help never needs a credential.
|
|
140
|
+
*
|
|
141
|
+
* This is a shallow peek at the arg list: it only looks for the help
|
|
142
|
+
* flag, mirroring the per-handler short-circuit (`flags.help ||
|
|
143
|
+
* flags.h`). It does NOT consume the args; the handler still parses
|
|
144
|
+
* them itself.
|
|
145
|
+
*/
|
|
146
|
+
export function isCommandHelpInvocation(commandArgs) {
|
|
147
|
+
return commandArgs.includes("--help") || commandArgs.includes("-h");
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=trigger-detection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger-detection.js","sourceRoot":"","sources":["../../src/lib/trigger-detection.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/tty.d.ts
CHANGED
|
@@ -41,7 +41,14 @@ import type { QuotaDashboard } from "../capabilities/quota.js";
|
|
|
41
41
|
* Provider-neutral TTY rendering of a {@link QuotaDashboard}. Each
|
|
42
42
|
* Provider entry is labelled with its Provider id and each category by
|
|
43
43
|
* its normalized name; progress bars represent the REMAINING percentage.
|
|
44
|
+
*
|
|
45
|
+
* PB-T5: a successful row carries a `quotaSource` label rendered as a
|
|
46
|
+
* separate `source` line beneath the categories (snapshot/live, fresh/
|
|
47
|
+
* stale). The new `"none"` status (no quota Capability — Exa) renders
|
|
48
|
+
* a single dim line; it never appears as a failure. The `now` argument
|
|
49
|
+
* defaults to `Date.now()` so existing callers keep working; tests
|
|
50
|
+
* inject a fixed clock for deterministic age labels.
|
|
44
51
|
*/
|
|
45
|
-
export declare function formatQuotaDashboard(dashboard: QuotaDashboard): string;
|
|
52
|
+
export declare function formatQuotaDashboard(dashboard: QuotaDashboard, now?: number): string;
|
|
46
53
|
export {};
|
|
47
54
|
//# sourceMappingURL=tty.d.ts.map
|
package/dist/lib/tty.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tty.d.ts","sourceRoot":"","sources":["../../src/lib/tty.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,KAAK,OAAO,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AAUrC,eAAO,MAAM,KAAK;IAChB,IAAI;IACJ,GAAG;IACH,IAAI;IACJ,KAAK;IACL,MAAM;IACN,GAAG;IACH,OAAO;IACP,IAAI;CACL,CAAC;AAEF,wBAAgB,KAAK,IAAI,OAAO,CAE/B;AAED,uFAAuF;AACvF,wBAAgB,cAAc,CAC5B,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,IAAI,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAClD,KAAK,GAAG,SAAS,CAKnB;AAED,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAe7E;AAED,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"tty.d.ts","sourceRoot":"","sources":["../../src/lib/tty.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,KAAK,OAAO,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AAUrC,eAAO,MAAM,KAAK;IAChB,IAAI;IACJ,GAAG;IACH,IAAI;IACJ,KAAK;IACL,MAAM;IACN,GAAG;IACH,OAAO;IACP,IAAI;CACL,CAAC;AAEF,wBAAgB,KAAK,IAAI,OAAO,CAE/B;AAED,uFAAuF;AACvF,wBAAgB,cAAc,CAC5B,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,IAAI,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAClD,KAAK,GAAG,SAAS,CAKnB;AAED,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAe7E;AAED,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAyH/D;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,cAAc,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAyBhG"}
|
package/dist/lib/tty.js
CHANGED
|
@@ -96,12 +96,56 @@ function renderCategory(category, lines) {
|
|
|
96
96
|
lines.push(` ${color.gray("weekly")} ${remainingBar(w.remainingPercent)} ${w.remainingPercent}%`);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Format a relative-age label for an epoch-ms timestamp (PB-T5).
|
|
101
|
+
* Returns a short "Xm"/"Xh"/"Xd" string suitable for a TTY row. Used
|
|
102
|
+
* to surface `observedAt` age so a user can judge whether a selection
|
|
103
|
+
* pick was made against stale or fresh data. The `now` parameter is
|
|
104
|
+
* injected by the caller so a deterministic test clock produces a
|
|
105
|
+
* deterministic label.
|
|
106
|
+
*/
|
|
107
|
+
function formatObservedAge(observedAt, now) {
|
|
108
|
+
const diff = Math.max(0, now - observedAt);
|
|
109
|
+
const sec = Math.floor(diff / 1000);
|
|
110
|
+
if (sec < 60)
|
|
111
|
+
return `${sec}s`;
|
|
112
|
+
const min = Math.floor(sec / 60);
|
|
113
|
+
if (min < 60)
|
|
114
|
+
return `${min}m`;
|
|
115
|
+
const hr = Math.floor(min / 60);
|
|
116
|
+
if (hr < 24)
|
|
117
|
+
return `${hr}h`;
|
|
118
|
+
const day = Math.floor(hr / 24);
|
|
119
|
+
return `${day}d`;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Render the quotaSource label for a successful row (PB-T5). Mirrors
|
|
123
|
+
* the public {@link QuotaSourceLabel} shape: source/observedAt/
|
|
124
|
+
* authoritative. A non-authoritative row is dimmed so a user can see
|
|
125
|
+
* at a glance that the displayed numbers may be stale. The age label
|
|
126
|
+
* is computed against the injected clock so tests stay deterministic.
|
|
127
|
+
*/
|
|
128
|
+
function renderSourceLabel(source, lines, now) {
|
|
129
|
+
const age = formatObservedAge(source.observedAt, now);
|
|
130
|
+
const authority = source.authoritative
|
|
131
|
+
? color.green("fresh")
|
|
132
|
+
: color.yellow("stale · non-authoritative");
|
|
133
|
+
const sourceTxt = source.source === "snapshot" ? color.gray("snapshot") : color.cyan("live");
|
|
134
|
+
lines.push(` ${color.gray("source")} ${sourceTxt} · ${authority} ${color.gray(`(${age} ago`)}`);
|
|
135
|
+
}
|
|
99
136
|
/**
|
|
100
137
|
* Provider-neutral TTY rendering of a {@link QuotaDashboard}. Each
|
|
101
138
|
* Provider entry is labelled with its Provider id and each category by
|
|
102
139
|
* its normalized name; progress bars represent the REMAINING percentage.
|
|
140
|
+
*
|
|
141
|
+
* PB-T5: a successful row carries a `quotaSource` label rendered as a
|
|
142
|
+
* separate `source` line beneath the categories (snapshot/live, fresh/
|
|
143
|
+
* stale). The new `"none"` status (no quota Capability — Exa) renders
|
|
144
|
+
* a single dim line; it never appears as a failure. The `now` argument
|
|
145
|
+
* defaults to `Date.now()` so existing callers keep working; tests
|
|
146
|
+
* inject a fixed clock for deterministic age labels.
|
|
103
147
|
*/
|
|
104
|
-
export function formatQuotaDashboard(dashboard) {
|
|
148
|
+
export function formatQuotaDashboard(dashboard, now = Date.now()) {
|
|
105
149
|
const lines = [""];
|
|
106
150
|
for (const entry of dashboard.providers) {
|
|
107
151
|
if (entry.status === "ok") {
|
|
@@ -111,6 +155,14 @@ export function formatQuotaDashboard(dashboard) {
|
|
|
111
155
|
for (const category of success.categories) {
|
|
112
156
|
renderCategory(category, lines);
|
|
113
157
|
}
|
|
158
|
+
if (success.quotaSource) {
|
|
159
|
+
renderSourceLabel(success.quotaSource, lines, now);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else if (entry.status === "none") {
|
|
163
|
+
const none = entry;
|
|
164
|
+
lines.push(` ${color.bold(none.provider)} ${color.dim("(no quota)")}`);
|
|
165
|
+
lines.push(` ${color.gray("no quota capability — skipping probe")}`);
|
|
114
166
|
}
|
|
115
167
|
else {
|
|
116
168
|
const failure = entry;
|
package/dist/lib/tty.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tty.js","sourceRoot":"","sources":["../../src/lib/tty.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tty.js","sourceRoot":"","sources":["../../src/lib/tty.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,14 +18,34 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import type { ProviderDescriptor } from "../types.js";
|
|
20
20
|
import { type BraveTransportDeps } from "./client.js";
|
|
21
|
+
import type { BraveRateLimitHeaders } from "./client.js";
|
|
21
22
|
/**
|
|
22
23
|
* Dependencies the Brave Adapter accepts. The unified `transport`
|
|
23
24
|
* seam carries `fetch` and timer injection; the Search Capability
|
|
24
25
|
* threads it through to the direct-HTTP transport.
|
|
26
|
+
*
|
|
27
|
+
* `onRateLimitHarvest` (PB-T1) is the passive-harvest callback invoked
|
|
28
|
+
* with Brave's `X-RateLimit-*` headers after every successful web
|
|
29
|
+
* search. Production wires a default that normalizes the headers via
|
|
30
|
+
* `normalizeBraveQuota` and writes a raw-category snapshot to
|
|
31
|
+
* `~/.scoutline/state.json` through `writeQuotaSnapshot`; the write is
|
|
32
|
+
* awaited (so it survives the bin's immediate `process.exit`) and
|
|
33
|
+
* fail-open (a write error is isolated to a stderr warning and never
|
|
34
|
+
* converts the search success into a fallback). Tests inject a double
|
|
35
|
+
* to keep harvest assertions hermetic. `undefined` disables the
|
|
36
|
+
* harvest (used by tests that only exercise search-result
|
|
37
|
+
* normalization).
|
|
25
38
|
*/
|
|
26
39
|
export interface BraveAdapterDependencies {
|
|
27
40
|
/** Optional transport injection (fetch, timers, env). */
|
|
28
41
|
readonly transport?: BraveTransportDeps;
|
|
42
|
+
/**
|
|
43
|
+
* Optional passive-harvest callback fired with Brave's
|
|
44
|
+
* `X-RateLimit-*` headers after a successful web search. Production
|
|
45
|
+
* wires a default (see {@link createDefaultBraveHarvest}); tests
|
|
46
|
+
* inject a recorder or `undefined` to disable.
|
|
47
|
+
*/
|
|
48
|
+
readonly onRateLimitHarvest?: (headers: BraveRateLimitHeaders) => Promise<void> | void;
|
|
29
49
|
}
|
|
30
50
|
/**
|
|
31
51
|
* Build the Brave Provider Descriptor. The descriptor advertises the
|
|
@@ -37,6 +57,12 @@ export interface BraveAdapterDependencies {
|
|
|
37
57
|
* (typically a fake-fetch wrapper); production uses the no-argument
|
|
38
58
|
* factory which resolves to the global `fetch` and timers inside the
|
|
39
59
|
* transport Module.
|
|
60
|
+
*
|
|
61
|
+
* PB-T1: the no-argument factory wires the production passive-harvest
|
|
62
|
+
* callback ({@link createDefaultBraveHarvest}) so every successful web
|
|
63
|
+
* search writes a rate-limit snapshot to `state.json`. Tests inject
|
|
64
|
+
* `onRateLimitHarvest` (or `undefined` to disable) through
|
|
65
|
+
* `BraveAdapterDependencies`.
|
|
40
66
|
*/
|
|
41
67
|
export declare function createBraveDescriptor(dependencies?: BraveAdapterDependencies): ProviderDescriptor;
|
|
42
68
|
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/providers/brave/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAIV,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAqBrB,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/providers/brave/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAIV,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAqBrB,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGzD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,wBAAwB;IACvC,yDAAyD;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACxF;AA6iBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,CAAC,EAAE,wBAAwB,GAAG,kBAAkB,CA4BjG"}
|
|
@@ -22,7 +22,8 @@ import { applySearchTopic } from "../../lib/search-topic.js";
|
|
|
22
22
|
import { requireBraveApiKey, isBraveConfigured } from "./credentials.js";
|
|
23
23
|
import { fetchBraveSearch, fetchBraveNewsSearch, fetchBraveVideoSearch, fetchBraveLlmContext, } from "./client.js";
|
|
24
24
|
import { createBraveDiagnosticsCapability } from "./diagnostics.js";
|
|
25
|
-
import { createBraveQuotaCapability } from "./quota.js";
|
|
25
|
+
import { createBraveQuotaCapability, normalizeBraveQuota } from "./quota.js";
|
|
26
|
+
import { writeQuotaSnapshot } from "../../lib/quota-store.js";
|
|
26
27
|
// ---------------------------------------------------------------------------
|
|
27
28
|
// Provider-owned credential fingerprint
|
|
28
29
|
// ---------------------------------------------------------------------------
|
|
@@ -382,7 +383,7 @@ function normalizeBraveError(error) {
|
|
|
382
383
|
return new ApiError("Brave request failed", inferStatusCode(lower));
|
|
383
384
|
}
|
|
384
385
|
function createBraveSearchCapability(options) {
|
|
385
|
-
const { env, transport } = options;
|
|
386
|
+
const { env, transport, onRateLimitHarvest } = options;
|
|
386
387
|
const capability = {
|
|
387
388
|
validate(request) {
|
|
388
389
|
if (!request || typeof request.query !== "string" || request.query.trim() === "") {
|
|
@@ -450,7 +451,7 @@ function createBraveSearchCapability(options) {
|
|
|
450
451
|
? await fetchBraveLlmContext(apiKey, effectiveQuery, params, transport)
|
|
451
452
|
: isNews
|
|
452
453
|
? await fetchBraveNewsSearch(apiKey, effectiveQuery, params, transport)
|
|
453
|
-
: await fetchBraveSearch(apiKey, effectiveQuery, params, transport);
|
|
454
|
+
: await fetchBraveSearch(apiKey, effectiveQuery, params, transport, onRateLimitHarvest);
|
|
454
455
|
return isVideo
|
|
455
456
|
? normalizeBraveTopLevelResults(raw)
|
|
456
457
|
: isHigh
|
|
@@ -467,6 +468,54 @@ function createBraveSearchCapability(options) {
|
|
|
467
468
|
return capability;
|
|
468
469
|
}
|
|
469
470
|
// ---------------------------------------------------------------------------
|
|
471
|
+
// Passive harvest default (PB-T1)
|
|
472
|
+
// ---------------------------------------------------------------------------
|
|
473
|
+
/**
|
|
474
|
+
* Build the production passive-harvest callback for Brave rate-limit
|
|
475
|
+
* headers. Normalizes the four `X-RateLimit-*` headers via the SAME
|
|
476
|
+
* `normalizeBraveQuota` the explicit quota probe uses (so the
|
|
477
|
+
* harvested snapshot matches the probed snapshot's category shape
|
|
478
|
+
* exactly), then writes a raw-category snapshot to the quota store
|
|
479
|
+
* through {@link writeQuotaSnapshot}. The write advances `observedAt`
|
|
480
|
+
* only; `locallyUpdatedAt` is preserved by the store (PB-T2 advances
|
|
481
|
+
* it).
|
|
482
|
+
*
|
|
483
|
+
* Fail-open: any error (header parse failure, store write failure) is
|
|
484
|
+
* caught and routed to a stderr notice. The returned callback never
|
|
485
|
+
* rejects, so the transport's `try/catch` around the callback is
|
|
486
|
+
* belt-and-suspenders — a harvest failure cannot convert a search
|
|
487
|
+
* success into a fallback.
|
|
488
|
+
*
|
|
489
|
+
* `now` is injectable so tests can assert deterministic `observedAt` /
|
|
490
|
+
* `resetsAt` values without touching `Date.now`.
|
|
491
|
+
*/
|
|
492
|
+
function createDefaultBraveHarvest(now = Date.now) {
|
|
493
|
+
return async (headers) => {
|
|
494
|
+
// Skip silently when no rate-limit headers are present — Brave
|
|
495
|
+
// does not always attach them (e.g. metered plans, proxy
|
|
496
|
+
// gateways). An absent header set is the common case, not an
|
|
497
|
+
// error worth a stderr notice.
|
|
498
|
+
if (headers.policy === null &&
|
|
499
|
+
headers.limit === null &&
|
|
500
|
+
headers.remaining === null &&
|
|
501
|
+
headers.reset === null) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
const result = normalizeBraveQuota(headers, now);
|
|
506
|
+
const snapshot = {
|
|
507
|
+
observedAt: now(),
|
|
508
|
+
categories: result.categories,
|
|
509
|
+
};
|
|
510
|
+
await writeQuotaSnapshot("brave", snapshot);
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
514
|
+
process.stderr.write(`scoutline: brave quota harvest failed: ${message}\n`);
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
// ---------------------------------------------------------------------------
|
|
470
519
|
// Descriptor factory
|
|
471
520
|
// ---------------------------------------------------------------------------
|
|
472
521
|
/**
|
|
@@ -479,9 +528,16 @@ function createBraveSearchCapability(options) {
|
|
|
479
528
|
* (typically a fake-fetch wrapper); production uses the no-argument
|
|
480
529
|
* factory which resolves to the global `fetch` and timers inside the
|
|
481
530
|
* transport Module.
|
|
531
|
+
*
|
|
532
|
+
* PB-T1: the no-argument factory wires the production passive-harvest
|
|
533
|
+
* callback ({@link createDefaultBraveHarvest}) so every successful web
|
|
534
|
+
* search writes a rate-limit snapshot to `state.json`. Tests inject
|
|
535
|
+
* `onRateLimitHarvest` (or `undefined` to disable) through
|
|
536
|
+
* `BraveAdapterDependencies`.
|
|
482
537
|
*/
|
|
483
538
|
export function createBraveDescriptor(dependencies) {
|
|
484
539
|
const transport = dependencies?.transport;
|
|
540
|
+
const onRateLimitHarvest = dependencies?.onRateLimitHarvest ?? createDefaultBraveHarvest();
|
|
485
541
|
return {
|
|
486
542
|
id: "brave",
|
|
487
543
|
isConfigured(env) {
|
|
@@ -494,11 +550,17 @@ export function createBraveDescriptor(dependencies) {
|
|
|
494
550
|
return new Set(["search", "diagnostics", "quota"]);
|
|
495
551
|
},
|
|
496
552
|
create(context) {
|
|
497
|
-
const search = createBraveSearchCapability({
|
|
553
|
+
const search = createBraveSearchCapability({
|
|
554
|
+
env: context.env,
|
|
555
|
+
transport,
|
|
556
|
+
onRateLimitHarvest,
|
|
557
|
+
});
|
|
498
558
|
const diagnostics = createBraveDiagnosticsCapability({ env: context.env, transport });
|
|
499
559
|
const quota = createBraveQuotaCapability({ env: context.env, transport });
|
|
500
560
|
return { id: "brave", search, diagnostics, quota };
|
|
501
561
|
},
|
|
562
|
+
// Provider-fallback Ticket 02 — see ProviderDescriptor.credentialEnvVars.
|
|
563
|
+
credentialEnvVars: ["BRAVE_SEARCH_API_KEY"],
|
|
502
564
|
};
|
|
503
565
|
}
|
|
504
566
|
//# sourceMappingURL=adapter.js.map
|