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
@@ -0,0 +1,332 @@
1
+ /**
2
+ * source.ts — Where the passphrase that unlocks `enc:v1:` envelopes comes from.
3
+ *
4
+ * Resolution order (first match wins):
5
+ * 1. `SAROS_PASSPHRASE` in the environment — systemd / container friendly.
6
+ * 2. The OS secret store, always through the platform's own CLI:
7
+ * Linux secret-tool lookup service saros-proxy account <user>
8
+ * macOS security find-generic-password -s saros-proxy -a <user> -w
9
+ * Windows PowerShell DPAPI, blob in %APPDATA%\saros\secret.dpapi
10
+ * 3. A TTY prompt — only foreground `serve` asks; never a daemon child.
11
+ *
12
+ * Every source returns `{ value } | { reason }`. Reasons never contain the
13
+ * secret, so they are safe for `secret status`, logs and error messages.
14
+ */
15
+ import { spawnSync } from 'node:child_process';
16
+ import { existsSync, mkdirSync, readFileSync } from 'node:fs';
17
+ import { homedir } from 'node:os';
18
+ import { dirname, join } from 'node:path';
19
+ import { createInterface } from 'node:readline';
20
+ import { Writable } from 'node:stream';
21
+ export const SERVICE_NAME = 'saros-proxy';
22
+ export const ENV_PASSPHRASE = 'SAROS_PASSPHRASE';
23
+ export const NO_SOURCE_MESSAGE = [
24
+ 'Encrypted API keys found, but no passphrase source is available.',
25
+ ' a. Set SAROS_PASSPHRASE in the environment (systemd: use systemd-creds).',
26
+ ' b. Store the passphrase in the OS secret store: saros-proxy secret set',
27
+ ' c. Run `saros-proxy serve` in a terminal to be prompted.',
28
+ ].join('\n');
29
+ /**
30
+ * Raised when encrypted keys cannot be unlocked. The message is safe to log:
31
+ * it names sources and options, never a passphrase, plaintext or ciphertext.
32
+ */
33
+ export class SecretSourceError extends Error {
34
+ constructor(message) {
35
+ super(message);
36
+ this.name = 'SecretSourceError';
37
+ }
38
+ }
39
+ export function decryptFailureMessage(label, detail) {
40
+ return (`Cannot decrypt API key "${label}": ${detail}. ` +
41
+ 'Check SAROS_PASSPHRASE or run `saros-proxy secret status`.');
42
+ }
43
+ /**
44
+ * Run a platform CLI without a shell. Failures carry only a generic reason
45
+ * plus exit status and (bounded) stderr for the store classifier — callers
46
+ * never print stderr, and stdout is dropped.
47
+ */
48
+ export function runCommand(command, args, options = {}) {
49
+ const result = spawnSync(command, args, {
50
+ encoding: 'utf8',
51
+ input: options.input,
52
+ env: options.env ? { ...process.env, ...options.env } : process.env,
53
+ timeout: 15_000,
54
+ windowsHide: true,
55
+ maxBuffer: 1024 * 1024,
56
+ });
57
+ if (result.error) {
58
+ const code = result.error.code;
59
+ return { ok: false, stdout: '', reason: code === 'ENOENT' ? `${command} is not installed` : `${command} could not be run` };
60
+ }
61
+ if (result.status !== 0) {
62
+ const detail = result.status === null ? 'was terminated' : `exited with code ${result.status}`;
63
+ return {
64
+ ok: false,
65
+ stdout: '',
66
+ reason: `${command} ${detail}`,
67
+ status: result.status,
68
+ // Diagnostics for the store classifier only; never printed verbatim.
69
+ stderr: (result.stderr || '').slice(0, 4096),
70
+ };
71
+ }
72
+ return { ok: true, stdout: result.stdout || '' };
73
+ }
74
+ /** Strip the trailing newline a CLI adds; keep spaces inside the secret. */
75
+ function firstLine(stdout) {
76
+ const line = stdout.split('\n')[0];
77
+ return line.replace(/\r$/, '');
78
+ }
79
+ /** `secret-tool`/`security` say "no match" through exit code and stderr. */
80
+ const STORE_NOT_FOUND = /not found|no such|does not exist|item could not be found|errSecItemNotFound/i;
81
+ const STORE_UNAVAILABLE = /(?:d-?bus|autolaunch|secret service|keyring|locked|denied|permission|not authorized|timed out)/i;
82
+ function lookupFailureReason(result, command) {
83
+ // Injected runners (tests/embedders) only provide a ready-made reason.
84
+ if (result.status === undefined)
85
+ return result.reason;
86
+ const stderr = result.stderr ?? '';
87
+ if (STORE_NOT_FOUND.test(stderr) ||
88
+ result.status === 44 ||
89
+ (result.status === 1 && stderr.trim().length === 0)) {
90
+ return `${command}: no entry for service ${SERVICE_NAME} (not found)`;
91
+ }
92
+ if (STORE_UNAVAILABLE.test(stderr)) {
93
+ return `${command}: OS secret store unavailable (keyring locked or error)`;
94
+ }
95
+ return result.reason;
96
+ }
97
+ function interpret(result, command, lookup = false) {
98
+ if (!result.ok) {
99
+ return { ok: false, reason: lookup ? lookupFailureReason(result, command) : result.reason };
100
+ }
101
+ const value = firstLine(result.stdout);
102
+ return value.length > 0
103
+ ? { ok: true, value }
104
+ : { ok: false, reason: `${command} returned an empty secret` };
105
+ }
106
+ export function readEnvPassphrase(env = process.env) {
107
+ const value = env[ENV_PASSPHRASE];
108
+ return typeof value === 'string' && value.length > 0
109
+ ? { ok: true, value }
110
+ : { ok: false, reason: `${ENV_PASSPHRASE} is not set` };
111
+ }
112
+ function currentUsername() {
113
+ return process.env.USER || process.env.USERNAME || 'user';
114
+ }
115
+ export function defaultDpapiPath(env = process.env) {
116
+ const appData = env.APPDATA || join(homedir(), 'AppData', 'Roaming');
117
+ return join(appData, 'saros', 'secret.dpapi');
118
+ }
119
+ const DPAPI_BLOB = /^[A-Za-z0-9+/]+={0,2}$/;
120
+ function readDpapi(passphrasePath, run) {
121
+ if (!existsSync(passphrasePath)) {
122
+ return { ok: false, reason: `DPAPI blob not found at ${passphrasePath}` };
123
+ }
124
+ let blob;
125
+ try {
126
+ blob = readFileSync(passphrasePath, 'utf8').trim();
127
+ }
128
+ catch {
129
+ return { ok: false, reason: `DPAPI blob at ${passphrasePath} could not be read` };
130
+ }
131
+ if (blob.length === 0) {
132
+ return { ok: false, reason: `DPAPI blob at ${passphrasePath} is empty` };
133
+ }
134
+ // Fail closed before PowerShell sees anything: a quote in the blob used to
135
+ // break out of the single-quoted string and run arbitrary commands.
136
+ if (!DPAPI_BLOB.test(blob) || blob.length % 4 !== 0) {
137
+ return { ok: false, reason: `DPAPI blob at ${passphrasePath} is not valid base64` };
138
+ }
139
+ // The blob travels in the child environment, never interpolated into the
140
+ // script, so no base64 character can become PowerShell syntax.
141
+ const script = "$ErrorActionPreference='Stop';" +
142
+ 'Add-Type -AssemblyName System.Security;' +
143
+ '$b=[Convert]::FromBase64String($env:SAROS_DPAPI_BLOB);' +
144
+ "[System.Text.Encoding]::UTF8.GetString([System.Security.Cryptography.ProtectedData]::Unprotect($b, $null, 'CurrentUser'))";
145
+ return interpret(run('powershell', ['-NoProfile', '-NonInteractive', '-Command', script], {
146
+ env: { SAROS_DPAPI_BLOB: blob },
147
+ }), 'powershell');
148
+ }
149
+ /** Read the passphrase from the OS store. Never prompts. */
150
+ export function readStorePassphrase(options = {}) {
151
+ const platform = options.platform ?? process.platform;
152
+ const run = options.run ?? runCommand;
153
+ const username = options.username ?? currentUsername();
154
+ if (platform === 'linux') {
155
+ return interpret(run('secret-tool', ['lookup', 'service', SERVICE_NAME, 'account', username]), 'secret-tool', true);
156
+ }
157
+ if (platform === 'darwin') {
158
+ return interpret(run('security', ['find-generic-password', '-s', SERVICE_NAME, '-a', username, '-w']), 'security', true);
159
+ }
160
+ if (platform === 'win32') {
161
+ const path = options.dpapiPath ?? defaultDpapiPath(options.env);
162
+ return readDpapi(path, run);
163
+ }
164
+ return { ok: false, reason: `the OS secret store is not supported on ${platform}` };
165
+ }
166
+ export function isInteractive(input = process.stdin) {
167
+ return input.isTTY === true;
168
+ }
169
+ /** Read one line from stdin when it is piped (`secret set` in scripts). */
170
+ export async function readStdinLine(input) {
171
+ let buffer = '';
172
+ for await (const chunk of input) {
173
+ buffer += String(chunk);
174
+ const newline = buffer.indexOf('\n');
175
+ if (newline !== -1)
176
+ return buffer.slice(0, newline).replace(/\r$/, '');
177
+ }
178
+ return buffer.length > 0 ? buffer : null;
179
+ }
180
+ /** Hidden TTY prompt. Resolves null when there is no TTY or no input. */
181
+ export function promptHidden(options = {}) {
182
+ const input = options.input ?? process.stdin;
183
+ const output = options.output ?? process.stderr;
184
+ if (!isInteractive(input))
185
+ return Promise.resolve(null);
186
+ const promise = new Promise((resolve) => {
187
+ let settled = false;
188
+ const finish = (value) => {
189
+ if (settled)
190
+ return;
191
+ settled = true;
192
+ resolve(value);
193
+ };
194
+ // A sink as readline's output discards the echoed characters; the question
195
+ // itself is written to the real stderr.
196
+ const sink = new Writable({
197
+ write(_chunk, _encoding, callback) {
198
+ callback();
199
+ },
200
+ });
201
+ const rl = createInterface({ input, output: sink, terminal: true });
202
+ output.write(options.question ?? 'Passphrase: ');
203
+ rl.once('line', (line) => {
204
+ finish(line.length > 0 ? line : null);
205
+ output.write('\n');
206
+ rl.close();
207
+ });
208
+ rl.once('close', () => finish(null));
209
+ });
210
+ return promise;
211
+ }
212
+ export async function readTtyPassphrase(options = {}) {
213
+ if (!isInteractive(options.input ?? process.stdin)) {
214
+ return { ok: false, reason: 'stdin is not a TTY' };
215
+ }
216
+ const value = await promptHidden(options);
217
+ return value === null
218
+ ? { ok: false, reason: 'no passphrase entered' }
219
+ : { ok: true, value };
220
+ }
221
+ function attempt(source, result) {
222
+ return result.ok ? { source, ok: true } : { source, ok: false, reason: result.reason };
223
+ }
224
+ /** One line per failed source; safe for errors, never includes a value. */
225
+ export function formatAttempts(attempts) {
226
+ return attempts
227
+ .filter((entry) => !entry.ok)
228
+ .map((entry) => ` ${entry.source}: ${entry.reason}`)
229
+ .join('\n');
230
+ }
231
+ /**
232
+ * Resolve the passphrase in precedence order. The TTY source runs only when
233
+ * `allowTty` is true (foreground serve), never for the daemon child.
234
+ */
235
+ export async function resolvePassphrase(options = {}) {
236
+ const attempts = [];
237
+ const envResult = readEnvPassphrase(options.env);
238
+ attempts.push(attempt('env', envResult));
239
+ if (envResult.ok) {
240
+ return { ok: true, value: envResult.value, source: 'env', attempts };
241
+ }
242
+ const storeResult = readStorePassphrase(options);
243
+ attempts.push(attempt('store', storeResult));
244
+ if (storeResult.ok) {
245
+ return { ok: true, value: storeResult.value, source: 'store', attempts };
246
+ }
247
+ if (options.allowTty !== true) {
248
+ attempts.push({
249
+ source: 'tty',
250
+ ok: false,
251
+ reason: 'interactive prompt is disabled in this context',
252
+ });
253
+ return { ok: false, attempts };
254
+ }
255
+ const ttyResult = await readTtyPassphrase(options);
256
+ attempts.push(attempt('tty', ttyResult));
257
+ return ttyResult.ok
258
+ ? { ok: true, value: ttyResult.value, source: 'tty', attempts }
259
+ : { ok: false, attempts };
260
+ }
261
+ export function storeBackend(platform = process.platform) {
262
+ if (platform === 'linux')
263
+ return 'secret-tool (libsecret)';
264
+ if (platform === 'darwin')
265
+ return 'macOS Keychain (security)';
266
+ if (platform === 'win32')
267
+ return 'Windows DPAPI (CurrentUser)';
268
+ return `unsupported platform (${platform})`;
269
+ }
270
+ /** Read-only snapshot for `saros-proxy secret status`; never returns a value. */
271
+ export function getSecretStatus(options = {}) {
272
+ const env = readEnvPassphrase(options.env);
273
+ const store = readStorePassphrase(options);
274
+ const tty = isInteractive(options.input ?? process.stdin);
275
+ return {
276
+ env: env.ok,
277
+ store: {
278
+ backend: storeBackend(options.platform ?? process.platform),
279
+ populated: store.ok,
280
+ reason: store.ok ? '' : store.reason,
281
+ },
282
+ tty,
283
+ effective: env.ok ? 'env' : store.ok ? 'store' : tty ? 'tty' : null,
284
+ };
285
+ }
286
+ /** Store a passphrase through the platform CLI (used by `secret set`). */
287
+ export function storePassphrase(passphrase, options = {}) {
288
+ const platform = options.platform ?? process.platform;
289
+ const backend = storeBackend(platform);
290
+ if (passphrase.length === 0) {
291
+ return { ok: false, backend, reason: 'passphrase must not be empty' };
292
+ }
293
+ const run = options.run ?? runCommand;
294
+ const username = options.username ?? currentUsername();
295
+ if (platform === 'linux') {
296
+ const result = run('secret-tool', ['store', '--label=saros-proxy passphrase', 'service', SERVICE_NAME, 'account', username], { input: passphrase });
297
+ return result.ok ? { ok: true, backend } : { ok: false, backend, reason: result.reason };
298
+ }
299
+ if (platform === 'darwin') {
300
+ const result = run('security', [
301
+ 'add-generic-password',
302
+ '-U',
303
+ '-s',
304
+ SERVICE_NAME,
305
+ '-a',
306
+ username,
307
+ '-w',
308
+ passphrase,
309
+ ]);
310
+ return result.ok ? { ok: true, backend } : { ok: false, backend, reason: result.reason };
311
+ }
312
+ if (platform === 'win32') {
313
+ const path = options.dpapiPath ?? defaultDpapiPath(options.env);
314
+ try {
315
+ mkdirSync(dirname(path), { recursive: true });
316
+ }
317
+ catch {
318
+ return { ok: false, backend, reason: `could not create ${dirname(path)}` };
319
+ }
320
+ const script = "$ErrorActionPreference='Stop';" +
321
+ 'Add-Type -AssemblyName System.Security;' +
322
+ '$b=[System.Text.Encoding]::UTF8.GetBytes($env:SAROS_SECRET_VALUE);' +
323
+ "$p=[System.Security.Cryptography.ProtectedData]::Protect($b,$null,'CurrentUser');" +
324
+ '[System.IO.File]::WriteAllText($env:SAROS_SECRET_BLOB,[Convert]::ToBase64String($p))';
325
+ const result = run('powershell', ['-NoProfile', '-NonInteractive', '-Command', script], {
326
+ env: { SAROS_SECRET_VALUE: passphrase, SAROS_SECRET_BLOB: path },
327
+ });
328
+ return result.ok ? { ok: true, backend } : { ok: false, backend, reason: result.reason };
329
+ }
330
+ return { ok: false, backend, reason: `the OS secret store is not supported on ${platform}` };
331
+ }
332
+ //# sourceMappingURL=source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/secrets/source.ts"],"names":[],"mappings":"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"}
@@ -49,5 +49,12 @@ export declare function getKeyTokenUsage(): {
49
49
  since: number;
50
50
  keys: Record<string, KeyTokenUsage>;
51
51
  };
