saros-proxy 0.10.2 → 1.0.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 +261 -78
- package/dist/cli/daemon.d.ts +38 -6
- package/dist/cli/daemon.js +524 -109
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/dsh-config.js +34 -9
- package/dist/cli/dsh-config.js.map +1 -1
- package/dist/cli/help.js +3 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/key-state.d.ts +21 -0
- package/dist/cli/key-state.js +57 -0
- package/dist/cli/key-state.js.map +1 -0
- package/dist/cli/keys.d.ts +28 -0
- package/dist/cli/keys.js +185 -0
- package/dist/cli/keys.js.map +1 -0
- package/dist/cli/overview.js +6 -1
- package/dist/cli/overview.js.map +1 -1
- package/dist/cli/secret.d.ts +31 -0
- package/dist/cli/secret.js +89 -0
- package/dist/cli/secret.js.map +1 -0
- package/dist/cli/usage.js +20 -10
- package/dist/cli/usage.js.map +1 -1
- package/dist/config.d.ts +15 -1
- package/dist/config.js +98 -25
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/debug-buffer.d.ts +58 -0
- package/dist/debug-buffer.js +42 -0
- package/dist/debug-buffer.js.map +1 -0
- package/dist/index.js +30 -4
- package/dist/index.js.map +1 -1
- package/dist/log-redact.d.ts +18 -0
- package/dist/log-redact.js +41 -0
- package/dist/log-redact.js.map +1 -0
- package/dist/logger.d.ts +6 -0
- package/dist/logger.js +30 -0
- package/dist/logger.js.map +1 -1
- package/dist/models-fetcher.js +2 -0
- package/dist/models-fetcher.js.map +1 -1
- package/dist/providers/commandcode.d.ts +20 -5
- package/dist/providers/commandcode.js +115 -7
- package/dist/providers/commandcode.js.map +1 -1
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js +2 -2
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/infer.d.ts +11 -0
- package/dist/providers/infer.js.map +1 -1
- package/dist/providers/opencode-go.d.ts +8 -0
- package/dist/providers/opencode-go.js +35 -7
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/providers/types.d.ts +10 -2
- package/dist/proxy-logic.d.ts +101 -7
- package/dist/proxy-logic.js +197 -41
- package/dist/proxy-logic.js.map +1 -1
- package/dist/proxy.d.ts +33 -1
- package/dist/proxy.js +369 -67
- package/dist/proxy.js.map +1 -1
- package/dist/secrets/boot.d.ts +33 -0
- package/dist/secrets/boot.js +65 -0
- package/dist/secrets/boot.js.map +1 -0
- package/dist/secrets/envelope.d.ts +44 -0
- package/dist/secrets/envelope.js +132 -0
- package/dist/secrets/envelope.js.map +1 -0
- package/dist/secrets/source.d.ts +130 -0
- package/dist/secrets/source.js +332 -0
- package/dist/secrets/source.js.map +1 -0
- package/dist/token-usage.d.ts +9 -2
- package/dist/token-usage.js +20 -4
- package/dist/token-usage.js.map +1 -1
- package/dist/usage-client.d.ts +43 -13
- package/dist/usage-client.js +37 -54
- package/dist/usage-client.js.map +1 -1
- package/dist/validation.d.ts +3 -3
- package/dist/validation.js +3 -3
- package/dist/validation.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
* Usage: GET /zen/go/v1/usage with Bearer = the key itself.
|
|
6
6
|
*/
|
|
7
7
|
import { API_KEY_PREFIX, MIN_KEY_LENGTH, OPENCODE_GO_KEY_PREFIX, USAGE_PATH, } from '../constants.js';
|
|
8
|
+
/**
|
|
9
|
+
* Identity the zen upstream expects. Cloudflare in front of opencode.ai
|
|
10
|
+
* rejects browser-like signatures with `403 error code: 1010` (verified live
|
|
11
|
+
* 2026-11-05: no UA, `Python-urllib/3.14` and `node` all 403; `cli` reaches
|
|
12
|
+
* the API). Forwarding the client's UA therefore killed every request made
|
|
13
|
+
* with an opencode-go key, so the provider overrides it.
|
|
14
|
+
*/
|
|
15
|
+
export const OPENCODE_GO_USER_AGENT = 'cli';
|
|
8
16
|
/** Bare-name index of the live opencode-go catalog. */
|
|
9
17
|
let catalogCache = null;
|
|
10
18
|
/** Ensures the background refresh fires at most once per process. */
|
|
@@ -18,6 +26,7 @@ async function refreshCatalog(baseUrl, timeoutMs) {
|
|
|
18
26
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
19
27
|
try {
|
|
20
28
|
const res = await fetch(`${baseUrl.replace(/\/$/, '')}${USAGE_PATH.replace(/\/usage$/, '/models')}`, {
|
|
29
|
+
headers: { 'User-Agent': OPENCODE_GO_USER_AGENT },
|
|
21
30
|
signal: controller.signal,
|
|
22
31
|
});
|
|
23
32
|
if (!res.ok)
|
|
@@ -47,10 +56,11 @@ async function refreshCatalog(baseUrl, timeoutMs) {
|
|
|
47
56
|
clearTimeout(timer);
|
|
48
57
|
}
|
|
49
58
|
}
|
|
59
|
+
/** Percent from one window; null when the window carries no numeric percent. */
|
|
50
60
|
function parsePercent(window) {
|
|
51
61
|
const percent = window?.percent;
|
|
52
62
|
if (typeof percent !== 'number' || !Number.isFinite(percent))
|
|
53
|
-
return
|
|
63
|
+
return null;
|
|
54
64
|
return Math.min(100, Math.max(0, percent));
|
|
55
65
|
}
|
|
56
66
|
/** Parse opencode-go usage JSON (moved from usage-client.ts). */
|
|
@@ -68,11 +78,23 @@ export function parseOpencodeUsage(body) {
|
|
|
68
78
|
const rolling = parsePercent(usage.rolling);
|
|
69
79
|
const weekly = parsePercent(usage.weekly);
|
|
70
80
|
const monthly = parsePercent(usage.monthly);
|
|
81
|
+
// A 200 whose `usage` object has no numeric window is not data: returning
|
|
82
|
+
// zeros would overwrite the previous percentage and hand the key max weight.
|
|
83
|
+
if (rolling === null && weekly === null && monthly === null)
|
|
84
|
+
return null;
|
|
85
|
+
const rollingPct = rolling ?? 0;
|
|
86
|
+
const weeklyPct = weekly ?? 0;
|
|
87
|
+
const monthlyPct = monthly ?? 0;
|
|
71
88
|
return {
|
|
72
|
-
rollingPercent:
|
|
73
|
-
weeklyPercent:
|
|
74
|
-
monthlyPercent:
|
|
75
|
-
|
|
89
|
+
rollingPercent: rollingPct,
|
|
90
|
+
weeklyPercent: weeklyPct,
|
|
91
|
+
monthlyPercent: monthlyPct,
|
|
92
|
+
// The monthly window is the budget measure; any window at 100% pulls the
|
|
93
|
+
// key out of rotation before the upstream rejects it. A missing monthly
|
|
94
|
+
// stays null so the previous percentage is not overwritten with 0%.
|
|
95
|
+
usedPercent: monthly,
|
|
96
|
+
exhausted: rollingPct >= 100 || weeklyPct >= 100 || monthlyPct >= 100,
|
|
97
|
+
monthlyAvailable: monthly !== null,
|
|
76
98
|
rollingResetsAt: typeof usage.rolling?.resetsAt === 'string' ? usage.rolling.resetsAt : undefined,
|
|
77
99
|
weeklyResetsAt: typeof usage.weekly?.resetsAt === 'string' ? usage.weekly.resetsAt : undefined,
|
|
78
100
|
monthlyResetsAt: typeof usage.monthly?.resetsAt === 'string' ? usage.monthly.resetsAt : undefined,
|
|
@@ -97,7 +119,7 @@ export const opencodeGoProvider = {
|
|
|
97
119
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
98
120
|
try {
|
|
99
121
|
const res = await fetch(`${this.baseUrl}${USAGE_PATH}`, {
|
|
100
|
-
headers: { Authorization: `Bearer ${key}
|
|
122
|
+
headers: { Authorization: `Bearer ${key}`, 'User-Agent': OPENCODE_GO_USER_AGENT },
|
|
101
123
|
signal: controller.signal,
|
|
102
124
|
});
|
|
103
125
|
if (res.ok) {
|
|
@@ -126,6 +148,10 @@ export const opencodeGoProvider = {
|
|
|
126
148
|
usagePath() {
|
|
127
149
|
return USAGE_PATH;
|
|
128
150
|
},
|
|
151
|
+
/** Cloudflare rejects forwarded browser UAs; always identify as the CLI. */
|
|
152
|
+
extraUpstreamHeaders() {
|
|
153
|
+
return { 'User-Agent': OPENCODE_GO_USER_AGENT };
|
|
154
|
+
},
|
|
129
155
|
parseUsageResponse(body) {
|
|
130
156
|
return parseOpencodeUsage(body);
|
|
131
157
|
},
|
|
@@ -174,7 +200,9 @@ export const opencodeGoProvider = {
|
|
|
174
200
|
return null;
|
|
175
201
|
// Re-fetch and return the raw list for catalog consumers (harness sync).
|
|
176
202
|
try {
|
|
177
|
-
const res = await fetch(`${this.baseUrl.replace(/\/$/, '')}${USAGE_PATH.replace(/\/usage$/, '/models')}
|
|
203
|
+
const res = await fetch(`${this.baseUrl.replace(/\/$/, '')}${USAGE_PATH.replace(/\/usage$/, '/models')}`, {
|
|
204
|
+
headers: { 'User-Agent': OPENCODE_GO_USER_AGENT },
|
|
205
|
+
});
|
|
178
206
|
if (!res.ok)
|
|
179
207
|
return null;
|
|
180
208
|
const body = (await res.json());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opencode-go.js","sourceRoot":"","sources":["../../src/providers/opencode-go.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,UAAU,GACX,MAAM,iBAAiB,CAAC;AAGzB,uDAAuD;AACvD,IAAI,YAAY,GAAsC,IAAI,CAAC;AAC3D,qEAAqE;AACrE,IAAI,cAAc,GAAG,KAAK,CAAC;AAE3B;;;GAGG;AACH,KAAK,UAAU,cAAc,CAAC,OAAe,EAAE,SAAiB;IAC9D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,EAAE;YACnG,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAC;QACtD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QAC5C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QACpC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,SAAS;YAClD,MAAM,EAAE,GAAI,KAAiC,CAAC,EAAE,CAAC;YACjD,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,EAAE;gBAAE,SAAS;YAC5C,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;YAChC,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;YACvC,IAAI,IAAI;gBAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,YAAY,GAAG,EAAE,SAAS,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAYD,SAAS,YAAY,CAAC,MAA+B;IACnD,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,CAAC;IAChC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"opencode-go.js","sourceRoot":"","sources":["../../src/providers/opencode-go.ts"],"names":[],"mappings":"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"}
|
|
@@ -123,9 +123,17 @@ export interface KeyProvider {
|
|
|
123
123
|
* Used for request routing in mixed-provider key pools.
|
|
124
124
|
*/
|
|
125
125
|
export type ModelAffinity = 'yes' | 'no' | 'maybe';
|
|
126
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* Shared usage shape used across providers (mirrors usage-client KeyUsage).
|
|
128
|
+
* `usedPercent` is the monthly percentage of the key's own provider budget;
|
|
129
|
+
* `exhausted` means any window is at 100% and the key must not serve traffic.
|
|
130
|
+
*/
|
|
127
131
|
export interface KeyUsage {
|
|
128
|
-
|
|
132
|
+
/** Monthly used percent, or null when it cannot be derived. */
|
|
133
|
+
usedPercent: number | null;
|
|
134
|
+
exhausted: boolean;
|
|
135
|
+
/** False when the payload carries no monthly percentage (CLI shows n/a). */
|
|
136
|
+
monthlyAvailable: boolean;
|
|
129
137
|
rollingPercent: number;
|
|
130
138
|
weeklyPercent: number;
|
|
131
139
|
monthlyPercent: number;
|
package/dist/proxy-logic.d.ts
CHANGED
|
@@ -18,8 +18,14 @@ interface ApiKey {
|
|
|
18
18
|
consecutiveFailures: number;
|
|
19
19
|
lastUsed: number | null;
|
|
20
20
|
disabledAt: number | null;
|
|
21
|
-
/**
|
|
21
|
+
/** Monthly used percent (0-100) reported by the upstream usage API. */
|
|
22
22
|
usagePercent: number | null;
|
|
23
|
+
/**
|
|
24
|
+
* True when any quota window (5h, weekly or monthly) is at 100%. Weight 0
|
|
25
|
+
* in rotation: a month with room does not help when the upstream already
|
|
26
|
+
* rejects the key for a spent shorter window.
|
|
27
|
+
*/
|
|
28
|
+
exhausted: boolean;
|
|
23
29
|
/**
|
|
24
30
|
* False when the upstream rejected this key for lack of plan
|
|
25
31
|
* (403 EntitlementError / upgrade_required): the account cannot use the
|
|
@@ -30,6 +36,58 @@ interface ApiKey {
|
|
|
30
36
|
entitled: boolean;
|
|
31
37
|
/** Smooth weighted round-robin state (nginx-style). */
|
|
32
38
|
currentWeight: number;
|
|
39
|
+
/** Why the key last left rotation — reported by /health and the 503 body. */
|
|
40
|
+
lastError: KeyFailure | null;
|
|
41
|
+
/**
|
|
42
|
+
* True when the key was retired in config (`enabled: false`). Separate from
|
|
43
|
+
* `enabled` so a success (or a cooldown) can never put it back in rotation.
|
|
44
|
+
*/
|
|
45
|
+
disabledByConfig: boolean;
|
|
46
|
+
/** True when the credential arrived as an `enc:` envelope in config. */
|
|
47
|
+
encrypted: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** Why a key is out of rotation, derived from the upstream verdict. */
|
|
50
|
+
export type KeyFailureReason = 'auth' | 'quota' | 'rate_limit' | 'server';
|
|
51
|
+
export interface KeyFailure {
|
|
52
|
+
/** Upstream HTTP status, when the failure came from a response. */
|
|
53
|
+
status: number | null;
|
|
54
|
+
reason: KeyFailureReason;
|
|
55
|
+
/** Epoch ms. */
|
|
56
|
+
at: number;
|
|
57
|
+
/** Short upstream message (≤200 chars). Never contains the credential. */
|
|
58
|
+
detail: string | null;
|
|
59
|
+
}
|
|
60
|
+
/** Upstream context for a failure, passed by the caller that saw it. */
|
|
61
|
+
export interface KeyFailureInfo {
|
|
62
|
+
status?: number;
|
|
63
|
+
body?: string;
|
|
64
|
+
}
|
|
65
|
+
/** Public projection of one key's state — never contains the credential. */
|
|
66
|
+
export interface KeyHealthReport {
|
|
67
|
+
label: string;
|
|
68
|
+
provider: string;
|
|
69
|
+
enabled: boolean;
|
|
70
|
+
entitled: boolean;
|
|
71
|
+
/** True when the key was retired in config (`enabled: false`). */
|
|
72
|
+
disabledByConfig: boolean;
|
|
73
|
+
/** True when the credential is stored as an `enc:` envelope, never its value. */
|
|
74
|
+
encrypted: boolean;
|
|
75
|
+
consecutiveFailures: number;
|
|
76
|
+
disabledAt: string | null;
|
|
77
|
+
retryAfterMs: number | null;
|
|
78
|
+
lastError: {
|
|
79
|
+
status: number | null;
|
|
80
|
+
reason: KeyFailureReason;
|
|
81
|
+
at: string;
|
|
82
|
+
} | null;
|
|
83
|
+
/** Monthly used percent (0-100) of this key's provider budget. */
|
|
84
|
+
usagePercent: number | null;
|
|
85
|
+
}
|
|
86
|
+
/** Fields needed to answer "can this key be used right now?". */
|
|
87
|
+
export interface KeyAvailability {
|
|
88
|
+
enabled: boolean;
|
|
89
|
+
consecutiveFailures: number;
|
|
90
|
+
disabledAt: number | null;
|
|
33
91
|
}
|
|
34
92
|
/** Read-only projection returned to callers — cannot mutate internal state. */
|
|
35
93
|
export interface KeySnapshot {
|
|
@@ -81,15 +139,33 @@ export declare function createProxyState(keys: Array<{
|
|
|
81
139
|
label: string;
|
|
82
140
|
key: string;
|
|
83
141
|
provider?: string;
|
|
142
|
+
enabled?: boolean;
|
|
143
|
+
encrypted?: boolean;
|
|
84
144
|
}>, options?: {
|
|
85
145
|
circuitBreakerThreshold?: number;
|
|
86
146
|
circuitBreakerCooldownMs?: number;
|
|
87
147
|
}): ProxyState;
|
|
148
|
+
/** Usage snapshot applied to one key. */
|
|
149
|
+
export interface KeyUsageMetrics {
|
|
150
|
+
/**
|
|
151
|
+
* Monthly used percent, 0-100, of the key's own provider budget; null when
|
|
152
|
+
* the payload had no derivable monthly value and the previous one must stay.
|
|
153
|
+
*/
|
|
154
|
+
usedPercent: number | null;
|
|
155
|
+
/** True when any quota window is at 100%. */
|
|
156
|
+
exhausted: boolean;
|
|
157
|
+
}
|
|
88
158
|
/**
|
|
89
|
-
* Update per-key usage data
|
|
90
|
-
*
|
|
159
|
+
* Update per-key usage data. `usedPercent` is the **monthly** percentage of
|
|
160
|
+
* the key's **own provider budget**, not absolute capacity: a mixed pool
|
|
161
|
+
* feeds every provider's number into `pickWeighted`, where only this
|
|
162
|
+
* normalized percent is comparable (opencode-go publishes percentages alone —
|
|
163
|
+
* no counts or caps). `exhausted` means a shorter window already hit its cap,
|
|
164
|
+
* so the key weighs 0 even when the month has room. A null `usedPercent` means
|
|
165
|
+
* the payload had no derivable monthly value and the previous percentage is
|
|
166
|
+
* kept. Keys absent from the map keep their previous values.
|
|
91
167
|
*/
|
|
92
|
-
export declare function updateKeyUsage(state: ProxyState, usage: Map<string,
|
|
168
|
+
export declare function updateKeyUsage(state: ProxyState, usage: Map<string, KeyUsageMetrics>): void;
|
|
93
169
|
/**
|
|
94
170
|
* Mark a key as (not) entitled to use its provider. Unentitled keys are
|
|
95
171
|
* skipped by selection without touching the circuit breaker; the usage
|
|
@@ -121,16 +197,34 @@ export declare function failoverRequest(state: ProxyState, requestId: string): K
|
|
|
121
197
|
*/
|
|
122
198
|
export declare function completeRequest(state: ProxyState, requestId: string, success: boolean): void;
|
|
123
199
|
/**
|
|
124
|
-
* Mark a key as failed according to the error type (C2).
|
|
200
|
+
* Mark a key as failed according to the error type (C2) and record why.
|
|
125
201
|
* - KeyFault / ServerFault: penalise the key (KeyFault disables immediately)
|
|
126
202
|
* - RequestFault / NetworkFault: no penalisation (bad request, or network
|
|
127
203
|
* trouble that is not the key's fault)
|
|
128
204
|
*/
|
|
129
|
-
export declare function markKeyFailed(state: ProxyState, keyLabel: string, errorType: ErrorType): void;
|
|
205
|
+
export declare function markKeyFailed(state: ProxyState, keyLabel: string, errorType: ErrorType, info?: KeyFailureInfo): void;
|
|
130
206
|
/**
|
|
131
|
-
* Mark a key as succeeded — resets consecutive failures
|
|
207
|
+
* Mark a key as succeeded — resets consecutive failures and clears any
|
|
208
|
+
* cooldown. `enabled` must come back too: a success can arrive after the key
|
|
209
|
+
* was disabled (tier 2 shares a key between concurrent requests, and a stream
|
|
210
|
+
* that fails after completeRequest(…, true) marks failure late). Leaving
|
|
211
|
+
* `enabled=false` with `disabledAt=null` made the key unselectable forever,
|
|
212
|
+
* because isKeyAvailable only revives keys that still carry a disabledAt.
|
|
132
213
|
*/
|
|
133
214
|
export declare function markKeySucceeded(state: ProxyState, keyLabel: string): void;
|
|
215
|
+
/**
|
|
216
|
+
* Milliseconds until the key becomes selectable again, or null when nothing
|
|
217
|
+
* will bring it back (healthy, retired in config, or no cooldown recorded).
|
|
218
|
+
* A key past its cooldown but not yet lazily revived reports 0 — it is
|
|
219
|
+
* selectable on the next request, which is not "already selectable" here.
|
|
220
|
+
* Pure: unlike isKeyAvailable this never revives the key.
|
|
221
|
+
*/
|
|
222
|
+
export declare function keyRetryAfterMs(state: Pick<ProxyState, 'circuitBreakerThreshold' | 'circuitBreakerCooldownMs'>, key: KeyAvailability, now?: number): number | null;
|
|
223
|
+
/**
|
|
224
|
+
* Per-key state for /health. Labels are already public (X-Proxy-Key-Label);
|
|
225
|
+
* the credential and the upstream message are not part of this projection.
|
|
226
|
+
*/
|
|
227
|
+
export declare function describeKeys(state: ProxyState, now?: number): KeyHealthReport[];
|
|
134
228
|
/**
|
|
135
229
|
* Classify an HTTP error status + optional response body into an ErrorType.
|
|
136
230
|
*
|