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.
Files changed (77) hide show
  1. package/README.md +261 -78
  2. package/dist/cli/daemon.d.ts +38 -6
  3. package/dist/cli/daemon.js +524 -109
  4. package/dist/cli/daemon.js.map +1 -1
  5. package/dist/cli/dsh-config.js +34 -9
  6. package/dist/cli/dsh-config.js.map +1 -1
  7. package/dist/cli/help.js +3 -0
  8. package/dist/cli/help.js.map +1 -1
  9. package/dist/cli/key-state.d.ts +21 -0
  10. package/dist/cli/key-state.js +57 -0
  11. package/dist/cli/key-state.js.map +1 -0
  12. package/dist/cli/keys.d.ts +28 -0
  13. package/dist/cli/keys.js +185 -0
  14. package/dist/cli/keys.js.map +1 -0
  15. package/dist/cli/overview.js +6 -1
  16. package/dist/cli/overview.js.map +1 -1
  17. package/dist/cli/secret.d.ts +31 -0
  18. package/dist/cli/secret.js +89 -0
  19. package/dist/cli/secret.js.map +1 -0
  20. package/dist/cli/usage.js +20 -10
  21. package/dist/cli/usage.js.map +1 -1
  22. package/dist/config.d.ts +15 -1
  23. package/dist/config.js +98 -25
  24. package/dist/config.js.map +1 -1
  25. package/dist/constants.d.ts +2 -0
  26. package/dist/constants.js +2 -0
  27. package/dist/constants.js.map +1 -1
  28. package/dist/debug-buffer.d.ts +58 -0
  29. package/dist/debug-buffer.js +42 -0
  30. package/dist/debug-buffer.js.map +1 -0
  31. package/dist/index.js +30 -4
  32. package/dist/index.js.map +1 -1
  33. package/dist/log-redact.d.ts +18 -0
  34. package/dist/log-redact.js +41 -0
  35. package/dist/log-redact.js.map +1 -0
  36. package/dist/logger.d.ts +6 -0
  37. package/dist/logger.js +30 -0
  38. package/dist/logger.js.map +1 -1
  39. package/dist/models-fetcher.js +2 -0
  40. package/dist/models-fetcher.js.map +1 -1
  41. package/dist/providers/commandcode.d.ts +20 -5
  42. package/dist/providers/commandcode.js +115 -7
  43. package/dist/providers/commandcode.js.map +1 -1
  44. package/dist/providers/index.d.ts +2 -2
  45. package/dist/providers/index.js +2 -2
  46. package/dist/providers/index.js.map +1 -1
  47. package/dist/providers/infer.d.ts +11 -0
  48. package/dist/providers/infer.js.map +1 -1
  49. package/dist/providers/opencode-go.d.ts +8 -0
  50. package/dist/providers/opencode-go.js +35 -7
  51. package/dist/providers/opencode-go.js.map +1 -1
  52. package/dist/providers/types.d.ts +10 -2
  53. package/dist/proxy-logic.d.ts +101 -7
  54. package/dist/proxy-logic.js +197 -41
  55. package/dist/proxy-logic.js.map +1 -1
  56. package/dist/proxy.d.ts +33 -1
  57. package/dist/proxy.js +369 -67
  58. package/dist/proxy.js.map +1 -1
  59. package/dist/secrets/boot.d.ts +33 -0
  60. package/dist/secrets/boot.js +65 -0
  61. package/dist/secrets/boot.js.map +1 -0
  62. package/dist/secrets/envelope.d.ts +44 -0
  63. package/dist/secrets/envelope.js +132 -0
  64. package/dist/secrets/envelope.js.map +1 -0
  65. package/dist/secrets/source.d.ts +130 -0
  66. package/dist/secrets/source.js +332 -0
  67. package/dist/secrets/source.js.map +1 -0
  68. package/dist/token-usage.d.ts +9 -2
  69. package/dist/token-usage.js +20 -4
  70. package/dist/token-usage.js.map +1 -1
  71. package/dist/usage-client.d.ts +43 -13
  72. package/dist/usage-client.js +37 -54
  73. package/dist/usage-client.js.map +1 -1
  74. package/dist/validation.d.ts +3 -3
  75. package/dist/validation.js +3 -3
  76. package/dist/validation.js.map +1 -1
  77. 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 0;
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: rolling,
73
- weeklyPercent: weekly,
74
- monthlyPercent: monthly,
75
- usedPercent: Math.max(rolling, weekly, monthly),
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":"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"}
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
- /** Shared usage shape used across providers (mirrors usage-client KeyUsage). */
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
- usedPercent: number;
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;
@@ -18,8 +18,14 @@ interface ApiKey {
18
18
  consecutiveFailures: number;
19
19
  lastUsed: number | null;
20
20
  disabledAt: number | null;
21
- /** Worst-window used percent (0-100) reported by the upstream usage API. */
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 (worst-window used percent, 0-100).
90
- * Keys absent from the map keep their previous value.
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, number>): void;
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
  *