52
- /** Test seam: drop in-memory state and any pending debounced write. */
53
- export declare function resetTokenUsage(): void;
52
+ /**
53
+ * Test seam: drop in-memory state and any pending debounced write. By default
54
+ * the persisted file is removed too, so counters cannot leak between tests
55
+ * that share a config dir; `keepFile` simulates a process restart that
56
+ * reloads existing history.
57
+ */
58
+ export declare function resetTokenUsage(options?: {
59
+ keepFile?: boolean;
60
+ }): void;
@@ -11,7 +11,7 @@
11
11
  * Storage mirrors model-stats.ts: a tiny JSON file in the saros config dir,
12
12
  * written behind a debounce so the request path never blocks on I/O.
13
13
  */
14
- import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from 'node:fs';
14
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, rmSync } from 'node:fs';
15
15
  import { dirname } from 'node:path';
16
16
  import { TOKEN_USAGE_SAVE_DEBOUNCE_MS } from './constants.js';
17
17
  import { getSarosDir } from './config.js';
@@ -60,7 +60,10 @@ export function parseTokenCounts(usage) {
60
60
  return {
61
61
  promptTokens,
62
62
  completionTokens,
63
- totalTokens: total || promptTokens + completionTokens,
63
+ // A stream merges counts per field, so a chunk can contribute a stale
64
+ // total while a later chunk raises prompt/completion. Never report a
65
+ // total below the parts the upstream itself reported.
66
+ totalTokens: Math.max(total, promptTokens + completionTokens),
64
67
  };
65
68
  }
