offrouter-core 0.2.0 → 0.2.2
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/dist/audit.d.ts +137 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +320 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/credential-chain.d.ts +84 -0
- package/dist/auth/credential-chain.d.ts.map +1 -0
- package/dist/auth/credential-chain.js +150 -0
- package/dist/auth/credential-chain.js.map +1 -0
- package/dist/auth/index.d.ts +10 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +7 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/keychain.d.ts +60 -0
- package/dist/auth/keychain.d.ts.map +1 -0
- package/dist/auth/keychain.js +104 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/oauth-pkce.d.ts +138 -0
- package/dist/auth/oauth-pkce.d.ts.map +1 -0
- package/dist/auth/oauth-pkce.js +375 -0
- package/dist/auth/oauth-pkce.js.map +1 -0
- package/dist/auth/oauth-server.d.ts +36 -0
- package/dist/auth/oauth-server.d.ts.map +1 -0
- package/dist/auth/oauth-server.js +210 -0
- package/dist/auth/oauth-server.js.map +1 -0
- package/dist/config.d.ts +76 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +355 -0
- package/dist/config.js.map +1 -0
- package/dist/delegation.d.ts +123 -0
- package/dist/delegation.d.ts.map +1 -0
- package/dist/delegation.js +455 -0
- package/dist/delegation.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/policy.d.ts +45 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +318 -0
- package/dist/policy.js.map +1 -0
- package/{src/protocol.ts → dist/protocol.d.ts} +2 -2
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +6 -0
- package/dist/protocol.js.map +1 -0
- package/dist/providers/adapter.d.ts +137 -0
- package/dist/providers/adapter.d.ts.map +1 -0
- package/dist/providers/adapter.js +163 -0
- package/dist/providers/adapter.js.map +1 -0
- package/dist/providers/catalog-data.d.ts +128 -0
- package/dist/providers/catalog-data.d.ts.map +1 -0
- package/dist/providers/catalog-data.js +397 -0
- package/dist/providers/catalog-data.js.map +1 -0
- package/dist/providers/catalog.d.ts +63 -0
- package/dist/providers/catalog.d.ts.map +1 -0
- package/dist/providers/catalog.js +394 -0
- package/dist/providers/catalog.js.map +1 -0
- package/dist/providers/fake.d.ts +46 -0
- package/dist/providers/fake.d.ts.map +1 -0
- package/dist/providers/fake.js +234 -0
- package/dist/providers/fake.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +65 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -0
- package/dist/providers/openai-compatible.js +434 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/proxy/responses-mapper.d.ts +366 -0
- package/dist/proxy/responses-mapper.d.ts.map +1 -0
- package/dist/proxy/responses-mapper.js +517 -0
- package/dist/proxy/responses-mapper.js.map +1 -0
- package/dist/proxy/responses-server.d.ts +29 -0
- package/dist/proxy/responses-server.d.ts.map +1 -0
- package/dist/proxy/responses-server.js +360 -0
- package/dist/proxy/responses-server.js.map +1 -0
- package/dist/router.d.ts +18 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +296 -0
- package/dist/router.js.map +1 -0
- package/dist/schemas.d.ts +560 -0
- package/dist/schemas.d.ts.map +1 -0
- package/{src/schemas.ts → dist/schemas.js} +83 -103
- package/dist/schemas.js.map +1 -0
- package/dist/secrets.d.ts +112 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +326 -0
- package/dist/secrets.js.map +1 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/usage-file.d.ts +59 -0
- package/dist/usage-file.d.ts.map +1 -0
- package/dist/usage-file.js +316 -0
- package/dist/usage-file.js.map +1 -0
- package/dist/usage.d.ts +235 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +517 -0
- package/dist/usage.js.map +1 -0
- package/package.json +9 -4
- package/src/audit.test.ts +0 -302
- package/src/audit.ts +0 -553
- package/src/auth/credential-chain.test.ts +0 -326
- package/src/auth/credential-chain.ts +0 -235
- package/src/auth/index.ts +0 -45
- package/src/auth/keychain.test.ts +0 -265
- package/src/auth/keychain.ts +0 -168
- package/src/auth/oauth-pkce.test.ts +0 -329
- package/src/auth/oauth-pkce.ts +0 -571
- package/src/auth/oauth-server.test.ts +0 -83
- package/src/auth/oauth-server.ts +0 -296
- package/src/config.test.ts +0 -479
- package/src/config.ts +0 -505
- package/src/delegation.test.ts +0 -368
- package/src/delegation.ts +0 -605
- package/src/index.ts +0 -280
- package/src/policy.test.ts +0 -634
- package/src/policy.ts +0 -420
- package/src/providers/adapter.test.ts +0 -293
- package/src/providers/adapter.ts +0 -328
- package/src/providers/catalog-data.test.ts +0 -258
- package/src/providers/catalog-data.ts +0 -498
- package/src/providers/catalog.test.ts +0 -84
- package/src/providers/catalog.ts +0 -483
- package/src/providers/fake.ts +0 -312
- package/src/providers/openai-compatible.test.ts +0 -366
- package/src/providers/openai-compatible.ts +0 -554
- package/src/proxy/responses-mapper.test.ts +0 -290
- package/src/proxy/responses-mapper.ts +0 -736
- package/src/proxy/responses-server.test.ts +0 -322
- package/src/proxy/responses-server.ts +0 -469
- package/src/router.test.ts +0 -699
- package/src/router.ts +0 -352
- package/src/schemas.test.ts +0 -291
- package/src/secrets.test.ts +0 -271
- package/src/secrets.ts +0 -461
- package/src/types.ts +0 -173
- package/src/usage-file.test.ts +0 -243
- package/src/usage-file.ts +0 -435
- package/src/usage.test.ts +0 -335
- package/src/usage.ts +0 -859
- package/tsconfig.json +0 -9
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-backed usage store for OffRouter.
|
|
3
|
+
*
|
|
4
|
+
* Wraps an in-memory provider-health/quota engine and adds JSON persistence for
|
|
5
|
+
* per-account quota usage so near-limit detection survives across CLI
|
|
6
|
+
* invocations and MCP server restarts.
|
|
7
|
+
*
|
|
8
|
+
* On-disk layout (`$OFFROUTER_HOME/state/usage.json`):
|
|
9
|
+
* { version: 1, accounts: Record<`${providerId}:${accountId}`, PersistedAccount> }
|
|
10
|
+
*
|
|
11
|
+
* Safety guarantees mirror FileSecretStore:
|
|
12
|
+
* - atomic write via temp file + rename
|
|
13
|
+
* - chmod 0600 on POSIX after every write
|
|
14
|
+
* - missing or corrupt file => graceful fallback to empty state (never throws)
|
|
15
|
+
*
|
|
16
|
+
* Provider health/quota math (sliding-window error budget, cooldown) stays
|
|
17
|
+
* in-memory and process-local; only account quota usage is persisted, which is
|
|
18
|
+
* what near-limit detection and quota reporting depend on across sessions.
|
|
19
|
+
*/
|
|
20
|
+
import { readFileSync } from "node:fs";
|
|
21
|
+
import { chmod, mkdir, readFile, rename, unlink, writeFile, } from "node:fs/promises";
|
|
22
|
+
import { dirname } from "node:path";
|
|
23
|
+
import { randomBytes } from "node:crypto";
|
|
24
|
+
import { computeAccountSnapshot, InMemoryUsageStore, } from "./usage.js";
|
|
25
|
+
export { DEFAULT_NEAR_LIMIT_THRESHOLD } from "./usage.js";
|
|
26
|
+
export { InMemoryUsageStore } from "./usage.js";
|
|
27
|
+
const EMPTY_DOC = { version: 1, accounts: {} };
|
|
28
|
+
function accountKey(providerId, accountId) {
|
|
29
|
+
return `${providerId}:${accountId}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse a persisted usage document leniently. A missing or corrupt file is
|
|
33
|
+
* treated as empty state rather than a hard failure so a bad write can never
|
|
34
|
+
* wedge the CLI.
|
|
35
|
+
*/
|
|
36
|
+
function parseUsageDocument(raw) {
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Corrupt JSON: fall back to empty instead of throwing.
|
|
43
|
+
return { ...EMPTY_DOC, accounts: {} };
|
|
44
|
+
}
|
|
45
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
46
|
+
return { ...EMPTY_DOC, accounts: {} };
|
|
47
|
+
}
|
|
48
|
+
const record = parsed;
|
|
49
|
+
const accountsRaw = record.accounts;
|
|
50
|
+
if (typeof accountsRaw !== "object" ||
|
|
51
|
+
accountsRaw === null ||
|
|
52
|
+
Array.isArray(accountsRaw)) {
|
|
53
|
+
return { ...EMPTY_DOC, accounts: {} };
|
|
54
|
+
}
|
|
55
|
+
const accounts = {};
|
|
56
|
+
for (const [key, value] of Object.entries(accountsRaw)) {
|
|
57
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const entry = value;
|
|
61
|
+
if (typeof entry.providerId !== "string" ||
|
|
62
|
+
typeof entry.accountId !== "string" ||
|
|
63
|
+
typeof entry.used !== "number") {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const doc = {
|
|
67
|
+
providerId: entry.providerId,
|
|
68
|
+
accountId: entry.accountId,
|
|
69
|
+
used: entry.used,
|
|
70
|
+
lastUpdated: typeof entry.lastUpdated === "string" ? entry.lastUpdated : "",
|
|
71
|
+
};
|
|
72
|
+
if (typeof entry.limit === "number") {
|
|
73
|
+
doc.limit = entry.limit;
|
|
74
|
+
}
|
|
75
|
+
accounts[key] = doc;
|
|
76
|
+
}
|
|
77
|
+
return { version: 1, accounts };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* File-backed UsageStore. Persists per-account quota usage atomically and
|
|
81
|
+
* delegates provider health/quota logic to an in-memory engine.
|
|
82
|
+
*/
|
|
83
|
+
export class FileUsageStore {
|
|
84
|
+
filePath;
|
|
85
|
+
#fs;
|
|
86
|
+
#now;
|
|
87
|
+
#delegate;
|
|
88
|
+
/** In-memory mirror of persisted account docs, hydrated lazily. */
|
|
89
|
+
#accounts = null;
|
|
90
|
+
/** Serializes mutations so concurrent writes never interleave. */
|
|
91
|
+
#chain = Promise.resolve();
|
|
92
|
+
constructor(options) {
|
|
93
|
+
if (!options?.filePath || typeof options.filePath !== "string") {
|
|
94
|
+
throw new TypeError("FileUsageStore requires options.filePath");
|
|
95
|
+
}
|
|
96
|
+
this.filePath = options.filePath;
|
|
97
|
+
this.#now = options.now ?? (() => new Date());
|
|
98
|
+
this.#delegate =
|
|
99
|
+
options.delegate ?? new InMemoryUsageStore({ now: this.#now });
|
|
100
|
+
this.#fs = {
|
|
101
|
+
mkdir,
|
|
102
|
+
readFile,
|
|
103
|
+
rename,
|
|
104
|
+
unlink,
|
|
105
|
+
writeFile,
|
|
106
|
+
chmod,
|
|
107
|
+
...options.fileSystem,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
#exclusive(fn) {
|
|
111
|
+
const run = this.#chain.then(fn, fn);
|
|
112
|
+
this.#chain = run.then(() => undefined, () => undefined);
|
|
113
|
+
return run;
|
|
114
|
+
}
|
|
115
|
+
/** Load the file once into the in-memory account map. Never throws. */
|
|
116
|
+
async #load() {
|
|
117
|
+
if (this.#accounts) {
|
|
118
|
+
return this.#accounts;
|
|
119
|
+
}
|
|
120
|
+
let doc;
|
|
121
|
+
try {
|
|
122
|
+
const raw = await this.#fs.readFile(this.filePath, "utf8");
|
|
123
|
+
doc = parseUsageDocument(raw);
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
if (err &&
|
|
127
|
+
typeof err === "object" &&
|
|
128
|
+
"code" in err &&
|
|
129
|
+
err.code === "ENOENT") {
|
|
130
|
+
doc = { ...EMPTY_DOC, accounts: {} };
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Any read failure (including corruption surfaced as a throw) is
|
|
134
|
+
// treated as empty state so the CLI keeps working.
|
|
135
|
+
doc = { ...EMPTY_DOC, accounts: {} };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const map = new Map();
|
|
139
|
+
for (const [key, entry] of Object.entries(doc.accounts)) {
|
|
140
|
+
map.set(key, {
|
|
141
|
+
providerId: entry.providerId,
|
|
142
|
+
accountId: entry.accountId,
|
|
143
|
+
used: entry.used,
|
|
144
|
+
limit: entry.limit,
|
|
145
|
+
updatedAt: entry.lastUpdated,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
this.#accounts = map;
|
|
149
|
+
return map;
|
|
150
|
+
}
|
|
151
|
+
/** Synchronous load for toJSON diagnostics; never throws. */
|
|
152
|
+
#loadKnownSync() {
|
|
153
|
+
if (this.#accounts) {
|
|
154
|
+
return this.#accounts;
|
|
155
|
+
}
|
|
156
|
+
let map = new Map();
|
|
157
|
+
try {
|
|
158
|
+
const raw = readFileSync(this.filePath, "utf8");
|
|
159
|
+
const doc = parseUsageDocument(raw);
|
|
160
|
+
map = new Map();
|
|
161
|
+
for (const [key, entry] of Object.entries(doc.accounts)) {
|
|
162
|
+
map.set(key, {
|
|
163
|
+
providerId: entry.providerId,
|
|
164
|
+
accountId: entry.accountId,
|
|
165
|
+
used: entry.used,
|
|
166
|
+
limit: entry.limit,
|
|
167
|
+
updatedAt: entry.lastUpdated,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
map = new Map();
|
|
173
|
+
}
|
|
174
|
+
return map;
|
|
175
|
+
}
|
|
176
|
+
async #persist(accounts) {
|
|
177
|
+
const persistedAccounts = {};
|
|
178
|
+
for (const [key, doc] of accounts) {
|
|
179
|
+
const snap = computeAccountSnapshot(doc);
|
|
180
|
+
const entry = {
|
|
181
|
+
providerId: doc.providerId,
|
|
182
|
+
accountId: doc.accountId,
|
|
183
|
+
used: doc.used,
|
|
184
|
+
lastUpdated: doc.updatedAt,
|
|
185
|
+
health: snap.health,
|
|
186
|
+
recentErrors: [],
|
|
187
|
+
};
|
|
188
|
+
if (doc.limit !== undefined) {
|
|
189
|
+
entry.limit = doc.limit;
|
|
190
|
+
}
|
|
191
|
+
persistedAccounts[key] = entry;
|
|
192
|
+
}
|
|
193
|
+
const payload = `${JSON.stringify({ version: 1, accounts: persistedAccounts }, null, 2)}\n`;
|
|
194
|
+
await this.#fs.mkdir(dirname(this.filePath), { recursive: true });
|
|
195
|
+
const tmp = `${this.filePath}.${randomBytes(8).toString("hex")}.tmp`;
|
|
196
|
+
try {
|
|
197
|
+
await this.#fs.writeFile(tmp, payload, {
|
|
198
|
+
encoding: "utf8",
|
|
199
|
+
mode: 0o600,
|
|
200
|
+
});
|
|
201
|
+
await this.#fs.chmod(tmp, 0o600);
|
|
202
|
+
await this.#fs.rename(tmp, this.filePath);
|
|
203
|
+
await this.#fs.chmod(this.filePath, 0o600);
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
await this.#fs.unlink(tmp).catch((unlinkErr) => {
|
|
207
|
+
if (unlinkErr &&
|
|
208
|
+
typeof unlinkErr === "object" &&
|
|
209
|
+
"code" in unlinkErr &&
|
|
210
|
+
unlinkErr.code === "ENOENT") {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
throw unlinkErr;
|
|
214
|
+
});
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// ---- Provider health/quota: delegated to the in-memory engine ----
|
|
219
|
+
record(event) {
|
|
220
|
+
return this.#delegate.record(event);
|
|
221
|
+
}
|
|
222
|
+
getProvider(providerId) {
|
|
223
|
+
return this.#delegate.getProvider(providerId);
|
|
224
|
+
}
|
|
225
|
+
listProviders() {
|
|
226
|
+
return this.#delegate.listProviders();
|
|
227
|
+
}
|
|
228
|
+
setSubscriptionQuota(providerId, input) {
|
|
229
|
+
return this.#delegate.setSubscriptionQuota(providerId, input);
|
|
230
|
+
}
|
|
231
|
+
explainPaidFallback(providerId, options) {
|
|
232
|
+
return this.#delegate.explainPaidFallback(providerId, options);
|
|
233
|
+
}
|
|
234
|
+
// ---- Per-account quota usage: persisted ----
|
|
235
|
+
async recordUsage(providerId, accountId, input) {
|
|
236
|
+
return this.#exclusive(async () => {
|
|
237
|
+
const accounts = await this.#load();
|
|
238
|
+
const at = input.at ?? this.#now();
|
|
239
|
+
const key = accountKey(providerId, accountId);
|
|
240
|
+
let doc = accounts.get(key);
|
|
241
|
+
if (!doc) {
|
|
242
|
+
doc = {
|
|
243
|
+
providerId,
|
|
244
|
+
accountId,
|
|
245
|
+
used: 0,
|
|
246
|
+
updatedAt: at.toISOString(),
|
|
247
|
+
};
|
|
248
|
+
accounts.set(key, doc);
|
|
249
|
+
}
|
|
250
|
+
if (input.used !== undefined)
|
|
251
|
+
doc.used = input.used;
|
|
252
|
+
if (input.limit !== undefined)
|
|
253
|
+
doc.limit = input.limit;
|
|
254
|
+
doc.updatedAt = at.toISOString();
|
|
255
|
+
await this.#persist(accounts);
|
|
256
|
+
return computeAccountSnapshot(doc);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
async isNearLimit(providerId, accountId, threshold) {
|
|
260
|
+
return this.#exclusive(async () => {
|
|
261
|
+
const accounts = await this.#load();
|
|
262
|
+
const doc = accounts.get(accountKey(providerId, accountId));
|
|
263
|
+
if (!doc)
|
|
264
|
+
return false;
|
|
265
|
+
return computeAccountSnapshot(doc, threshold).nearLimit;
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
async getNearLimitAccounts(threshold) {
|
|
269
|
+
return this.#exclusive(async () => {
|
|
270
|
+
const accounts = await this.#load();
|
|
271
|
+
const out = [];
|
|
272
|
+
for (const doc of accounts.values()) {
|
|
273
|
+
const snap = computeAccountSnapshot(doc, threshold);
|
|
274
|
+
if (snap.nearLimit)
|
|
275
|
+
out.push(snap);
|
|
276
|
+
}
|
|
277
|
+
return out.sort((a, b) => {
|
|
278
|
+
if (a.providerId !== b.providerId)
|
|
279
|
+
return a.providerId.localeCompare(b.providerId);
|
|
280
|
+
return a.accountId.localeCompare(b.accountId);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
async getAccountUsage(providerId, accountId) {
|
|
285
|
+
return this.#exclusive(async () => {
|
|
286
|
+
const accounts = await this.#load();
|
|
287
|
+
const doc = accounts.get(accountKey(providerId, accountId));
|
|
288
|
+
if (!doc)
|
|
289
|
+
return undefined;
|
|
290
|
+
return computeAccountSnapshot(doc);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
async listAccountUsage() {
|
|
294
|
+
return this.#exclusive(async () => {
|
|
295
|
+
const accounts = await this.#load();
|
|
296
|
+
const out = [];
|
|
297
|
+
for (const doc of accounts.values()) {
|
|
298
|
+
out.push(computeAccountSnapshot(doc));
|
|
299
|
+
}
|
|
300
|
+
return out.sort((a, b) => {
|
|
301
|
+
if (a.providerId !== b.providerId)
|
|
302
|
+
return a.providerId.localeCompare(b.providerId);
|
|
303
|
+
return a.accountId.localeCompare(b.accountId);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
toJSON() {
|
|
308
|
+
const accounts = this.#loadKnownSync();
|
|
309
|
+
return {
|
|
310
|
+
kind: "file-usage",
|
|
311
|
+
filePath: this.filePath,
|
|
312
|
+
accountCount: accounts.size,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=usage-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-file.js","sourceRoot":"","sources":["../src/usage-file.ts"],"names":[],"mappings":"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"}
|
package/dist/usage.d.ts
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage, quota, and provider-health store for OffRouter V1.
|
|
3
|
+
*
|
|
4
|
+
* Distinguishes subscription quota from API-key spend so doctor/status/route
|
|
5
|
+
* explain can say why paid fallback was or was not used.
|
|
6
|
+
*
|
|
7
|
+
* Provider health uses a small sliding-window error budget plus cooldown:
|
|
8
|
+
* - healthy: enough budget remaining, no active cooldown
|
|
9
|
+
* - degraded: elevated errors but still usable
|
|
10
|
+
* - dead: cooldown active after budget exhaustion / hard failures
|
|
11
|
+
*
|
|
12
|
+
* In-memory only for this task; SQLite is deferred until the interface stabilizes.
|
|
13
|
+
*/
|
|
14
|
+
import { type AuditStore, type InMemoryAuditStoreOptions } from "./audit.js";
|
|
15
|
+
import type { AuthTier, ProviderHealth, SubscriptionStatus } from "./types.js";
|
|
16
|
+
/** Canonical event classes that drive health / quota / spend counters. */
|
|
17
|
+
export type UsageEventKind = "request_success" | "request_error" | "rate_limited" | "quota_exhausted" | "timeout" | "cancelled" | "auth_failure" | "subscription_quota_set" | "subscription_usage" | "api_key_spend";
|
|
18
|
+
export interface SlidingWindowOptions {
|
|
19
|
+
/** Error-window length in ms. Default 60_000. */
|
|
20
|
+
windowMs?: number;
|
|
21
|
+
/** Errors inside the window that keep the provider healthy. Default 2. */
|
|
22
|
+
healthyMaxErrors?: number;
|
|
23
|
+
/** Errors inside the window before dead (above = dead). Default 5. */
|
|
24
|
+
deadMinErrors?: number;
|
|
25
|
+
/** Cooldown after death / hard failure before resurrectable. Default 30_000. */
|
|
26
|
+
cooldownMs?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface ProviderUsageSnapshot {
|
|
29
|
+
providerId: string;
|
|
30
|
+
modelId?: string;
|
|
31
|
+
health: ProviderHealth;
|
|
32
|
+
/** Why health is at this level (short, audit-safe). */
|
|
33
|
+
healthReason?: string;
|
|
34
|
+
/** Errors counted in the sliding window. */
|
|
35
|
+
recentErrorCount: number;
|
|
36
|
+
/** Epoch ms when cooldown ends, if any. */
|
|
37
|
+
cooldownUntil?: number;
|
|
38
|
+
/** Configured / observed subscription unit quota (opaque units). */
|
|
39
|
+
subscriptionQuotaLimit?: number;
|
|
40
|
+
/** Subscription units consumed (opaque units). */
|
|
41
|
+
subscriptionQuotaUsed: number;
|
|
42
|
+
/** Remaining subscription units when limit known. */
|
|
43
|
+
subscriptionQuotaRemaining?: number;
|
|
44
|
+
subscriptionStatus?: SubscriptionStatus;
|
|
45
|
+
/** Cumulative paid API-key spend in USD (estimated). */
|
|
46
|
+
apiKeySpendUsd: number;
|
|
47
|
+
/** Total successful request count. */
|
|
48
|
+
successCount: number;
|
|
49
|
+
/** Total error count (all time in this process). */
|
|
50
|
+
errorCount: number;
|
|
51
|
+
lastErrorAt?: string;
|
|
52
|
+
lastErrorKind?: UsageEventKind;
|
|
53
|
+
lastSuccessAt?: string;
|
|
54
|
+
updatedAt: string;
|
|
55
|
+
}
|
|
56
|
+
export interface UsageEvent {
|
|
57
|
+
providerId: string;
|
|
58
|
+
modelId?: string;
|
|
59
|
+
authTier: AuthTier;
|
|
60
|
+
kind: UsageEventKind;
|
|
61
|
+
/**
|
|
62
|
+
* For subscription_usage / subscription_quota_set: opaque quota units.
|
|
63
|
+
* For api_key_spend: USD amount.
|
|
64
|
+
*/
|
|
65
|
+
amount?: number;
|
|
66
|
+
/** Optional hard set of subscription limit when kind is subscription_quota_set. */
|
|
67
|
+
quotaLimit?: number;
|
|
68
|
+
subscriptionStatus?: SubscriptionStatus;
|
|
69
|
+
/** Override clock for tests. */
|
|
70
|
+
at?: Date;
|
|
71
|
+
/** Human-safe detail (already redacted by caller). */
|
|
72
|
+
detail?: string;
|
|
73
|
+
}
|
|
74
|
+
export interface RecordUsageResult {
|
|
75
|
+
snapshot: ProviderUsageSnapshot;
|
|
76
|
+
/**
|
|
77
|
+
* True when this event moved health from a usable state into dead/degraded,
|
|
78
|
+
* or when subscription became exhausted.
|
|
79
|
+
*/
|
|
80
|
+
healthChanged: boolean;
|
|
81
|
+
previousHealth: ProviderHealth;
|
|
82
|
+
}
|
|
83
|
+
export declare const DEFAULT_NEAR_LIMIT_THRESHOLD = 0.2;
|
|
84
|
+
/**
|
|
85
|
+
* Snapshot of usage for a specific account within a provider.
|
|
86
|
+
*/
|
|
87
|
+
export interface AccountUsageSnapshot {
|
|
88
|
+
providerId: string;
|
|
89
|
+
accountId: string;
|
|
90
|
+
used: number;
|
|
91
|
+
limit?: number;
|
|
92
|
+
remaining?: number;
|
|
93
|
+
percentRemaining?: number;
|
|
94
|
+
nearLimit: boolean;
|
|
95
|
+
subscriptionStatus: SubscriptionStatus;
|
|
96
|
+
/**
|
|
97
|
+
* Derived account health from quota usage: "dead" when exhausted,
|
|
98
|
+
* "degraded" when near-limit, otherwise "healthy". Useful for
|
|
99
|
+
* persisted stores that need a stable, restorable health signal.
|
|
100
|
+
*/
|
|
101
|
+
health?: ProviderHealth;
|
|
102
|
+
updatedAt: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Minimal persisted document for a single account's quota usage.
|
|
106
|
+
* Shared between the in-memory and file-backed usage stores so snapshot
|
|
107
|
+
* math stays in one place.
|
|
108
|
+
*/
|
|
109
|
+
export interface AccountUsageDoc {
|
|
110
|
+
providerId: string;
|
|
111
|
+
accountId: string;
|
|
112
|
+
used: number;
|
|
113
|
+
limit?: number;
|
|
114
|
+
updatedAt: string;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Why paid API-key capacity was or was not selected given current usage state.
|
|
118
|
+
* Consumed by doctor / status / route explain surfaces.
|
|
119
|
+
*/
|
|
120
|
+
export interface PaidFallbackExplanation {
|
|
121
|
+
providerId: string;
|
|
122
|
+
recommendation: "prefer_subscription" | "allow_api_key_fallback" | "api_key_only" | "no_capacity";
|
|
123
|
+
reason: string;
|
|
124
|
+
subscriptionStatus?: SubscriptionStatus;
|
|
125
|
+
subscriptionQuotaRemaining?: number;
|
|
126
|
+
subscriptionHealth?: ProviderHealth;
|
|
127
|
+
apiKeySpendUsd: number;
|
|
128
|
+
usedPaidFallback: boolean;
|
|
129
|
+
}
|
|
130
|
+
export interface UsageStore {
|
|
131
|
+
/** Atomically apply a usage event and recompute health. */
|
|
132
|
+
record(event: UsageEvent): Promise<RecordUsageResult>;
|
|
133
|
+
getProvider(providerId: string): Promise<ProviderUsageSnapshot | undefined>;
|
|
134
|
+
listProviders(): Promise<ProviderUsageSnapshot[]>;
|
|
135
|
+
/**
|
|
136
|
+
* Set subscription quota limit/used for a provider without incrementing spend.
|
|
137
|
+
* Useful when adapters discover limit snapshots out of band.
|
|
138
|
+
*/
|
|
139
|
+
setSubscriptionQuota(providerId: string, input: {
|
|
140
|
+
limit?: number;
|
|
141
|
+
used?: number;
|
|
142
|
+
status?: SubscriptionStatus;
|
|
143
|
+
modelId?: string;
|
|
144
|
+
at?: Date;
|
|
145
|
+
}): Promise<ProviderUsageSnapshot>;
|
|
146
|
+
/**
|
|
147
|
+
* Explain whether subscription capacity remains or paid fallback is justified.
|
|
148
|
+
*/
|
|
149
|
+
explainPaidFallback(providerId: string, options?: {
|
|
150
|
+
hasApiKeyCandidate?: boolean;
|
|
151
|
+
}): Promise<PaidFallbackExplanation>;
|
|
152
|
+
/**
|
|
153
|
+
* Record usage for a specific account within a provider.
|
|
154
|
+
*/
|
|
155
|
+
recordUsage(providerId: string, accountId: string, input: {
|
|
156
|
+
used?: number;
|
|
157
|
+
limit?: number;
|
|
158
|
+
at?: Date;
|
|
159
|
+
}): Promise<AccountUsageSnapshot>;
|
|
160
|
+
/**
|
|
161
|
+
* Check whether a specific account is near its quota limit.
|
|
162
|
+
*/
|
|
163
|
+
isNearLimit(providerId: string, accountId: string, threshold?: number): Promise<boolean>;
|
|
164
|
+
/**
|
|
165
|
+
* Get all accounts whose usage is near their quota limit.
|
|
166
|
+
*/
|
|
167
|
+
getNearLimitAccounts(threshold?: number): Promise<AccountUsageSnapshot[]>;
|
|
168
|
+
/**
|
|
169
|
+
* Get the usage snapshot for a specific account.
|
|
170
|
+
*/
|
|
171
|
+
getAccountUsage(providerId: string, accountId: string): Promise<AccountUsageSnapshot | undefined>;
|
|
172
|
+
/**
|
|
173
|
+
* List all account usage snapshots.
|
|
174
|
+
*/
|
|
175
|
+
listAccountUsage(): Promise<AccountUsageSnapshot[]>;
|
|
176
|
+
/**
|
|
177
|
+
* JSON-safe diagnostic view. No secrets.
|
|
178
|
+
*/
|
|
179
|
+
toJSON(): Record<string, unknown>;
|
|
180
|
+
}
|
|
181
|
+
export interface InMemoryUsageStoreOptions extends SlidingWindowOptions {
|
|
182
|
+
now?: () => Date;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* In-memory usage/health store.
|
|
186
|
+
* Mutations are serialized so concurrent updates never lose counters.
|
|
187
|
+
*/
|
|
188
|
+
export declare class InMemoryUsageStore implements UsageStore {
|
|
189
|
+
#private;
|
|
190
|
+
constructor(options?: InMemoryUsageStoreOptions);
|
|
191
|
+
record(event: UsageEvent): Promise<RecordUsageResult>;
|
|
192
|
+
getProvider(providerId: string): Promise<ProviderUsageSnapshot | undefined>;
|
|
193
|
+
listProviders(): Promise<ProviderUsageSnapshot[]>;
|
|
194
|
+
setSubscriptionQuota(providerId: string, input: {
|
|
195
|
+
limit?: number;
|
|
196
|
+
used?: number;
|
|
197
|
+
status?: SubscriptionStatus;
|
|
198
|
+
modelId?: string;
|
|
199
|
+
at?: Date;
|
|
200
|
+
}): Promise<ProviderUsageSnapshot>;
|
|
201
|
+
explainPaidFallback(providerId: string, options?: {
|
|
202
|
+
hasApiKeyCandidate?: boolean;
|
|
203
|
+
}): Promise<PaidFallbackExplanation>;
|
|
204
|
+
recordUsage(providerId: string, accountId: string, input: {
|
|
205
|
+
used?: number;
|
|
206
|
+
limit?: number;
|
|
207
|
+
at?: Date;
|
|
208
|
+
}): Promise<AccountUsageSnapshot>;
|
|
209
|
+
isNearLimit(providerId: string, accountId: string, threshold?: number): Promise<boolean>;
|
|
210
|
+
getNearLimitAccounts(threshold?: number): Promise<AccountUsageSnapshot[]>;
|
|
211
|
+
getAccountUsage(providerId: string, accountId: string): Promise<AccountUsageSnapshot | undefined>;
|
|
212
|
+
listAccountUsage(): Promise<AccountUsageSnapshot[]>;
|
|
213
|
+
toJSON(): Record<string, unknown>;
|
|
214
|
+
}
|
|
215
|
+
export declare function computeAccountSnapshot(doc: AccountUsageDoc, threshold?: number): AccountUsageSnapshot;
|
|
216
|
+
/**
|
|
217
|
+
* Composite facade for callers that want one injectable "state" handle.
|
|
218
|
+
* Writes stay on the specialized stores; this is a thin convenience.
|
|
219
|
+
*/
|
|
220
|
+
export interface StateStore {
|
|
221
|
+
audit: AuditStore;
|
|
222
|
+
usage: UsageStore;
|
|
223
|
+
}
|
|
224
|
+
export declare class InMemoryStateStore implements StateStore {
|
|
225
|
+
readonly audit: AuditStore;
|
|
226
|
+
readonly usage: UsageStore;
|
|
227
|
+
constructor(options?: {
|
|
228
|
+
audit?: AuditStore;
|
|
229
|
+
usage?: UsageStore;
|
|
230
|
+
auditOptions?: InMemoryAuditStoreOptions;
|
|
231
|
+
usageOptions?: InMemoryUsageStoreOptions;
|
|
232
|
+
});
|
|
233
|
+
toJSON(): Record<string, unknown>;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=usage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"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"}
|