66
69
  function load() {
@@ -188,8 +191,13 @@ export function getKeyTokenUsage() {
188
191
  load();
189
192
  return { since, keys: Object.fromEntries(counters) };
190
193
  }
191
- /** Test seam: drop in-memory state and any pending debounced write. */
192
- export function resetTokenUsage() {
194
+ /**
195
+ * Test seam: drop in-memory state and any pending debounced write. By default
196
+ * the persisted file is removed too, so counters cannot leak between tests
197
+ * that share a config dir; `keepFile` simulates a process restart that
198
+ * reloads existing history.
199
+ */
200
+ export function resetTokenUsage(options = {}) {
193
201
  if (saveTimer !== undefined) {
194
202
  clearTimeout(saveTimer);
195
203
  saveTimer = undefined;
@@ -197,5 +205,13 @@ export function resetTokenUsage() {
197
205
  counters.clear();
198
206
  since = 0;
199
207
  loaded = false;
208
+ if (options.keepFile)
209
+ return;
210
+ try {
211
+ rmSync(getTokenUsagePath(), { force: true });
212
+ }
213
+ catch (err) {
214
+ logger.debug({ err }, 'failed to remove token usage file');
215
+ }
200
216
  }
201
217
  //# sourceMappingURL=token-usage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"token-usage.js","sourceRoot":"","sources":["../src/token-usage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"token-usage.js","sourceRoot":"","sources":["../src/token-usage.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,10 @@
1
1
  /**
2
- * usage-client.ts — Per-key quota usage from the upstream usage API.
2
+ * usage-client.ts — Per-key quota usage from each provider's usage API.
3
3
  *
4
- * GET <upstream>/zen/go/v1/usage (Bearer = the proxy's own API key) returns:
5
- * {"usage":{"rolling":{"status":"ok","percent":12,"resetsAt":"…"},
6
- * "weekly":{…},"monthly":{…}}}
4
+ * Every provider owns its endpoint path, response parser and identity
5
+ * headers (opencode-go: `/zen/go/v1/usage` behind Cloudflare, which needs the
6
+ * CLI User-Agent; commandcode: `/alpha/billing/credits`, gated on the same
7
+ * CLI identity). `fetchKeyUsage` only adds the bearer credential.
7
8
  *
8
9
  * Refresh strategy: stale-while-revalidate on demand. `maybeRefreshUsage`
9
10
  * is called on the request path; when data is older than USAGE_REFRESH_TTL_MS
@@ -13,9 +14,36 @@
13
14
  */
14
15
  import { type ProxyState } from './proxy-logic.js';
15
16
  import type { ProxyConfig } from './config.js';
17
+ import type { KeyProvider } from './providers/index.js';
18
+ /**
19
+ * Per-key quota usage, normalized as a percentage *of the provider's own
20
+ * budget* — never absolute capacity. `usedPercent` is the **monthly** window:
21
+ * 0 = untouched, 100 = monthly allowance spent. `exhausted` is true when any
22
+ * window (5h, weekly or monthly) reached 100%, which takes the key out of
23
+ * rotation regardless of how much of the month remains — otherwise a key with
24
+ * 50% monthly and a spent weekly window would keep receiving traffic and
25
+ * failing upstream.
26
+ *
27
+ * This normalization is the contract mixed-provider pools rely on:
28
+ * `pickWeighted` (`proxy-logic.ts`) compares the numbers from every provider
29
+ * directly, so "30%" must always mean "30% of that provider's monthly budget".
30
+ * Percent is the most saros can honestly compare — opencode-go's
31
+ * `/zen/go/v1/usage` publishes only percentages
32
+ * (`{"usage":{"rolling":{"percent":1},"weekly":{"percent":15},
33
+ * "monthly":{"percent":19}}}`), with no token counts or absolute caps, while
34
+ * CommandCode publishes `used`/`cap` per window plus the remaining monthly
35
+ * credits, and its parser converts them to the same percent scale.
36
+ */
16
37
  export interface KeyUsage {
17
- /** Worst-window used percent, 0-100. */
18
- usedPercent: number;
38
+ /**
39
+ * Monthly used percent, 0-100, of this provider's own budget; null when the
40
+ * payload has no derivable monthly metric (the previous value must survive).
41
+ */
42
+ usedPercent: number | null;
43
+ /** True when any window is at 100% — rotation weighs the key 0. */
44
+ exhausted: boolean;
45
+ /** False when the payload carries no monthly percentage (CLI shows n/a). */
46
+ monthlyAvailable: boolean;
19
47
  rollingPercent: number;
20
48
  weeklyPercent: number;
21
49
  monthlyPercent: number;
@@ -43,15 +71,17 @@ export interface UsageFetchResult {
43
71
  * problem, not an account one, and must not match.
44
72
  */
45
73
  export declare function isEntitlementError(status: number, body: string): boolean;
46
- /** Parse a usage API response body into KeyUsage (throws on contract violation). */
47
- export declare function parseUsageResponse(body: string): KeyUsage;
48
74
  /**
49
- * Fetch current usage for one key. Failures (network, timeout, non-200,
50
- * contract violation) yield `usage: null` — callers treat that as "no fresh
51
- * data". A 403 entitlement rejection is reported separately because it is
52
- * definitive rather than transient.
75
+ * Fetch current usage for one key from its provider's endpoint. The provider
76
+ * supplies the path, the identity headers (CommandCode and opencode-go both
77
+ * reject requests without a CLI User-Agent) and the response parser; this
78
+ * function only adds the bearer credential and the timeout.
79
+ *
80
+ * Failures (network, timeout, non-200, contract violation) yield
81
+ * `usage: null` — callers treat that as "no fresh data". A 403 entitlement
82
+ * rejection is reported separately because it is definitive, not transient.
53
83
  */
54
- export declare function fetchKeyUsage(upstreamBaseUrl: string, apiKey: string, usagePath?: string): Promise<UsageFetchResult>;
84
+ export declare function fetchKeyUsage(upstreamBaseUrl: string, apiKey: string, provider?: KeyProvider): Promise<UsageFetchResult>;
55
85
  /** Test seam: reset coordinator state. */
56
86
  export declare function resetUsageRefreshState(): void;
57
87
  /**