@certalis/agent-smith 0.0.0-stage → 0.1.1

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.
@@ -0,0 +1,4945 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
12
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
13
+ var __require = import.meta.require;
14
+
15
+ // ../../packages/core/src/constants.ts
16
+ var TIME_CONSTANTS, BUFFER_SIZES, NETWORK, CACHE, LIMITS, HTTP_STATUS;
17
+ var init_constants = __esm(() => {
18
+ TIME_CONSTANTS = {
19
+ SECOND: 1000,
20
+ MINUTE: 60 * 1000,
21
+ HOUR: 60 * 60 * 1000,
22
+ DAY: 24 * 60 * 60 * 1000,
23
+ SESSION_DURATION_DEFAULT: 5 * 60 * 60 * 1000,
24
+ SESSION_DURATION_FALLBACK: 3600000,
25
+ STREAM_TIMEOUT_DEFAULT: 1000 * 60 * 1,
26
+ OAUTH_STATE_TTL: 10,
27
+ RETRY_DELAY_DEFAULT: 1000,
28
+ CACHE_YEAR: 31536000
29
+ };
30
+ BUFFER_SIZES = {
31
+ STREAM_USAGE_BUFFER_KB: 256,
32
+ STREAM_USAGE_BUFFER_BYTES: 256 * 1024,
33
+ STREAM_BODY_MAX_KB: 256,
34
+ STREAM_BODY_MAX_BYTES: 256 * 1024,
35
+ ANTHROPIC_STREAM_CAP_BYTES: 32768,
36
+ STREAM_TEE_MAX_BYTES: 1024 * 1024,
37
+ LOG_FILE_MAX_SIZE: 10 * 1024 * 1024
38
+ };
39
+ NETWORK = {
40
+ DEFAULT_PORT: 8080,
41
+ IDLE_TIMEOUT_MAX: 255
42
+ };
43
+ CACHE = {
44
+ STATIC_ASSETS_MAX_AGE: 31536000,
45
+ CACHE_CONTROL_IMMUTABLE: "public, max-age=31536000, immutable",
46
+ CACHE_CONTROL_STATIC: "public, max-age=31536000",
47
+ CACHE_CONTROL_NO_CACHE: "no-cache, no-store, must-revalidate"
48
+ };
49
+ LIMITS = {
50
+ REQUEST_HISTORY_DEFAULT: 50,
51
+ REQUEST_DETAILS_DEFAULT: 100,
52
+ REQUEST_HISTORY_MAX: 1000,
53
+ LOG_READ_DEFAULT: 1000,
54
+ ACCOUNT_NAME_MIN_LENGTH: 1,
55
+ ACCOUNT_NAME_MAX_LENGTH: 100,
56
+ CONSOLE_SEPARATOR_LENGTH: 100,
57
+ CONSOLE_COLUMN_PADDING: {
58
+ NAME: 20,
59
+ TYPE: 10,
60
+ TIER: 6,
61
+ REQUESTS: 12,
62
+ TOKEN: 10,
63
+ STATUS: 20
64
+ }
65
+ };
66
+ HTTP_STATUS = {
67
+ OK: 200,
68
+ NOT_FOUND: 404,
69
+ TOO_MANY_REQUESTS: 429,
70
+ INTERNAL_SERVER_ERROR: 500,
71
+ SERVICE_UNAVAILABLE: 503
72
+ };
73
+ });
74
+
75
+ // ../../packages/core/src/container.ts
76
+ class Container {
77
+ services = new Map;
78
+ factories = new Map;
79
+ register(key, factory) {
80
+ this.factories.set(key, factory);
81
+ }
82
+ registerInstance(key, instance) {
83
+ this.services.set(key, instance);
84
+ }
85
+ resolve(key) {
86
+ if (this.services.has(key)) {
87
+ return this.services.get(key);
88
+ }
89
+ const factory = this.factories.get(key);
90
+ if (!factory) {
91
+ throw new Error(`Service '${String(key)}' not registered`);
92
+ }
93
+ const instance = factory();
94
+ this.services.set(key, instance);
95
+ return instance;
96
+ }
97
+ has(key) {
98
+ return this.factories.has(key) || this.services.has(key);
99
+ }
100
+ clear() {
101
+ this.services.clear();
102
+ this.factories.clear();
103
+ }
104
+ }
105
+ var container;
106
+ var init_container = __esm(() => {
107
+ container = new Container;
108
+ });
109
+
110
+ // ../../packages/core/src/di.ts
111
+ var SERVICE_KEYS;
112
+ var init_di = __esm(() => {
113
+ init_container();
114
+ SERVICE_KEYS = {
115
+ Logger: Symbol("Logger"),
116
+ Config: Symbol("Config"),
117
+ Database: Symbol("Database"),
118
+ PricingLogger: Symbol("PricingLogger"),
119
+ AsyncWriter: Symbol("AsyncWriter")
120
+ };
121
+ });
122
+
123
+ // ../../packages/core/src/errors.ts
124
+ function isAppError(error) {
125
+ return error instanceof AppError;
126
+ }
127
+ function logError(error, logger) {
128
+ if (isAppError(error)) {
129
+ const sanitizedContext = error.context ? sanitizeErrorContext(error.context) : undefined;
130
+ logger.error(`${error.name}: ${error.message}`, {
131
+ code: error.code,
132
+ statusCode: error.statusCode,
133
+ context: sanitizedContext
134
+ });
135
+ } else if (error instanceof Error) {
136
+ logger.error(`Error: ${error.message}`, {
137
+ name: error.name,
138
+ stack: error.stack
139
+ });
140
+ } else {
141
+ logger.error("Unknown error", error);
142
+ }
143
+ }
144
+ function sanitizeErrorContext(context) {
145
+ const sanitized = {};
146
+ const sensitiveKeys = ["token", "password", "secret", "key", "authorization"];
147
+ for (const [key, value] of Object.entries(context)) {
148
+ const lowerKey = key.toLowerCase();
149
+ if (sensitiveKeys.some((sensitive) => lowerKey.includes(sensitive))) {
150
+ sanitized[key] = "[REDACTED]";
151
+ } else if (typeof value === "object" && value !== null) {
152
+ sanitized[key] = sanitizeErrorContext(value);
153
+ } else {
154
+ sanitized[key] = value;
155
+ }
156
+ }
157
+ return sanitized;
158
+ }
159
+ var AppError, AuthError, TokenRefreshError, RateLimitError, ValidationError, ProviderError, OAuthError, ServiceUnavailableError;
160
+ var init_errors = __esm(() => {
161
+ AppError = class AppError extends Error {
162
+ code;
163
+ statusCode;
164
+ timestamp;
165
+ context;
166
+ constructor(message, code, statusCode, context) {
167
+ super(message);
168
+ this.code = code;
169
+ this.statusCode = statusCode;
170
+ this.name = this.constructor.name;
171
+ this.timestamp = new Date;
172
+ this.context = context;
173
+ Error.captureStackTrace(this, this.constructor);
174
+ }
175
+ toJSON() {
176
+ return {
177
+ name: this.name,
178
+ message: this.message,
179
+ code: this.code,
180
+ statusCode: this.statusCode,
181
+ timestamp: this.timestamp,
182
+ context: this.context
183
+ };
184
+ }
185
+ };
186
+ AuthError = class AuthError extends AppError {
187
+ constructor(message, context) {
188
+ super(message, "AUTH_ERROR", 401, context);
189
+ }
190
+ };
191
+ TokenRefreshError = class TokenRefreshError extends AuthError {
192
+ originalError;
193
+ constructor(accountId, originalError) {
194
+ super("Failed to refresh access token", {
195
+ accountId,
196
+ originalError: originalError?.message
197
+ });
198
+ this.originalError = originalError;
199
+ }
200
+ };
201
+ RateLimitError = class RateLimitError extends AppError {
202
+ accountId;
203
+ resetTime;
204
+ remaining;
205
+ constructor(accountId, resetTime, remaining) {
206
+ super("Rate limit exceeded", "RATE_LIMIT_ERROR", 429, {
207
+ accountId,
208
+ resetTime,
209
+ remaining
210
+ });
211
+ this.accountId = accountId;
212
+ this.resetTime = resetTime;
213
+ this.remaining = remaining;
214
+ }
215
+ };
216
+ ValidationError = class ValidationError extends AppError {
217
+ field;
218
+ value;
219
+ constructor(message, field, value) {
220
+ super(message, "VALIDATION_ERROR", 400, { field, value });
221
+ this.field = field;
222
+ this.value = value;
223
+ }
224
+ };
225
+ ProviderError = class ProviderError extends AppError {
226
+ provider;
227
+ constructor(message, provider, statusCode = 502, context) {
228
+ super(message, "PROVIDER_ERROR", statusCode, { provider, ...context });
229
+ this.provider = provider;
230
+ }
231
+ };
232
+ OAuthError = class OAuthError extends ProviderError {
233
+ oauthCode;
234
+ constructor(message, provider, oauthCode) {
235
+ super(message, provider, 400, { oauthCode });
236
+ this.oauthCode = oauthCode;
237
+ }
238
+ };
239
+ ServiceUnavailableError = class ServiceUnavailableError extends AppError {
240
+ service;
241
+ constructor(message, service) {
242
+ super(message, "SERVICE_UNAVAILABLE", 503, { service });
243
+ this.service = service;
244
+ }
245
+ };
246
+ });
247
+
248
+ // ../../packages/core/src/formatters.ts
249
+ function formatCost(cost) {
250
+ if (!cost || cost === 0)
251
+ return "$0.0000";
252
+ return `$${cost.toFixed(4)}`;
253
+ }
254
+
255
+ // ../../packages/core/src/lifecycle.ts
256
+ class LifecycleManager {
257
+ disposables = new Set;
258
+ isShuttingDown = false;
259
+ register(disposable) {
260
+ this.disposables.add(disposable);
261
+ }
262
+ unregister(disposable) {
263
+ this.disposables.delete(disposable);
264
+ }
265
+ async shutdown() {
266
+ if (this.isShuttingDown) {
267
+ return;
268
+ }
269
+ this.isShuttingDown = true;
270
+ const errors = [];
271
+ const disposableArray = Array.from(this.disposables).reverse();
272
+ for (const disposable of disposableArray) {
273
+ try {
274
+ await disposable.dispose();
275
+ } catch (error) {
276
+ errors.push(error instanceof Error ? error : new Error(`Disposal error: ${String(error)}`));
277
+ }
278
+ }
279
+ this.disposables.clear();
280
+ this.isShuttingDown = false;
281
+ if (errors.length > 0) {
282
+ throw new AggregateError(errors, "Errors occurred during shutdown");
283
+ }
284
+ }
285
+ }
286
+ function registerDisposable(disposable) {
287
+ lifecycleManager.register(disposable);
288
+ }
289
+ function unregisterDisposable(disposable) {
290
+ lifecycleManager.unregister(disposable);
291
+ }
292
+ async function shutdown() {
293
+ await lifecycleManager.shutdown();
294
+ }
295
+ var lifecycleManager;
296
+ var init_lifecycle = __esm(() => {
297
+ lifecycleManager = new LifecycleManager;
298
+ });
299
+
300
+ // ../../packages/core/src/models.ts
301
+ function normalizeModelId(modelId) {
302
+ return modelId.trim().replace(PROVIDER_PREFIX_RE, "").replace(LATEST_SUFFIX_RE, "").replace(DATE_SUFFIX_RE, "");
303
+ }
304
+ function getModelShortName(modelId) {
305
+ const normalized = normalizeModelId(modelId);
306
+ return normalized || modelId;
307
+ }
308
+ var PROVIDER_PREFIX_RE, DATE_SUFFIX_RE, LATEST_SUFFIX_RE;
309
+ var init_models = __esm(() => {
310
+ PROVIDER_PREFIX_RE = /^(anthropic|openai|codex|claude-code|google|xai|zai)\//;
311
+ DATE_SUFFIX_RE = /-\d{8}$/;
312
+ LATEST_SUFFIX_RE = /-latest$/;
313
+ });
314
+
315
+ // ../../packages/core/src/pricing.ts
316
+ import { promises as fs } from "fs";
317
+ import { tmpdir } from "os";
318
+ import { join } from "path";
319
+
320
+ class PriceCatalogue {
321
+ static instance;
322
+ priceData = null;
323
+ lastFetch = 0;
324
+ warnedModels = new Set;
325
+ logger = null;
326
+ constructor() {}
327
+ setLogger(logger) {
328
+ this.logger = logger;
329
+ }
330
+ static get() {
331
+ if (!PriceCatalogue.instance) {
332
+ PriceCatalogue.instance = new PriceCatalogue;
333
+ }
334
+ return PriceCatalogue.instance;
335
+ }
336
+ getCacheDir() {
337
+ return join(tmpdir(), "ccflare");
338
+ }
339
+ getCachePath() {
340
+ return join(this.getCacheDir(), "models.dev.json");
341
+ }
342
+ getCacheDurationMs() {
343
+ const hours = Number(process.env.CF_PRICING_REFRESH_HOURS) || 24;
344
+ return hours * TIME_CONSTANTS.HOUR;
345
+ }
346
+ async ensureCacheDir() {
347
+ try {
348
+ await fs.mkdir(this.getCacheDir(), { recursive: true });
349
+ } catch (error) {
350
+ this.logger?.warn("Failed to create cache directory: %s", error);
351
+ }
352
+ }
353
+ async loadFromCache() {
354
+ try {
355
+ const cachePath = this.getCachePath();
356
+ const stats = await fs.stat(cachePath);
357
+ const age = Date.now() - stats.mtime.getTime();
358
+ if (age < this.getCacheDurationMs()) {
359
+ const content = await fs.readFile(cachePath, "utf-8");
360
+ return JSON.parse(content);
361
+ }
362
+ } catch {}
363
+ return null;
364
+ }
365
+ async saveToCache(data) {
366
+ try {
367
+ await this.ensureCacheDir();
368
+ const cachePath = this.getCachePath();
369
+ await fs.writeFile(cachePath, JSON.stringify(data, null, 2));
370
+ } catch (error) {
371
+ this.logger?.warn("Failed to save pricing cache: %s", error);
372
+ }
373
+ }
374
+ async fetchRemote() {
375
+ try {
376
+ const response = await fetch("https://models.dev/api.json");
377
+ if (!response.ok) {
378
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
379
+ }
380
+ const data = await response.json();
381
+ await this.saveToCache(data);
382
+ return data;
383
+ } catch (error) {
384
+ this.logger?.warn("Failed to fetch pricing data: %s", error);
385
+ return null;
386
+ }
387
+ }
388
+ async getPricing() {
389
+ if (this.priceData && Date.now() - this.lastFetch < this.getCacheDurationMs()) {
390
+ return this.priceData;
391
+ }
392
+ let data = await this.fetchRemote();
393
+ if (!data) {
394
+ data = await this.loadFromCache();
395
+ }
396
+ if (!data) {
397
+ data = {};
398
+ }
399
+ this.priceData = data;
400
+ this.lastFetch = Date.now();
401
+ return data;
402
+ }
403
+ warnOnce(modelId) {
404
+ if (!this.warnedModels.has(modelId)) {
405
+ this.warnedModels.add(modelId);
406
+ this.logger?.warn("Price for model %s not found - cost set to 0", modelId);
407
+ }
408
+ }
409
+ }
410
+ function setPricingLogger(logger) {
411
+ PriceCatalogue.get().setLogger(logger);
412
+ }
413
+ function findModelCost(pricing, modelId) {
414
+ for (const provider of Object.values(pricing)) {
415
+ const model = provider.models?.[modelId];
416
+ if (model?.cost) {
417
+ return model.cost;
418
+ }
419
+ }
420
+ return null;
421
+ }
422
+ async function estimateCostUSD(modelId, tokens) {
423
+ const catalogue = PriceCatalogue.get();
424
+ try {
425
+ const pricing = await catalogue.getPricing();
426
+ const normalizedModelId = modelId.trim();
427
+ if (!normalizedModelId) {
428
+ throw new Error("Model id is empty");
429
+ }
430
+ const cost = findModelCost(pricing, normalizedModelId);
431
+ if (!cost) {
432
+ throw new Error(`Model ${normalizedModelId} not found in pricing catalogue`);
433
+ }
434
+ let totalCost = 0;
435
+ if (tokens.inputTokens && cost.input !== undefined) {
436
+ totalCost += tokens.inputTokens * (cost.input / 1e6);
437
+ }
438
+ if (tokens.outputTokens && cost.output !== undefined) {
439
+ totalCost += tokens.outputTokens * (cost.output / 1e6);
440
+ }
441
+ if (tokens.cacheReadInputTokens && cost.cache_read !== undefined) {
442
+ totalCost += tokens.cacheReadInputTokens * (cost.cache_read / 1e6);
443
+ }
444
+ if (tokens.cacheCreationInputTokens && cost.cache_write !== undefined) {
445
+ totalCost += tokens.cacheCreationInputTokens * (cost.cache_write / 1e6);
446
+ }
447
+ return totalCost;
448
+ } catch (_error) {
449
+ catalogue.warnOnce(modelId);
450
+ return 0;
451
+ }
452
+ }
453
+ var init_pricing = __esm(() => {
454
+ init_constants();
455
+ });
456
+
457
+ // ../../packages/core/src/request-events.ts
458
+ import { EventEmitter } from "events";
459
+ var RequestEventBus, requestEvents;
460
+ var init_request_events = __esm(() => {
461
+ RequestEventBus = class RequestEventBus extends EventEmitter {
462
+ };
463
+ requestEvents = new RequestEventBus;
464
+ });
465
+
466
+ // ../../packages/types/src/account.ts
467
+ function normalizeRateLimitCode(account, now) {
468
+ if (account.paused) {
469
+ return "paused";
470
+ }
471
+ if (account.rate_limit_status) {
472
+ return account.rate_limit_status;
473
+ }
474
+ if (account.rate_limited_until && account.rate_limited_until > now) {
475
+ return "rate_limited";
476
+ }
477
+ return "ok";
478
+ }
479
+ function getAccountRateLimitInfo(account, now = Date.now()) {
480
+ const limitedUntil = account.rate_limited_until && account.rate_limited_until > now ? account.rate_limited_until : null;
481
+ return {
482
+ code: normalizeRateLimitCode(account, now),
483
+ isLimited: account.paused ? false : limitedUntil !== null,
484
+ until: limitedUntil,
485
+ resetAt: account.rate_limit_reset ?? null,
486
+ remaining: account.rate_limit_remaining ?? null
487
+ };
488
+ }
489
+ function getAccountSessionInfo(account) {
490
+ return {
491
+ active: account.session_start !== null,
492
+ startedAt: account.session_start ?? null,
493
+ requestCount: account.session_request_count
494
+ };
495
+ }
496
+ function getAccountTokenStatus(account, now = Date.now()) {
497
+ if (account.expires_at !== null) {
498
+ return account.expires_at > now ? "valid" : "expired";
499
+ }
500
+ return account.access_token ? "valid" : "expired";
501
+ }
502
+ // ../../packages/types/src/conversation.ts
503
+ var init_conversation = () => {};
504
+
505
+ // ../../packages/types/src/guards.ts
506
+ function isRecord(value) {
507
+ return typeof value === "object" && value !== null && !Array.isArray(value);
508
+ }
509
+ function isFiniteNumber(value) {
510
+ return typeof value === "number" && Number.isFinite(value);
511
+ }
512
+
513
+ // ../../packages/types/src/conversation-links.ts
514
+ function decodeBase64Utf8(value) {
515
+ if (!value || value === "[streamed]") {
516
+ return null;
517
+ }
518
+ try {
519
+ if (typeof Buffer !== "undefined") {
520
+ return Buffer.from(value, "base64").toString("utf8");
521
+ }
522
+ if (typeof atob === "function") {
523
+ const binary = atob(value);
524
+ const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
525
+ return new TextDecoder("utf-8").decode(bytes);
526
+ }
527
+ } catch {}
528
+ return null;
529
+ }
530
+ function extractResponseIdFromRecord(record) {
531
+ if (typeof record.id === "string") {
532
+ return record.id;
533
+ }
534
+ if (isRecord(record.response) && typeof record.response.id === "string") {
535
+ return record.response.id;
536
+ }
537
+ return null;
538
+ }
539
+ function extractPreviousResponseId(requestBody) {
540
+ if (!requestBody) {
541
+ return null;
542
+ }
543
+ try {
544
+ const parsed = JSON.parse(requestBody);
545
+ if (!isRecord(parsed)) {
546
+ return null;
547
+ }
548
+ return typeof parsed.previous_response_id === "string" ? parsed.previous_response_id : null;
549
+ } catch {
550
+ return null;
551
+ }
552
+ }
553
+ function extractResponseId(responseBody) {
554
+ if (!responseBody) {
555
+ return null;
556
+ }
557
+ try {
558
+ const parsed = JSON.parse(responseBody);
559
+ if (isRecord(parsed)) {
560
+ return extractResponseIdFromRecord(parsed);
561
+ }
562
+ } catch {}
563
+ for (const line of responseBody.split(`
564
+ `)) {
565
+ if (!line.startsWith("data:")) {
566
+ continue;
567
+ }
568
+ const dataStr = line.slice(5).trim();
569
+ if (!dataStr || dataStr === "[DONE]") {
570
+ continue;
571
+ }
572
+ try {
573
+ const parsed = JSON.parse(dataStr);
574
+ if (isRecord(parsed)) {
575
+ const responseId = extractResponseIdFromRecord(parsed);
576
+ if (responseId) {
577
+ return responseId;
578
+ }
579
+ }
580
+ } catch {}
581
+ }
582
+ return null;
583
+ }
584
+ function extractClientSessionIdFromHeaders(headers) {
585
+ if (!headers) {
586
+ return null;
587
+ }
588
+ const value = headers[CLAUDE_CODE_SESSION_ID_HEADER];
589
+ return typeof value === "string" ? value : null;
590
+ }
591
+ function extractRequestLinkageFromPayload(payload) {
592
+ if (!isRecord(payload)) {
593
+ return {
594
+ previousResponseId: null,
595
+ responseId: null,
596
+ clientSessionId: null
597
+ };
598
+ }
599
+ const requestBody = isRecord(payload.request) ? decodeBase64Utf8(typeof payload.request.body === "string" ? payload.request.body : null) : null;
600
+ const responseBody = isRecord(payload.response) ? decodeBase64Utf8(typeof payload.response.body === "string" ? payload.response.body : null) : null;
601
+ const requestHeaders = isRecord(payload.request) && isRecord(payload.request.headers) ? payload.request.headers : null;
602
+ return {
603
+ previousResponseId: extractPreviousResponseId(requestBody),
604
+ responseId: extractResponseId(responseBody),
605
+ clientSessionId: extractClientSessionIdFromHeaders(requestHeaders)
606
+ };
607
+ }
608
+ var CLAUDE_CODE_SESSION_ID_HEADER = "x-claude-code-session-id";
609
+ var init_conversation_links = () => {};
610
+
611
+ // ../../packages/types/src/logging.ts
612
+ function isLogLevel(value) {
613
+ return LOG_LEVELS.includes(value);
614
+ }
615
+ function isLogEvent(value) {
616
+ return isRecord(value) && isFiniteNumber(value.ts) && typeof value.level === "string" && isLogLevel(value.level) && typeof value.msg === "string";
617
+ }
618
+ var LOG_LEVELS;
619
+ var init_logging = __esm(() => {
620
+ LOG_LEVELS = ["DEBUG", "INFO", "WARN", "ERROR"];
621
+ });
622
+
623
+ // ../../packages/types/src/provider-metadata.ts
624
+ function isAccountProvider(value) {
625
+ return Object.hasOwn(PROVIDER_METADATA_MAP, value);
626
+ }
627
+ function isAuthMethod(value) {
628
+ return AUTH_METHODS.includes(value);
629
+ }
630
+ function isProviderWire(value) {
631
+ return PROVIDER_WIRES.includes(value);
632
+ }
633
+ function isApiKeyProvider(value) {
634
+ return isAccountProvider(value) && PROVIDER_METADATA_MAP[value].authMethod === "api_key";
635
+ }
636
+ function isOAuthProvider(value) {
637
+ return isAccountProvider(value) && PROVIDER_METADATA_MAP[value].authMethod === "oauth";
638
+ }
639
+ function getProviderDisplayLabel(provider) {
640
+ return isAccountProvider(provider) ? PROVIDER_METADATA_MAP[provider].displayLabel : provider;
641
+ }
642
+ function getProviderDefaultBaseUrl(provider) {
643
+ return PROVIDER_METADATA_MAP[provider].defaultBaseUrl;
644
+ }
645
+ var PROVIDER_WIRES, PROVIDER_METADATA_MAP, providerNames, authMethods, providerOptions, ACCOUNT_PROVIDERS, AUTH_METHODS, API_KEY_PROVIDERS, OAUTH_PROVIDERS;
646
+ var init_provider_metadata = __esm(() => {
647
+ PROVIDER_WIRES = [
648
+ "anthropic-messages",
649
+ "openai-responses"
650
+ ];
651
+ PROVIDER_METADATA_MAP = {
652
+ anthropic: {
653
+ displayLabel: "Anthropic",
654
+ authMethod: "api_key",
655
+ supportsOAuth: false,
656
+ supportsWebSocket: false,
657
+ defaultBaseUrl: "https://api.anthropic.com",
658
+ specialRequirements: []
659
+ },
660
+ openai: {
661
+ displayLabel: "OpenAI",
662
+ authMethod: "api_key",
663
+ supportsOAuth: false,
664
+ supportsWebSocket: false,
665
+ defaultBaseUrl: "https://api.openai.com/v1",
666
+ specialRequirements: []
667
+ },
668
+ "claude-code": {
669
+ displayLabel: "Claude Code",
670
+ authMethod: "oauth",
671
+ supportsOAuth: true,
672
+ supportsWebSocket: false,
673
+ defaultBaseUrl: "https://api.anthropic.com",
674
+ specialRequirements: ["Requires Claude Code OAuth authentication"]
675
+ },
676
+ codex: {
677
+ displayLabel: "Codex",
678
+ authMethod: "oauth",
679
+ supportsOAuth: true,
680
+ supportsWebSocket: true,
681
+ defaultBaseUrl: "https://chatgpt.com/backend-api/codex",
682
+ specialRequirements: ["Requires Codex OAuth authentication"]
683
+ }
684
+ };
685
+ providerNames = Object.freeze(Object.keys(PROVIDER_METADATA_MAP));
686
+ authMethods = Object.freeze(Array.from(new Set(providerNames.map((provider) => PROVIDER_METADATA_MAP[provider].authMethod))));
687
+ providerOptions = Object.freeze(providerNames.map((provider) => ({
688
+ value: provider,
689
+ label: PROVIDER_METADATA_MAP[provider].displayLabel
690
+ })));
691
+ ACCOUNT_PROVIDERS = providerNames;
692
+ AUTH_METHODS = authMethods;
693
+ API_KEY_PROVIDERS = Object.freeze(ACCOUNT_PROVIDERS.filter((provider) => isApiKeyProvider(provider)));
694
+ OAUTH_PROVIDERS = Object.freeze(ACCOUNT_PROVIDERS.filter((provider) => isOAuthProvider(provider)));
695
+ });
696
+
697
+ // ../../packages/types/src/quota.ts
698
+ function quotaThresholdFor(account, bucket, fleetThreshold) {
699
+ const threshold = account.switch_threshold;
700
+ if (typeof threshold === "number")
701
+ return threshold;
702
+ return threshold?.[bucket] ?? threshold?.default ?? fleetThreshold;
703
+ }
704
+ function isCurrentQuotaWindow(window, now) {
705
+ return (window.resetAt === null || window.resetAt > now) && now - window.seenAt <= QUOTA_STALE_MS;
706
+ }
707
+ var QUOTA_STALE_MS;
708
+ var init_quota = __esm(() => {
709
+ QUOTA_STALE_MS = 30 * 60000;
710
+ });
711
+
712
+ // ../../packages/types/src/request.ts
713
+ function isOptionalNullableNumber(value) {
714
+ return value === undefined || value === null || isFiniteNumber(value);
715
+ }
716
+ function isNullableNumber(value) {
717
+ return value === null || isFiniteNumber(value);
718
+ }
719
+ function isOptionalString(value) {
720
+ return value === undefined || typeof value === "string";
721
+ }
722
+ function isOptionalNullableString(value) {
723
+ return value === undefined || value === null || typeof value === "string";
724
+ }
725
+ function isNullableString(value) {
726
+ return value === null || typeof value === "string";
727
+ }
728
+ function isNullableBoolean(value) {
729
+ return value === null || typeof value === "boolean";
730
+ }
731
+ function isStringRecord(value) {
732
+ return isRecord(value) && Object.values(value).every((entry) => typeof entry === "string");
733
+ }
734
+ function isTokenCurveSampleArray(value) {
735
+ return Array.isArray(value) && value.every((sample) => isRecord(sample) && isFiniteNumber(sample.chunkIndex) && isFiniteNumber(sample.tokenDelta) && isFiniteNumber(sample.timestamp));
736
+ }
737
+ function isHttpMethod(value) {
738
+ return HTTP_METHODS.includes(value);
739
+ }
740
+ function toRequestSummary(request) {
741
+ return {
742
+ id: request.id,
743
+ timestamp: new Date(request.timestamp).toISOString(),
744
+ method: request.method,
745
+ path: request.path,
746
+ provider: request.provider,
747
+ upstreamPath: request.upstreamPath,
748
+ accountUsed: request.accountUsed,
749
+ accountName: null,
750
+ statusCode: request.statusCode,
751
+ success: request.success,
752
+ errorMessage: request.errorMessage,
753
+ responseTimeMs: request.responseTimeMs,
754
+ failoverAttempts: request.failoverAttempts,
755
+ model: request.model ?? null,
756
+ promptTokens: request.promptTokens ?? null,
757
+ completionTokens: request.completionTokens ?? null,
758
+ totalTokens: request.totalTokens ?? null,
759
+ inputTokens: request.inputTokens ?? null,
760
+ cacheReadInputTokens: request.cacheReadInputTokens ?? null,
761
+ cacheCreationInputTokens: request.cacheCreationInputTokens ?? null,
762
+ outputTokens: request.outputTokens ?? null,
763
+ reasoningTokens: request.reasoningTokens ?? null,
764
+ costUsd: request.costUsd ?? null,
765
+ tokensPerSecond: request.tokensPerSecond ?? null,
766
+ ttftMs: request.ttftMs ?? null,
767
+ proxyOverheadMs: request.proxyOverheadMs ?? null,
768
+ upstreamTtfbMs: request.upstreamTtfbMs ?? null,
769
+ streamingDurationMs: request.streamingDurationMs ?? null,
770
+ responseId: request.responseId ?? null,
771
+ previousResponseId: request.previousResponseId ?? null,
772
+ responseChainId: request.responseChainId ?? null,
773
+ clientSessionId: request.clientSessionId ?? null
774
+ };
775
+ }
776
+ function isRequestSummary(value) {
777
+ if (!isRecord(value)) {
778
+ return false;
779
+ }
780
+ return typeof value.id === "string" && typeof value.timestamp === "string" && typeof value.method === "string" && isHttpMethod(value.method) && typeof value.path === "string" && typeof value.provider === "string" && value.provider.length > 0 && typeof value.upstreamPath === "string" && (value.accountUsed === null || typeof value.accountUsed === "string") && (value.accountName === null || typeof value.accountName === "string") && isOptionalNullableNumber(value.statusCode) && isNullableBoolean(value.success) && isOptionalNullableString(value.errorMessage) && isOptionalNullableNumber(value.responseTimeMs) && isFiniteNumber(value.failoverAttempts) && isNullableString(value.model) && isNullableNumber(value.promptTokens) && isNullableNumber(value.completionTokens) && isNullableNumber(value.totalTokens) && isNullableNumber(value.inputTokens) && isNullableNumber(value.cacheReadInputTokens) && isNullableNumber(value.cacheCreationInputTokens) && isNullableNumber(value.outputTokens) && isNullableNumber(value.reasoningTokens) && isNullableNumber(value.costUsd) && isNullableNumber(value.tokensPerSecond) && isNullableNumber(value.ttftMs) && isNullableNumber(value.proxyOverheadMs) && isNullableNumber(value.upstreamTtfbMs) && isNullableNumber(value.streamingDurationMs) && isNullableString(value.responseId) && isNullableString(value.previousResponseId) && isNullableString(value.responseChainId) && isNullableString(value.clientSessionId);
781
+ }
782
+ function isRequestPayload(value) {
783
+ if (!isRecord(value) || !isRecord(value.request) || !isRecord(value.meta) || !isRecord(value.meta.trace) || !isRecord(value.meta.account) || !isRecord(value.meta.transport)) {
784
+ return false;
785
+ }
786
+ const { request, response, meta } = value;
787
+ const trace = meta.trace;
788
+ const account = meta.account;
789
+ const transport = meta.transport;
790
+ if (!isStringRecord(request.headers) || isOptionalNullableString(request.body) === false) {
791
+ return false;
792
+ }
793
+ if (response !== null && (!isRecord(response) || !isFiniteNumber(response.status) || !isStringRecord(response.headers) || isOptionalNullableString(response.body) === false)) {
794
+ return false;
795
+ }
796
+ return typeof value.id === "string" && isOptionalString(value.error) && isFiniteNumber(trace.timestamp) && (trace.provider === undefined || typeof trace.provider === "string" && trace.provider.length > 0) && (trace.upstreamPath === undefined || typeof trace.upstreamPath === "string") && (trace.path === undefined || typeof trace.path === "string") && (trace.method === undefined || typeof trace.method === "string" && isHttpMethod(trace.method)) && isOptionalNullableString(trace.responseId) && isOptionalNullableString(trace.previousResponseId) && isOptionalNullableString(trace.responseChainId) && isOptionalNullableString(trace.clientSessionId) && (account.id === null || typeof account.id === "string") && (account.name === undefined || account.name === null || typeof account.name === "string") && (transport.success === undefined || typeof transport.success === "boolean") && (transport.rateLimited === undefined || typeof transport.rateLimited === "boolean") && (transport.accountsAttempted === undefined || isFiniteNumber(transport.accountsAttempted)) && (transport.pending === undefined || typeof transport.pending === "boolean") && (transport.retry === undefined || isFiniteNumber(transport.retry)) && (transport.isStream === undefined || typeof transport.isStream === "boolean") && isOptionalNullableNumber(transport.ttftMs) && isOptionalNullableNumber(transport.proxyOverheadMs) && isOptionalNullableNumber(transport.upstreamTtfbMs) && isOptionalNullableNumber(transport.streamingDurationMs) && (transport.tokenCurve === undefined || transport.tokenCurve === null || isTokenCurveSampleArray(transport.tokenCurve));
797
+ }
798
+ function parseRequestPayload(value) {
799
+ if (isRequestPayload(value)) {
800
+ return value;
801
+ }
802
+ return null;
803
+ }
804
+ var HTTP_METHODS, NO_ACCOUNT_ID = "no_account";
805
+ var init_request = __esm(() => {
806
+ HTTP_METHODS = Object.freeze([
807
+ "GET",
808
+ "POST",
809
+ "PUT",
810
+ "PATCH",
811
+ "DELETE",
812
+ "OPTIONS",
813
+ "HEAD",
814
+ "WS"
815
+ ]);
816
+ });
817
+
818
+ // ../../packages/types/src/request-events.ts
819
+ function isRequestStreamEvent(value) {
820
+ if (!isRecord(value) || typeof value.type !== "string") {
821
+ return false;
822
+ }
823
+ switch (value.type) {
824
+ case "ingress":
825
+ return typeof value.id === "string" && isFiniteNumber(value.timestamp) && typeof value.method === "string" && isHttpMethod(value.method) && typeof value.path === "string";
826
+ case "start":
827
+ return typeof value.id === "string" && isFiniteNumber(value.timestamp) && typeof value.method === "string" && isHttpMethod(value.method) && typeof value.path === "string" && (value.accountId === null || typeof value.accountId === "string") && isFiniteNumber(value.statusCode);
828
+ case "summary":
829
+ return isRequestSummary(value.payload);
830
+ case "payload":
831
+ return isRequestPayload(value.payload);
832
+ default:
833
+ return false;
834
+ }
835
+ }
836
+ var init_request_events2 = __esm(() => {
837
+ init_request();
838
+ });
839
+
840
+ // ../../packages/types/src/stats.ts
841
+ function isTimeRange(value) {
842
+ return TIME_RANGES.includes(value);
843
+ }
844
+ function isAnalyticsMode(value) {
845
+ return ANALYTICS_MODES.includes(value);
846
+ }
847
+ function isAnalyticsStatusFilter(value) {
848
+ return ANALYTICS_STATUS_FILTERS.includes(value);
849
+ }
850
+ var TIME_RANGES, ANALYTICS_MODES, ANALYTICS_STATUS_FILTERS;
851
+ var init_stats = __esm(() => {
852
+ TIME_RANGES = Object.freeze([
853
+ "1h",
854
+ "6h",
855
+ "24h",
856
+ "7d",
857
+ "30d"
858
+ ]);
859
+ ANALYTICS_MODES = Object.freeze([
860
+ "normal",
861
+ "cumulative"
862
+ ]);
863
+ ANALYTICS_STATUS_FILTERS = Object.freeze([
864
+ "all",
865
+ "success",
866
+ "error"
867
+ ]);
868
+ });
869
+
870
+ // ../../packages/types/src/strategy.ts
871
+ function isLbStrategy(value) {
872
+ return LB_STRATEGIES.includes(value);
873
+ }
874
+ var StrategyName, LB_STRATEGIES;
875
+ var init_strategy = __esm(() => {
876
+ StrategyName = {
877
+ Session: "session"
878
+ };
879
+ LB_STRATEGIES = Object.freeze(Object.values(StrategyName));
880
+ });
881
+
882
+ // ../../packages/types/src/index.ts
883
+ var init_src = __esm(() => {
884
+ init_conversation();
885
+ init_conversation_links();
886
+ init_logging();
887
+ init_provider_metadata();
888
+ init_quota();
889
+ init_request();
890
+ init_request_events2();
891
+ init_stats();
892
+ init_strategy();
893
+ });
894
+
895
+ // ../../packages/core/src/strategy.ts
896
+ function isValidStrategy(strategy2) {
897
+ return isLbStrategy(strategy2);
898
+ }
899
+ function isAccountAvailable(account2, now = Date.now()) {
900
+ return !account2.paused && (!account2.rate_limited_until || account2.rate_limited_until < now);
901
+ }
902
+ var DEFAULT_STRATEGY;
903
+ var init_strategy2 = __esm(() => {
904
+ init_src();
905
+ DEFAULT_STRATEGY = StrategyName.Session;
906
+ });
907
+
908
+ // ../../packages/core/src/validation.ts
909
+ function validateString(value, field, options = {}) {
910
+ if (value === undefined || value === null) {
911
+ if (options.required) {
912
+ throw new ValidationError(`${field} is required`, field);
913
+ }
914
+ return;
915
+ }
916
+ if (typeof value !== "string") {
917
+ throw new ValidationError(`${field} must be a string`, field, value);
918
+ }
919
+ const sanitized = options.transform ? options.transform(value) : value;
920
+ if (options.minLength !== undefined && sanitized.length < options.minLength) {
921
+ throw new ValidationError(`${field} must be at least ${options.minLength} characters long`, field, value);
922
+ }
923
+ if (options.maxLength !== undefined && sanitized.length > options.maxLength) {
924
+ throw new ValidationError(`${field} must be at most ${options.maxLength} characters long`, field, value);
925
+ }
926
+ if (options.pattern && !options.pattern.test(sanitized)) {
927
+ throw new ValidationError(`${field} has an invalid format`, field, value);
928
+ }
929
+ if (options.allowedValues && !options.allowedValues.includes(sanitized)) {
930
+ throw new ValidationError(`${field} must be one of: ${options.allowedValues.join(", ")}`, field, value);
931
+ }
932
+ return sanitized;
933
+ }
934
+ function validateNumber(value, field, options = {}) {
935
+ if (value === undefined || value === null) {
936
+ if (options.required) {
937
+ throw new ValidationError(`${field} is required`, field);
938
+ }
939
+ return;
940
+ }
941
+ let num;
942
+ if (typeof value === "string") {
943
+ num = Number(value);
944
+ if (Number.isNaN(num)) {
945
+ throw new ValidationError(`${field} must be a valid number`, field, value);
946
+ }
947
+ } else if (typeof value === "number") {
948
+ num = value;
949
+ } else {
950
+ throw new ValidationError(`${field} must be a number`, field, value);
951
+ }
952
+ if (options.integer && !Number.isInteger(num)) {
953
+ throw new ValidationError(`${field} must be an integer`, field, value);
954
+ }
955
+ if (options.min !== undefined && num < options.min) {
956
+ throw new ValidationError(`${field} must be at least ${options.min}`, field, value);
957
+ }
958
+ if (options.max !== undefined && num > options.max) {
959
+ throw new ValidationError(`${field} must be at most ${options.max}`, field, value);
960
+ }
961
+ if (options.allowedValues && !options.allowedValues.includes(num)) {
962
+ throw new ValidationError(`${field} must be one of: ${options.allowedValues.join(", ")}`, field, value);
963
+ }
964
+ return num;
965
+ }
966
+ var sanitizers, patterns;
967
+ var init_validation = __esm(() => {
968
+ init_errors();
969
+ sanitizers = {
970
+ trim: (value) => value.trim(),
971
+ lowercase: (value) => value.toLowerCase(),
972
+ uppercase: (value) => value.toUpperCase(),
973
+ removeWhitespace: (value) => value.replace(/\s+/g, ""),
974
+ alphanumeric: (value) => value.replace(/[^a-zA-Z0-9]/g, ""),
975
+ alphanumericWithSpaces: (value) => value.replace(/[^a-zA-Z0-9\s]/g, ""),
976
+ email: (value) => value.trim().toLowerCase(),
977
+ url: (value) => {
978
+ try {
979
+ const parsed = new URL(value);
980
+ return parsed.toString();
981
+ } catch {
982
+ throw new ValidationError("Invalid URL format", "url", value);
983
+ }
984
+ }
985
+ };
986
+ patterns = {
987
+ email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
988
+ uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
989
+ alphanumeric: /^[a-zA-Z0-9]+$/,
990
+ alphanumericWithSpaces: /^[a-zA-Z0-9\s]+$/,
991
+ accountName: /^[a-zA-Z0-9\s\-_@.+]+$/,
992
+ apiPath: /^\/v1\/[a-zA-Z0-9\-_/]*$/
993
+ };
994
+ });
995
+
996
+ // ../../packages/core/src/index.ts
997
+ var init_src2 = __esm(() => {
998
+ init_constants();
999
+ init_di();
1000
+ init_errors();
1001
+ init_models();
1002
+ init_pricing();
1003
+ init_validation();
1004
+ init_lifecycle();
1005
+ init_request_events();
1006
+ init_strategy2();
1007
+ });
1008
+
1009
+ // ../../packages/database/src/analytics-query.ts
1010
+ function buildAnalyticsQuery(input) {
1011
+ const now = input.now ?? Date.now();
1012
+ const config = ANALYTICS_RANGE_CONFIG[input.range];
1013
+ return {
1014
+ meta: {
1015
+ range: input.range,
1016
+ bucket: config.bucketLabel
1017
+ },
1018
+ options: {
1019
+ startMs: now - config.durationMs,
1020
+ bucketMs: config.bucketMs,
1021
+ accounts: input.accounts,
1022
+ models: input.models,
1023
+ providers: input.providers,
1024
+ status: input.status,
1025
+ includeModelBreakdown: input.includeModelBreakdown
1026
+ }
1027
+ };
1028
+ }
1029
+ var HOUR_MS, DAY_MS, ANALYTICS_RANGE_CONFIG;
1030
+ var init_analytics_query = __esm(() => {
1031
+ HOUR_MS = 60 * 60 * 1000;
1032
+ DAY_MS = 24 * HOUR_MS;
1033
+ ANALYTICS_RANGE_CONFIG = {
1034
+ "1h": {
1035
+ durationMs: HOUR_MS,
1036
+ bucketMs: 60 * 1000,
1037
+ bucketLabel: "1m"
1038
+ },
1039
+ "6h": {
1040
+ durationMs: 6 * HOUR_MS,
1041
+ bucketMs: 5 * 60 * 1000,
1042
+ bucketLabel: "5m"
1043
+ },
1044
+ "24h": {
1045
+ durationMs: DAY_MS,
1046
+ bucketMs: HOUR_MS,
1047
+ bucketLabel: "1h"
1048
+ },
1049
+ "7d": {
1050
+ durationMs: 7 * DAY_MS,
1051
+ bucketMs: HOUR_MS,
1052
+ bucketLabel: "1h"
1053
+ },
1054
+ "30d": {
1055
+ durationMs: 30 * DAY_MS,
1056
+ bucketMs: DAY_MS,
1057
+ bucketLabel: "1d"
1058
+ }
1059
+ };
1060
+ });
1061
+
1062
+ // ../../packages/logger/src/file-writer.ts
1063
+ import {
1064
+ createWriteStream,
1065
+ existsSync,
1066
+ mkdirSync,
1067
+ statSync,
1068
+ unlinkSync
1069
+ } from "fs";
1070
+ import { tmpdir as tmpdir2 } from "os";
1071
+ import { join as join2 } from "path";
1072
+ function writeToStderr(message, error) {
1073
+ if (typeof process === "undefined") {
1074
+ return;
1075
+ }
1076
+ const formattedError = error instanceof Error ? error.stack ?? error.message : String(error);
1077
+ process.stderr?.write?.(`${message}: ${formattedError}
1078
+ `);
1079
+ }
1080
+
1081
+ class LogFileWriter {
1082
+ logDir;
1083
+ logFile;
1084
+ stream = null;
1085
+ maxFileSize = BUFFER_SIZES.LOG_FILE_MAX_SIZE;
1086
+ constructor() {
1087
+ this.logDir = join2(tmpdir2(), "ccflare-logs");
1088
+ if (!existsSync(this.logDir)) {
1089
+ mkdirSync(this.logDir, { recursive: true });
1090
+ }
1091
+ this.logFile = join2(this.logDir, "app.log");
1092
+ this.initStream();
1093
+ }
1094
+ initStream() {
1095
+ if (existsSync(this.logFile)) {
1096
+ const stats2 = statSync(this.logFile);
1097
+ if (stats2.size > this.maxFileSize) {
1098
+ this.rotateLog();
1099
+ }
1100
+ }
1101
+ this.stream = createWriteStream(this.logFile, { flags: "a" });
1102
+ }
1103
+ rotateLog() {
1104
+ if (this.stream) {
1105
+ this.stream.end();
1106
+ }
1107
+ if (existsSync(this.logFile)) {
1108
+ try {
1109
+ unlinkSync(this.logFile);
1110
+ } catch (_e) {
1111
+ writeToStderr("Failed to rotate log", _e);
1112
+ }
1113
+ }
1114
+ }
1115
+ write(event) {
1116
+ if (!this.stream || this.stream.destroyed) {
1117
+ this.initStream();
1118
+ }
1119
+ const line = `${JSON.stringify(event)}
1120
+ `;
1121
+ if (this.stream) {
1122
+ this.stream.write(line);
1123
+ }
1124
+ }
1125
+ async readLogs(limit = LIMITS.LOG_READ_DEFAULT) {
1126
+ if (!existsSync(this.logFile)) {
1127
+ return [];
1128
+ }
1129
+ try {
1130
+ const content = await Bun.file(this.logFile).text();
1131
+ const lines = content.trim().split(`
1132
+ `).filter(Boolean);
1133
+ return lines.slice(-limit).map((line) => {
1134
+ try {
1135
+ return JSON.parse(line);
1136
+ } catch {
1137
+ return null;
1138
+ }
1139
+ }).filter((log) => log !== null);
1140
+ } catch (_e) {
1141
+ writeToStderr("Failed to read logs", _e);
1142
+ return [];
1143
+ }
1144
+ }
1145
+ close() {
1146
+ if (this.stream) {
1147
+ this.stream.end();
1148
+ this.stream = null;
1149
+ }
1150
+ }
1151
+ dispose() {
1152
+ this.close();
1153
+ }
1154
+ }
1155
+ var logFileWriter;
1156
+ var init_file_writer = __esm(() => {
1157
+ init_src2();
1158
+ logFileWriter = new LogFileWriter;
1159
+ registerDisposable(logFileWriter);
1160
+ });
1161
+
1162
+ // ../../packages/logger/src/index.ts
1163
+ import { EventEmitter as EventEmitter2 } from "events";
1164
+
1165
+ class Logger {
1166
+ level;
1167
+ prefix;
1168
+ format;
1169
+ silentConsole;
1170
+ constructor(prefix = "", level = 1 /* INFO */, options = {}) {
1171
+ this.prefix = prefix;
1172
+ this.level = this.getLogLevelFromEnv() || level;
1173
+ this.format = process.env.LOG_FORMAT || "pretty";
1174
+ this.silentConsole = options.silentConsole ?? !(process.env.ccflare_DEBUG === "1" || this.level === 0 /* DEBUG */);
1175
+ }
1176
+ getLogLevelFromEnv() {
1177
+ const envLevel = process.env.LOG_LEVEL?.toUpperCase();
1178
+ if (envLevel && envLevel in LogLevel) {
1179
+ return LogLevel[envLevel];
1180
+ }
1181
+ return null;
1182
+ }
1183
+ formatMessage(level, message, data) {
1184
+ const timestamp = new Date().toISOString();
1185
+ if (this.format === "json") {
1186
+ const logEntry = {
1187
+ ts: timestamp,
1188
+ level,
1189
+ prefix: this.prefix || undefined,
1190
+ msg: message,
1191
+ ...data && { data }
1192
+ };
1193
+ return JSON.stringify(logEntry);
1194
+ } else {
1195
+ const prefix = this.prefix ? `[${this.prefix}] ` : "";
1196
+ const dataStr = data ? ` ${JSON.stringify(data)}` : "";
1197
+ return `[${timestamp}] ${level}: ${prefix}${message}${dataStr}`;
1198
+ }
1199
+ }
1200
+ debug(message, data) {
1201
+ if (this.level <= 0 /* DEBUG */) {
1202
+ const msg = this.formatMessage("DEBUG", message, data);
1203
+ const event = {
1204
+ ts: Date.now(),
1205
+ level: "DEBUG",
1206
+ msg: message
1207
+ };
1208
+ logBus.emit("log", event);
1209
+ logFileWriter.write(event);
1210
+ this.writeToStream(0 /* DEBUG */, msg);
1211
+ }
1212
+ }
1213
+ info(message, data) {
1214
+ if (this.level <= 1 /* INFO */) {
1215
+ const msg = this.formatMessage("INFO", message, data);
1216
+ const event = {
1217
+ ts: Date.now(),
1218
+ level: "INFO",
1219
+ msg: message
1220
+ };
1221
+ logBus.emit("log", event);
1222
+ logFileWriter.write(event);
1223
+ this.writeToStream(1 /* INFO */, msg);
1224
+ }
1225
+ }
1226
+ warn(message, data) {
1227
+ if (this.level <= 2 /* WARN */) {
1228
+ const msg = this.formatMessage("WARN", message, data);
1229
+ const event = {
1230
+ ts: Date.now(),
1231
+ level: "WARN",
1232
+ msg: message
1233
+ };
1234
+ logBus.emit("log", event);
1235
+ logFileWriter.write(event);
1236
+ this.writeToStream(2 /* WARN */, msg);
1237
+ }
1238
+ }
1239
+ error(message, error) {
1240
+ if (this.level <= 3 /* ERROR */) {
1241
+ const msg = this.formatMessage("ERROR", message, error);
1242
+ const event = {
1243
+ ts: Date.now(),
1244
+ level: "ERROR",
1245
+ msg: message
1246
+ };
1247
+ logBus.emit("log", event);
1248
+ logFileWriter.write(event);
1249
+ this.writeToStream(3 /* ERROR */, msg);
1250
+ }
1251
+ }
1252
+ setLevel(level) {
1253
+ this.level = level;
1254
+ this.silentConsole = !(process.env.ccflare_DEBUG === "1" || this.level === 0 /* DEBUG */);
1255
+ }
1256
+ getLevel() {
1257
+ return this.level;
1258
+ }
1259
+ writeToStream(level, message) {
1260
+ if (this.silentConsole || typeof process === "undefined") {
1261
+ return;
1262
+ }
1263
+ const stream = level >= 2 /* WARN */ ? process.stderr : process.stdout;
1264
+ stream?.write?.(`${message}
1265
+ `);
1266
+ }
1267
+ }
1268
+ var LogLevel, logBus, log;
1269
+ var init_src3 = __esm(() => {
1270
+ init_file_writer();
1271
+ init_file_writer();
1272
+ ((LogLevel2) => {
1273
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
1274
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
1275
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
1276
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
1277
+ })(LogLevel ||= {});
1278
+ logBus = new EventEmitter2;
1279
+ log = new Logger;
1280
+ });
1281
+
1282
+ // ../../packages/config/src/paths-common.ts
1283
+ import { homedir } from "os";
1284
+ import { join as join3 } from "path";
1285
+ import { platform } from "process";
1286
+ function getPlatformConfigDir() {
1287
+ if (platform === "win32") {
1288
+ const baseDir = process.env.LOCALAPPDATA ?? process.env.APPDATA ?? join3(homedir(), "AppData", "Local");
1289
+ return join3(baseDir, "ccflare");
1290
+ } else {
1291
+ const xdgConfig = process.env.XDG_CONFIG_HOME;
1292
+ const baseDir = xdgConfig ?? join3(homedir(), ".config");
1293
+ return join3(baseDir, "ccflare");
1294
+ }
1295
+ }
1296
+ var init_paths_common = () => {};
1297
+
1298
+ // ../../packages/config/src/paths.ts
1299
+ import { join as join4 } from "path";
1300
+ function resolveConfigPath() {
1301
+ const explicitPath = process.env.ccflare_CONFIG_PATH;
1302
+ if (explicitPath) {
1303
+ return explicitPath;
1304
+ }
1305
+ const configDir = getPlatformConfigDir();
1306
+ return join4(configDir, "ccflare.json");
1307
+ }
1308
+ var init_paths = __esm(() => {
1309
+ init_paths_common();
1310
+ });
1311
+
1312
+ // ../../packages/config/src/index.ts
1313
+ import { EventEmitter as EventEmitter3 } from "events";
1314
+ import {
1315
+ existsSync as existsSync2,
1316
+ mkdirSync as mkdirSync2,
1317
+ readFileSync,
1318
+ renameSync,
1319
+ rmSync,
1320
+ writeFileSync
1321
+ } from "fs";
1322
+ import { dirname } from "path";
1323
+ function parseNumber(value) {
1324
+ if (value === undefined) {
1325
+ return;
1326
+ }
1327
+ const parsed = Number(value);
1328
+ return Number.isFinite(parsed) ? parsed : undefined;
1329
+ }
1330
+ function sanitizeConfigData(value) {
1331
+ if (!isRecord(value)) {
1332
+ return {};
1333
+ }
1334
+ const sanitized = {};
1335
+ for (const [key, entry] of Object.entries(value)) {
1336
+ if (key === "team_routing") {
1337
+ const teamRouting = sanitizeTeamRouting(entry);
1338
+ if (teamRouting)
1339
+ sanitized.team_routing = teamRouting;
1340
+ continue;
1341
+ }
1342
+ if (key === "providers") {
1343
+ const providers = sanitizeProviderConfigs(entry);
1344
+ if (providers)
1345
+ sanitized.providers = providers;
1346
+ continue;
1347
+ }
1348
+ if (key === "provider_pools") {
1349
+ const providerPools = sanitizeProviderPools(entry);
1350
+ if (providerPools)
1351
+ sanitized.provider_pools = providerPools;
1352
+ continue;
1353
+ }
1354
+ if (typeof entry === "string" || typeof entry === "boolean" || isFiniteNumber(entry)) {
1355
+ sanitized[key] = entry;
1356
+ }
1357
+ }
1358
+ if (!isLbStrategy(String(sanitized.lb_strategy ?? ""))) {
1359
+ delete sanitized.lb_strategy;
1360
+ }
1361
+ const numericKeys = [
1362
+ "retry_attempts",
1363
+ "max_body_bytes",
1364
+ "quota_threshold",
1365
+ "retry_delay_ms",
1366
+ "retry_backoff",
1367
+ "request_scoped_429_retry_delay_ms",
1368
+ "session_duration_ms",
1369
+ "port",
1370
+ "data_retention_days",
1371
+ "request_retention_days"
1372
+ ];
1373
+ for (const key of numericKeys) {
1374
+ const entry = sanitized[key];
1375
+ if (entry !== undefined && !isFiniteNumber(entry)) {
1376
+ delete sanitized[key];
1377
+ }
1378
+ }
1379
+ if (sanitized.client_id !== undefined && typeof sanitized.client_id !== "string") {
1380
+ delete sanitized.client_id;
1381
+ }
1382
+ return sanitized;
1383
+ }
1384
+ function isProviderStrategy(value) {
1385
+ return typeof value === "string" && PROVIDER_STRATEGIES.includes(value);
1386
+ }
1387
+ function sanitizeProviderBaseUrl(value) {
1388
+ if (typeof value !== "string" || value.length === 0)
1389
+ return;
1390
+ try {
1391
+ const url = new URL(value);
1392
+ return url.protocol === "https:" || url.protocol === "http:" ? url.toString().replace(/\/$/, "") : undefined;
1393
+ } catch {
1394
+ return;
1395
+ }
1396
+ }
1397
+ function sanitizeProviderModels(value) {
1398
+ if (!isRecord(value))
1399
+ return;
1400
+ const models = {};
1401
+ for (const [pattern, target] of Object.entries(value)) {
1402
+ const wildcardIndex = pattern.indexOf("*");
1403
+ if (pattern.length === 0 || pattern.length > 200 || wildcardIndex !== -1 && wildcardIndex !== pattern.length - 1 || typeof target !== "string" || target.length === 0 || target.length > 200) {
1404
+ continue;
1405
+ }
1406
+ models[pattern] = target;
1407
+ }
1408
+ return Object.keys(models).length > 0 ? models : undefined;
1409
+ }
1410
+ function sanitizeProviderConfigs(value) {
1411
+ if (!isRecord(value))
1412
+ return;
1413
+ const providers = {};
1414
+ for (const [name, entry] of Object.entries(value)) {
1415
+ if (name === "pool" || !PROVIDER_NAME_PATTERN.test(name) || !isRecord(entry))
1416
+ continue;
1417
+ const builtin = BUILTIN_PROVIDER_CONFIG_BY_NAME.get(name);
1418
+ const strategy3 = isProviderStrategy(entry.strategy) ? entry.strategy : builtin?.strategy;
1419
+ if (!strategy3)
1420
+ continue;
1421
+ const provider = { strategy: strategy3 };
1422
+ if (typeof entry.enabled === "boolean")
1423
+ provider.enabled = entry.enabled;
1424
+ const baseUrl = sanitizeProviderBaseUrl(entry.base_url);
1425
+ if (baseUrl)
1426
+ provider.base_url = baseUrl;
1427
+ const models = sanitizeProviderModels(entry.models);
1428
+ if (models)
1429
+ provider.models = models;
1430
+ providers[name] = provider;
1431
+ }
1432
+ return providers;
1433
+ }
1434
+ function sanitizeProviderPools(value) {
1435
+ if (!isRecord(value))
1436
+ return;
1437
+ const pools = {};
1438
+ for (const [wire, entries] of Object.entries(value)) {
1439
+ if (!isProviderWire(wire) || !Array.isArray(entries))
1440
+ continue;
1441
+ const providers = [...new Set(entries)].filter((provider) => typeof provider === "string" && PROVIDER_NAME_PATTERN.test(provider));
1442
+ if (providers.length > 0)
1443
+ pools[wire] = providers;
1444
+ }
1445
+ return pools;
1446
+ }
1447
+ function wireForStrategy(strategy3) {
1448
+ return strategy3 === "anthropic-api" || strategy3 === "claude-code" ? "anthropic-messages" : "openai-responses";
1449
+ }
1450
+ function isNonNegativeFiniteNumber(value) {
1451
+ return isFiniteNumber(value) && value >= 0;
1452
+ }
1453
+ function sanitizeTeamRouting(value) {
1454
+ if (!isRecord(value))
1455
+ return;
1456
+ const result = {};
1457
+ if (typeof value.enabled === "boolean")
1458
+ result.enabled = value.enabled;
1459
+ if (typeof value.distribution_enabled === "boolean") {
1460
+ result.distribution_enabled = value.distribution_enabled;
1461
+ }
1462
+ if (Array.isArray(value.allowlisted_account_uuids) && value.allowlisted_account_uuids.every((entry) => typeof entry === "string" && entry.length > 0 && entry.length <= 200)) {
1463
+ result.allowlisted_account_uuids = [...value.allowlisted_account_uuids];
1464
+ }
1465
+ if (isFiniteNumber(value.max_usage) && value.max_usage > 0 && value.max_usage <= 0.9) {
1466
+ result.max_usage = value.max_usage;
1467
+ }
1468
+ if (isNonNegativeFiniteNumber(value.admission_cap)) {
1469
+ result.admission_cap = Math.floor(value.admission_cap);
1470
+ }
1471
+ if (isNonNegativeFiniteNumber(value.admission_queue_limit)) {
1472
+ result.admission_queue_limit = Math.floor(value.admission_queue_limit);
1473
+ }
1474
+ if (isNonNegativeFiniteNumber(value.admission_wait_ms)) {
1475
+ result.admission_wait_ms = Math.floor(value.admission_wait_ms);
1476
+ }
1477
+ if (isNonNegativeFiniteNumber(value.hold_seconds)) {
1478
+ result.hold_seconds = Math.floor(value.hold_seconds);
1479
+ }
1480
+ if (isNonNegativeFiniteNumber(value.quota_probe_seconds)) {
1481
+ result.quota_probe_seconds = Math.min(Math.floor(value.quota_probe_seconds), 7 * 24 * 60 * 60);
1482
+ }
1483
+ return result;
1484
+ }
1485
+ var log2, PROVIDER_STRATEGIES, BUILTIN_PROVIDER_CONFIGS, BUILTIN_PROVIDER_CONFIG_BY_NAME, PROVIDER_NAME_PATTERN, TEAM_ROUTING_DEFAULTS, Config;
1486
+ var init_src4 = __esm(() => {
1487
+ init_src2();
1488
+ init_src3();
1489
+ init_src();
1490
+ init_paths();
1491
+ init_paths();
1492
+ init_paths_common();
1493
+ log2 = new Logger("Config");
1494
+ PROVIDER_STRATEGIES = [
1495
+ "anthropic-api",
1496
+ "openai-api",
1497
+ "claude-code",
1498
+ "codex"
1499
+ ];
1500
+ BUILTIN_PROVIDER_CONFIGS = [
1501
+ { name: "anthropic", enabled: true, strategy: "anthropic-api" },
1502
+ { name: "openai", enabled: true, strategy: "openai-api" },
1503
+ { name: "claude-code", enabled: false, strategy: "claude-code" },
1504
+ { name: "codex", enabled: false, strategy: "codex" }
1505
+ ];
1506
+ BUILTIN_PROVIDER_CONFIG_BY_NAME = new Map(BUILTIN_PROVIDER_CONFIGS.map((provider) => [provider.name, provider]));
1507
+ PROVIDER_NAME_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
1508
+ TEAM_ROUTING_DEFAULTS = {
1509
+ enabled: false,
1510
+ distributionEnabled: true,
1511
+ allowlistedAccountUuids: [],
1512
+ maxUsage: 0.9,
1513
+ admissionCap: 5,
1514
+ admissionQueueLimit: 25,
1515
+ admissionWaitMs: 30000,
1516
+ holdSeconds: 0,
1517
+ quotaProbeSeconds: 0
1518
+ };
1519
+ Config = class Config extends EventEmitter3 {
1520
+ configPath;
1521
+ data = {};
1522
+ constructor(configPath) {
1523
+ super();
1524
+ this.configPath = configPath ?? resolveConfigPath();
1525
+ this.loadConfig();
1526
+ }
1527
+ loadConfig() {
1528
+ if (existsSync2(this.configPath)) {
1529
+ try {
1530
+ const content = readFileSync(this.configPath, "utf8");
1531
+ this.data = sanitizeConfigData(JSON.parse(content));
1532
+ if ("default_agent_model" in this.data) {
1533
+ delete this.data.default_agent_model;
1534
+ this.saveConfig();
1535
+ }
1536
+ } catch (error) {
1537
+ log2.error(`Failed to parse config file: ${error}`);
1538
+ this.data = {};
1539
+ }
1540
+ } else {
1541
+ const dir = dirname(this.configPath);
1542
+ mkdirSync2(dir, { recursive: true });
1543
+ this.data = {
1544
+ lb_strategy: DEFAULT_STRATEGY
1545
+ };
1546
+ this.saveConfig();
1547
+ }
1548
+ }
1549
+ saveConfig() {
1550
+ try {
1551
+ const content = JSON.stringify(this.data, null, 2);
1552
+ writeFileSync(this.configPath, content, "utf8");
1553
+ } catch (error) {
1554
+ log2.error(`Failed to save config file: ${error}`);
1555
+ }
1556
+ }
1557
+ get(key, defaultValue) {
1558
+ const value = this.data[key];
1559
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
1560
+ return value;
1561
+ }
1562
+ if (defaultValue !== undefined) {
1563
+ this.set(key, defaultValue);
1564
+ return defaultValue;
1565
+ }
1566
+ return;
1567
+ }
1568
+ set(key, value) {
1569
+ const oldValue = this.data[key];
1570
+ this.data[key] = value;
1571
+ this.saveConfig();
1572
+ this.emit("change", { key, oldValue, newValue: value });
1573
+ }
1574
+ getStrategy() {
1575
+ const envStrategy = process.env.LB_STRATEGY;
1576
+ if (envStrategy && isValidStrategy(envStrategy)) {
1577
+ return envStrategy;
1578
+ }
1579
+ const configStrategy = this.data.lb_strategy;
1580
+ if (configStrategy && isValidStrategy(configStrategy)) {
1581
+ return configStrategy;
1582
+ }
1583
+ return DEFAULT_STRATEGY;
1584
+ }
1585
+ setStrategy(strategy3) {
1586
+ if (!isValidStrategy(strategy3)) {
1587
+ throw new Error(`Invalid strategy: ${strategy3}`);
1588
+ }
1589
+ this.set("lb_strategy", strategy3);
1590
+ }
1591
+ clamp(n, min, max) {
1592
+ return Math.max(min, Math.min(max, n));
1593
+ }
1594
+ getDataRetentionDays() {
1595
+ const fromEnv = process.env.DATA_RETENTION_DAYS;
1596
+ if (fromEnv) {
1597
+ const n = parseInt(fromEnv, 10);
1598
+ if (!Number.isNaN(n))
1599
+ return this.clamp(n, 1, 365);
1600
+ }
1601
+ const fromFile = this.data.data_retention_days;
1602
+ if (typeof fromFile === "number")
1603
+ return this.clamp(fromFile, 1, 365);
1604
+ return 7;
1605
+ }
1606
+ setDataRetentionDays(days) {
1607
+ const clamped = this.clamp(days, 1, 365);
1608
+ this.set("data_retention_days", clamped);
1609
+ }
1610
+ getRequestRetentionDays() {
1611
+ const fromEnv = process.env.REQUEST_RETENTION_DAYS;
1612
+ if (fromEnv) {
1613
+ const n = parseInt(fromEnv, 10);
1614
+ if (!Number.isNaN(n))
1615
+ return this.clamp(n, 1, 3650);
1616
+ }
1617
+ const fromFile = this.data.request_retention_days;
1618
+ if (typeof fromFile === "number")
1619
+ return this.clamp(fromFile, 1, 3650);
1620
+ return 365;
1621
+ }
1622
+ setRequestRetentionDays(days) {
1623
+ const clamped = this.clamp(days, 1, 3650);
1624
+ this.set("request_retention_days", clamped);
1625
+ }
1626
+ getAllSettings() {
1627
+ const primitiveSettings = Object.fromEntries(Object.entries(this.data).filter(([, value]) => typeof value === "string" || typeof value === "number" || typeof value === "boolean"));
1628
+ return {
1629
+ ...primitiveSettings,
1630
+ lb_strategy: this.getStrategy(),
1631
+ data_retention_days: this.getDataRetentionDays(),
1632
+ request_retention_days: this.getRequestRetentionDays()
1633
+ };
1634
+ }
1635
+ getTeamRouting() {
1636
+ const file = this.data.team_routing;
1637
+ return {
1638
+ enabled: file?.enabled ?? TEAM_ROUTING_DEFAULTS.enabled,
1639
+ distributionEnabled: file?.distribution_enabled ?? TEAM_ROUTING_DEFAULTS.distributionEnabled,
1640
+ allowlistedAccountUuids: [
1641
+ ...file?.allowlisted_account_uuids ?? TEAM_ROUTING_DEFAULTS.allowlistedAccountUuids
1642
+ ],
1643
+ maxUsage: file?.max_usage ?? TEAM_ROUTING_DEFAULTS.maxUsage,
1644
+ admissionCap: file?.admission_cap ?? TEAM_ROUTING_DEFAULTS.admissionCap,
1645
+ admissionQueueLimit: file?.admission_queue_limit ?? TEAM_ROUTING_DEFAULTS.admissionQueueLimit,
1646
+ admissionWaitMs: file?.admission_wait_ms ?? TEAM_ROUTING_DEFAULTS.admissionWaitMs,
1647
+ holdSeconds: file?.hold_seconds ?? TEAM_ROUTING_DEFAULTS.holdSeconds,
1648
+ quotaProbeSeconds: file?.quota_probe_seconds ?? TEAM_ROUTING_DEFAULTS.quotaProbeSeconds
1649
+ };
1650
+ }
1651
+ getProviderConfig(name) {
1652
+ const builtin = BUILTIN_PROVIDER_CONFIG_BY_NAME.get(name);
1653
+ const configured = this.data.providers?.[name];
1654
+ if (!builtin && !configured?.strategy)
1655
+ return;
1656
+ return {
1657
+ name,
1658
+ enabled: configured?.enabled ?? builtin?.enabled ?? true,
1659
+ strategy: configured?.strategy ?? builtin?.strategy ?? "anthropic-api",
1660
+ ...configured?.base_url && { baseUrl: configured.base_url },
1661
+ ...configured?.models && { models: { ...configured.models } }
1662
+ };
1663
+ }
1664
+ getProviderPool(wire) {
1665
+ return (this.data.provider_pools?.[wire] ?? []).filter((name) => {
1666
+ const provider = this.getProviderConfig(name);
1667
+ return provider?.enabled === true && wireForStrategy(provider.strategy) === wire;
1668
+ });
1669
+ }
1670
+ getProviderPools() {
1671
+ return Object.fromEntries(PROVIDER_WIRES.map((wire) => [wire, this.getProviderPool(wire)]).filter(([, providers]) => providers.length > 0));
1672
+ }
1673
+ getProviderConfigs() {
1674
+ const names = new Set(BUILTIN_PROVIDER_CONFIGS.map(({ name }) => name));
1675
+ for (const name of Object.keys(this.data.providers ?? {})) {
1676
+ names.add(name);
1677
+ }
1678
+ return [...names].map((name) => this.getProviderConfig(name)).filter((provider) => provider !== undefined);
1679
+ }
1680
+ setTeamRouting(value) {
1681
+ if (!isRecord(value)) {
1682
+ throw new Error("team routing configuration must be an object");
1683
+ }
1684
+ const allowed = new Set([
1685
+ "enabled",
1686
+ "distribution_enabled",
1687
+ "allowlisted_account_uuids",
1688
+ "max_usage",
1689
+ "admission_cap",
1690
+ "admission_queue_limit",
1691
+ "admission_wait_ms",
1692
+ "hold_seconds",
1693
+ "quota_probe_seconds"
1694
+ ]);
1695
+ if (Object.keys(value).some((key) => !allowed.has(key))) {
1696
+ throw new Error("team routing configuration contains an unknown field");
1697
+ }
1698
+ const sanitized = sanitizeTeamRouting(value);
1699
+ if (!sanitized) {
1700
+ throw new Error("team routing configuration must be an object");
1701
+ }
1702
+ for (const key of Object.keys(value)) {
1703
+ if (!(key in sanitized)) {
1704
+ throw new Error(`Invalid team routing field: ${key}`);
1705
+ }
1706
+ }
1707
+ const oldValue = this.data.team_routing;
1708
+ this.data.team_routing = sanitized;
1709
+ this.saveConfig();
1710
+ this.emit("change", {
1711
+ key: "team_routing",
1712
+ oldValue,
1713
+ newValue: sanitized
1714
+ });
1715
+ }
1716
+ allowAccountUuid(accountUuid) {
1717
+ const current = this.getTeamRouting().allowlistedAccountUuids;
1718
+ if (current.includes(accountUuid))
1719
+ return;
1720
+ this.persistAccountAllowlist([...current, accountUuid]);
1721
+ }
1722
+ removeAccountUuid(accountUuid) {
1723
+ const current = this.getTeamRouting().allowlistedAccountUuids;
1724
+ if (!current.includes(accountUuid))
1725
+ return;
1726
+ this.persistAccountAllowlist(current.filter((uuid) => uuid !== accountUuid));
1727
+ }
1728
+ persistAccountAllowlist(accountUuids) {
1729
+ const teamRouting = {
1730
+ ...this.data.team_routing,
1731
+ allowlisted_account_uuids: accountUuids
1732
+ };
1733
+ const next = { ...this.data, team_routing: teamRouting };
1734
+ const temporary = `${this.configPath}.${crypto.randomUUID()}.tmp`;
1735
+ try {
1736
+ writeFileSync(temporary, JSON.stringify(next, null, 2), {
1737
+ mode: 384,
1738
+ flag: "wx"
1739
+ });
1740
+ renameSync(temporary, this.configPath);
1741
+ } finally {
1742
+ rmSync(temporary, { force: true });
1743
+ }
1744
+ const oldValue = this.data.team_routing;
1745
+ this.data = next;
1746
+ this.emit("change", {
1747
+ key: "team_routing",
1748
+ oldValue,
1749
+ newValue: teamRouting
1750
+ });
1751
+ }
1752
+ getRuntime() {
1753
+ const defaults = {
1754
+ clientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
1755
+ maxBodyBytes: 64 * 1024 * 1024,
1756
+ quotaThreshold: 0.98,
1757
+ requestScopedRetryDelayMs: 2000,
1758
+ retry: {
1759
+ attempts: 3,
1760
+ delayMs: TIME_CONSTANTS.RETRY_DELAY_DEFAULT,
1761
+ backoff: 2
1762
+ },
1763
+ sessionDurationMs: TIME_CONSTANTS.SESSION_DURATION_DEFAULT,
1764
+ port: NETWORK.DEFAULT_PORT,
1765
+ teamRouting: this.getTeamRouting()
1766
+ };
1767
+ if (process.env.CLIENT_ID) {
1768
+ defaults.clientId = process.env.CLIENT_ID;
1769
+ }
1770
+ const retryAttempts = parseNumber(process.env.RETRY_ATTEMPTS);
1771
+ if (retryAttempts !== undefined) {
1772
+ defaults.retry.attempts = retryAttempts;
1773
+ }
1774
+ const maxBodyBytes = parseNumber(process.env.MAX_BODY_BYTES);
1775
+ if (maxBodyBytes !== undefined) {
1776
+ defaults.maxBodyBytes = maxBodyBytes;
1777
+ }
1778
+ const quotaThreshold = parseNumber(process.env.QUOTA_THRESHOLD);
1779
+ if (quotaThreshold !== undefined && quotaThreshold > 0 && quotaThreshold <= 1) {
1780
+ defaults.quotaThreshold = quotaThreshold;
1781
+ }
1782
+ const retryDelayMs = parseNumber(process.env.RETRY_DELAY_MS);
1783
+ if (retryDelayMs !== undefined) {
1784
+ defaults.retry.delayMs = retryDelayMs;
1785
+ }
1786
+ const retryBackoff = parseNumber(process.env.RETRY_BACKOFF);
1787
+ if (retryBackoff !== undefined) {
1788
+ defaults.retry.backoff = retryBackoff;
1789
+ }
1790
+ const requestScopedRetryDelayMs = parseNumber(process.env.REQUEST_SCOPED_429_RETRY_DELAY_MS);
1791
+ if (requestScopedRetryDelayMs !== undefined) {
1792
+ defaults.requestScopedRetryDelayMs = requestScopedRetryDelayMs;
1793
+ }
1794
+ const sessionDurationMs = parseNumber(process.env.SESSION_DURATION_MS);
1795
+ if (sessionDurationMs !== undefined) {
1796
+ defaults.sessionDurationMs = sessionDurationMs;
1797
+ }
1798
+ const port = parseNumber(process.env.PORT);
1799
+ if (port !== undefined) {
1800
+ defaults.port = port;
1801
+ }
1802
+ if (this.data.client_id) {
1803
+ defaults.clientId = this.data.client_id;
1804
+ }
1805
+ if (typeof this.data.retry_attempts === "number") {
1806
+ defaults.retry.attempts = this.data.retry_attempts;
1807
+ }
1808
+ if (typeof this.data.max_body_bytes === "number") {
1809
+ defaults.maxBodyBytes = this.data.max_body_bytes;
1810
+ }
1811
+ if (typeof this.data.quota_threshold === "number" && this.data.quota_threshold > 0 && this.data.quota_threshold <= 1) {
1812
+ defaults.quotaThreshold = this.data.quota_threshold;
1813
+ }
1814
+ if (typeof this.data.retry_delay_ms === "number") {
1815
+ defaults.retry.delayMs = this.data.retry_delay_ms;
1816
+ }
1817
+ if (typeof this.data.retry_backoff === "number") {
1818
+ defaults.retry.backoff = this.data.retry_backoff;
1819
+ }
1820
+ if (typeof this.data.request_scoped_429_retry_delay_ms === "number") {
1821
+ defaults.requestScopedRetryDelayMs = this.data.request_scoped_429_retry_delay_ms;
1822
+ }
1823
+ if (typeof this.data.session_duration_ms === "number") {
1824
+ defaults.sessionDurationMs = this.data.session_duration_ms;
1825
+ }
1826
+ if (typeof this.data.port === "number") {
1827
+ defaults.port = this.data.port;
1828
+ }
1829
+ return defaults;
1830
+ }
1831
+ };
1832
+ });
1833
+
1834
+ // ../../packages/database/src/paths.ts
1835
+ import { join as join5 } from "path";
1836
+ function resolveDbPath() {
1837
+ const explicitPath = process.env.ccflare_DB_PATH;
1838
+ if (explicitPath) {
1839
+ return explicitPath;
1840
+ }
1841
+ const configDir = getPlatformConfigDir();
1842
+ return join5(configDir, "ccflare.db");
1843
+ }
1844
+ var init_paths2 = __esm(() => {
1845
+ init_src4();
1846
+ });
1847
+
1848
+ // ../../packages/database/src/performance-indexes.ts
1849
+ function addPerformanceIndexes(db) {
1850
+ log3.info("Adding performance indexes...");
1851
+ db.run(`
1852
+ CREATE INDEX IF NOT EXISTS idx_requests_timestamp_account
1853
+ ON requests(timestamp DESC, account_used)
1854
+ `);
1855
+ log3.info("Added index: idx_requests_timestamp_account");
1856
+ db.run(`
1857
+ CREATE INDEX IF NOT EXISTS idx_requests_model_timestamp
1858
+ ON requests(model, timestamp DESC)
1859
+ WHERE model IS NOT NULL
1860
+ `);
1861
+ log3.info("Added index: idx_requests_model_timestamp");
1862
+ db.run(`
1863
+ CREATE INDEX IF NOT EXISTS idx_requests_success_timestamp
1864
+ ON requests(success, timestamp DESC)
1865
+ `);
1866
+ log3.info("Added index: idx_requests_success_timestamp");
1867
+ db.run(`
1868
+ CREATE INDEX IF NOT EXISTS idx_accounts_paused
1869
+ ON accounts(paused)
1870
+ WHERE paused = 0
1871
+ `);
1872
+ log3.info("Added index: idx_accounts_paused");
1873
+ db.run(`
1874
+ CREATE INDEX IF NOT EXISTS idx_requests_account_timestamp
1875
+ ON requests(account_used, timestamp DESC)
1876
+ `);
1877
+ log3.info("Added index: idx_requests_account_timestamp");
1878
+ db.run(`
1879
+ CREATE INDEX IF NOT EXISTS idx_requests_cost_model
1880
+ ON requests(cost_usd, model, timestamp DESC)
1881
+ WHERE cost_usd > 0 AND model IS NOT NULL
1882
+ `);
1883
+ log3.info("Added index: idx_requests_cost_model");
1884
+ db.run(`
1885
+ CREATE INDEX IF NOT EXISTS idx_requests_response_time
1886
+ ON requests(model, response_time_ms)
1887
+ WHERE response_time_ms IS NOT NULL AND model IS NOT NULL
1888
+ `);
1889
+ log3.info("Added index: idx_requests_response_time");
1890
+ db.run(`
1891
+ CREATE INDEX IF NOT EXISTS idx_requests_tokens
1892
+ ON requests(timestamp DESC, total_tokens)
1893
+ WHERE total_tokens > 0
1894
+ `);
1895
+ log3.info("Added index: idx_requests_tokens");
1896
+ db.run(`
1897
+ CREATE INDEX IF NOT EXISTS idx_accounts_name
1898
+ ON accounts(name)
1899
+ `);
1900
+ log3.info("Added index: idx_accounts_name");
1901
+ db.run(`
1902
+ CREATE INDEX IF NOT EXISTS idx_accounts_rate_limited
1903
+ ON accounts(rate_limited_until)
1904
+ WHERE rate_limited_until IS NOT NULL
1905
+ `);
1906
+ log3.info("Added index: idx_accounts_rate_limited");
1907
+ db.run(`
1908
+ CREATE INDEX IF NOT EXISTS idx_accounts_session
1909
+ ON accounts(session_start, session_request_count)
1910
+ WHERE session_start IS NOT NULL
1911
+ `);
1912
+ log3.info("Added index: idx_accounts_session");
1913
+ db.run(`
1914
+ CREATE INDEX IF NOT EXISTS idx_accounts_request_count
1915
+ ON accounts(request_count DESC, last_used)
1916
+ `);
1917
+ log3.info("Added index: idx_accounts_request_count");
1918
+ db.run(`
1919
+ CREATE INDEX IF NOT EXISTS idx_requests_response_id
1920
+ ON requests(response_id)
1921
+ WHERE response_id IS NOT NULL
1922
+ `);
1923
+ log3.info("Added index: idx_requests_response_id");
1924
+ db.run(`
1925
+ CREATE INDEX IF NOT EXISTS idx_requests_previous_response_id
1926
+ ON requests(previous_response_id)
1927
+ WHERE previous_response_id IS NOT NULL
1928
+ `);
1929
+ log3.info("Added index: idx_requests_previous_response_id");
1930
+ db.run(`
1931
+ CREATE INDEX IF NOT EXISTS idx_requests_response_chain_timestamp
1932
+ ON requests(response_chain_id, timestamp ASC)
1933
+ WHERE response_chain_id IS NOT NULL
1934
+ `);
1935
+ log3.info("Added index: idx_requests_response_chain_timestamp");
1936
+ db.run(`
1937
+ CREATE INDEX IF NOT EXISTS idx_requests_client_session_timestamp
1938
+ ON requests(client_session_id, timestamp DESC)
1939
+ WHERE client_session_id IS NOT NULL
1940
+ `);
1941
+ log3.info("Added index: idx_requests_client_session_timestamp");
1942
+ log3.info("Performance indexes added successfully");
1943
+ }
1944
+ function analyzeIndexUsage(db) {
1945
+ log3.info(`
1946
+ Analyzing index usage...`);
1947
+ const indexes = db.prepare(`SELECT name, tbl_name, sql
1948
+ FROM sqlite_master
1949
+ WHERE type = 'index' AND name NOT LIKE 'sqlite_%'
1950
+ ORDER BY tbl_name, name`).all();
1951
+ log3.info(`
1952
+ Total indexes: ${indexes.length}`);
1953
+ for (const index of indexes) {
1954
+ log3.info(`- ${index.name} on ${index.tbl_name}`);
1955
+ }
1956
+ const tables = ["accounts", "requests", "request_payloads"];
1957
+ for (const table of tables) {
1958
+ const count = db.prepare(`SELECT COUNT(*) as count FROM ${table}`).get();
1959
+ log3.info(`
1960
+ ${table} table: ${count.count} rows`);
1961
+ }
1962
+ }
1963
+ var log3;
1964
+ var init_performance_indexes = __esm(() => {
1965
+ init_src3();
1966
+ log3 = new Logger("PerformanceIndexes");
1967
+ });
1968
+
1969
+ // ../../packages/database/src/analyze-performance.ts
1970
+ function analyzeQueryPerformance(db) {
1971
+ log4.info(`
1972
+ === Query Performance Analysis ===
1973
+ `);
1974
+ const testQueries = [
1975
+ {
1976
+ name: "Time-based account analytics",
1977
+ query: `
1978
+ SELECT COUNT(*), account_used
1979
+ FROM requests
1980
+ WHERE timestamp > ? AND account_used IS NOT NULL
1981
+ GROUP BY account_used
1982
+ `,
1983
+ params: [Date.now() - 24 * 60 * 60 * 1000]
1984
+ },
1985
+ {
1986
+ name: "Model performance with timestamp filter",
1987
+ query: `
1988
+ SELECT model, COUNT(*), AVG(response_time_ms)
1989
+ FROM requests
1990
+ WHERE timestamp > ? AND model IS NOT NULL
1991
+ GROUP BY model
1992
+ `,
1993
+ params: [Date.now() - 24 * 60 * 60 * 1000]
1994
+ },
1995
+ {
1996
+ name: "Success rate calculation",
1997
+ query: `
1998
+ SELECT
1999
+ SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as success_rate
2000
+ FROM requests
2001
+ WHERE timestamp > ?
2002
+ `,
2003
+ params: [Date.now() - 24 * 60 * 60 * 1000]
2004
+ },
2005
+ {
2006
+ name: "Active accounts lookup",
2007
+ query: `
2008
+ SELECT id, name, request_count
2009
+ FROM accounts
2010
+ WHERE paused = 0
2011
+ ORDER BY request_count DESC
2012
+ `,
2013
+ params: []
2014
+ },
2015
+ {
2016
+ name: "Cost analysis by model",
2017
+ query: `
2018
+ SELECT model, SUM(cost_usd) as total_cost
2019
+ FROM requests
2020
+ WHERE cost_usd > 0 AND model IS NOT NULL
2021
+ GROUP BY model
2022
+ ORDER BY total_cost DESC
2023
+ `,
2024
+ params: []
2025
+ },
2026
+ {
2027
+ name: "P95 response time calculation",
2028
+ query: `
2029
+ WITH ordered_times AS (
2030
+ SELECT
2031
+ response_time_ms,
2032
+ ROW_NUMBER() OVER (ORDER BY response_time_ms) as row_num,
2033
+ COUNT(*) OVER () as total_count
2034
+ FROM requests
2035
+ WHERE model = ? AND response_time_ms IS NOT NULL
2036
+ )
2037
+ SELECT response_time_ms as p95_response_time
2038
+ FROM ordered_times
2039
+ WHERE row_num = CAST(CEIL(total_count * 0.95) AS INTEGER)
2040
+ LIMIT 1
2041
+ `,
2042
+ params: ["claude-3-5-sonnet-20241022"]
2043
+ }
2044
+ ];
2045
+ for (const test of testQueries) {
2046
+ log4.info(`
2047
+ --- ${test.name} ---`);
2048
+ try {
2049
+ const planStmt = db.prepare(`EXPLAIN QUERY PLAN ${test.query}`);
2050
+ const plan = planStmt.all(...test.params);
2051
+ log4.info("Query Plan:");
2052
+ for (const row of plan) {
2053
+ log4.info(` ${JSON.stringify(row)}`);
2054
+ }
2055
+ const start = performance.now();
2056
+ const stmt = db.prepare(test.query);
2057
+ const result = stmt.all(...test.params);
2058
+ const duration = performance.now() - start;
2059
+ log4.info(`Execution time: ${duration.toFixed(2)}ms`);
2060
+ log4.info(`Rows returned: ${result.length}`);
2061
+ } catch (error) {
2062
+ log4.error(`Error: ${error}`);
2063
+ }
2064
+ }
2065
+ }
2066
+ function showIndexStats(db) {
2067
+ log4.info(`
2068
+ === Index Statistics ===
2069
+ `);
2070
+ const indexes = db.prepare(`
2071
+ SELECT
2072
+ m.name as index_name,
2073
+ m.tbl_name as table_name,
2074
+ m.sql as create_sql,
2075
+ s.stat as stat_value
2076
+ FROM sqlite_master m
2077
+ LEFT JOIN sqlite_stat1 s ON m.name = s.idx
2078
+ WHERE m.type = 'index'
2079
+ AND m.name NOT LIKE 'sqlite_%'
2080
+ AND m.name LIKE 'idx_%'
2081
+ ORDER BY m.tbl_name, m.name
2082
+ `).all();
2083
+ log4.info(`Total performance indexes: ${indexes.length}
2084
+ `);
2085
+ let currentTable = "";
2086
+ for (const index of indexes) {
2087
+ if (index.table_name !== currentTable) {
2088
+ currentTable = index.table_name;
2089
+ log4.info(`
2090
+ ${currentTable}:`);
2091
+ }
2092
+ log4.info(` - ${index.index_name}`);
2093
+ if (index.stat_value) {
2094
+ log4.info(` Stats: ${index.stat_value}`);
2095
+ }
2096
+ }
2097
+ log4.info(`
2098
+ Running ANALYZE to update statistics...`);
2099
+ db.exec("ANALYZE");
2100
+ log4.info("Statistics updated.");
2101
+ }
2102
+ function analyzeDatabasePerformance(dbOps) {
2103
+ const db = dbOps.getDatabase();
2104
+ log4.info(`
2105
+ === Database Performance Analysis ===
2106
+ `);
2107
+ analyzeIndexUsage(db);
2108
+ showIndexStats(db);
2109
+ analyzeQueryPerformance(db);
2110
+ log4.info(`
2111
+ === Analysis Complete ===
2112
+ `);
2113
+ }
2114
+ var log4;
2115
+ var init_analyze_performance = __esm(() => {
2116
+ init_src3();
2117
+ init_paths2();
2118
+ init_performance_indexes();
2119
+ log4 = new Logger("PerformanceAnalysis");
2120
+ if (false) {}
2121
+ });
2122
+
2123
+ // ../../packages/database/src/async-writer.ts
2124
+ class AsyncDbWriter {
2125
+ queue = [];
2126
+ running = false;
2127
+ intervalId = null;
2128
+ failureCount = 0;
2129
+ disposePromise = null;
2130
+ currentRunPromise = null;
2131
+ constructor() {
2132
+ this.intervalId = setInterval(() => void this.processQueue(), 100);
2133
+ }
2134
+ enqueue(job) {
2135
+ this.queue.push(job);
2136
+ this.processQueue();
2137
+ }
2138
+ async processQueue() {
2139
+ if (this.running) {
2140
+ return this.currentRunPromise ?? Promise.resolve();
2141
+ }
2142
+ if (this.queue.length === 0) {
2143
+ return;
2144
+ }
2145
+ this.running = true;
2146
+ this.currentRunPromise = (async () => {
2147
+ try {
2148
+ while (this.queue.length > 0) {
2149
+ const job = this.queue.shift();
2150
+ if (!job)
2151
+ continue;
2152
+ try {
2153
+ await job();
2154
+ } catch (error) {
2155
+ this.failureCount += 1;
2156
+ logger.error("Failed to execute DB job", error);
2157
+ }
2158
+ }
2159
+ } finally {
2160
+ this.running = false;
2161
+ this.currentRunPromise = null;
2162
+ }
2163
+ })();
2164
+ return this.currentRunPromise;
2165
+ }
2166
+ isHealthy() {
2167
+ return this.failureCount === 0;
2168
+ }
2169
+ getFailureCount() {
2170
+ return this.failureCount;
2171
+ }
2172
+ getQueueSize() {
2173
+ return this.queue.length;
2174
+ }
2175
+ async dispose() {
2176
+ if (this.disposePromise) {
2177
+ return this.disposePromise;
2178
+ }
2179
+ this.disposePromise = (async () => {
2180
+ logger.info("Flushing async DB writer queue...");
2181
+ if (this.intervalId) {
2182
+ clearInterval(this.intervalId);
2183
+ this.intervalId = null;
2184
+ }
2185
+ await this.processQueue();
2186
+ logger.info("Async DB writer queue flushed", {
2187
+ remainingJobs: this.queue.length,
2188
+ failureCount: this.failureCount
2189
+ });
2190
+ })();
2191
+ return this.disposePromise;
2192
+ }
2193
+ }
2194
+ var logger;
2195
+ var init_async_writer = __esm(() => {
2196
+ init_src3();
2197
+ logger = new Logger("async-db-writer");
2198
+ });
2199
+
2200
+ // ../../packages/database/src/migrations.ts
2201
+ function getTableInfo(db, tableName) {
2202
+ return db.prepare(`PRAGMA table_info(${tableName})`).all();
2203
+ }
2204
+ function hasColumn(columns, columnName) {
2205
+ return columns.some((column) => column.name === columnName);
2206
+ }
2207
+ function getColumn(columns, columnName) {
2208
+ return columns.find((column) => column.name === columnName) ?? null;
2209
+ }
2210
+ function columnOr(columns, columnName, fallback) {
2211
+ return hasColumn(columns, columnName) ? columnName : fallback;
2212
+ }
2213
+ function shouldMigrateAccountsTable(columns) {
2214
+ const provider = getColumn(columns, "provider");
2215
+ const refreshToken = getColumn(columns, "refresh_token");
2216
+ const weight = getColumn(columns, "weight");
2217
+ const authMethod = getColumn(columns, "auth_method");
2218
+ const baseUrl = getColumn(columns, "base_url");
2219
+ return !provider || provider.notnull !== 1 || provider.dflt_value !== null || !refreshToken || refreshToken.notnull !== 0 || !weight || weight.notnull !== 1 || weight.dflt_value !== "1" || !authMethod || authMethod.notnull !== 1 || !baseUrl || hasColumn(columns, "account_tier");
2220
+ }
2221
+ function shouldMigrateRequestsTable(columns) {
2222
+ const provider = getColumn(columns, "provider");
2223
+ const upstreamPath = getColumn(columns, "upstream_path");
2224
+ const reasoningTokens = getColumn(columns, "reasoning_tokens");
2225
+ const responseChainId = getColumn(columns, "response_chain_id");
2226
+ const clientSessionId = getColumn(columns, "client_session_id");
2227
+ const ttftMs = getColumn(columns, "ttft_ms");
2228
+ const proxyOverheadMs = getColumn(columns, "proxy_overhead_ms");
2229
+ const upstreamTtfbMs = getColumn(columns, "upstream_ttfb_ms");
2230
+ const streamingDurationMs = getColumn(columns, "streaming_duration_ms");
2231
+ return !provider || provider.notnull !== 1 || !upstreamPath || upstreamPath.notnull !== 1 || !reasoningTokens || !responseChainId || !clientSessionId || !ttftMs || !proxyOverheadMs || !upstreamTtfbMs || !streamingDurationMs || hasColumn(columns, "conversation_id") || hasColumn(columns, "agent_used");
2232
+ }
2233
+ function ensureRequestLinkageColumns(db) {
2234
+ const columns = getTableInfo(db, "requests");
2235
+ const requestColumns = [
2236
+ ["response_id", "TEXT"],
2237
+ ["previous_response_id", "TEXT"],
2238
+ ["response_chain_id", "TEXT"],
2239
+ ["client_session_id", "TEXT"],
2240
+ ["ttft_ms", "INTEGER"],
2241
+ ["proxy_overhead_ms", "INTEGER"],
2242
+ ["upstream_ttfb_ms", "INTEGER"],
2243
+ ["streaming_duration_ms", "INTEGER"]
2244
+ ];
2245
+ for (const [columnName, columnType] of requestColumns) {
2246
+ if (!hasColumn(columns, columnName)) {
2247
+ db.run(`ALTER TABLE requests ADD COLUMN ${columnName} ${columnType}`);
2248
+ }
2249
+ }
2250
+ }
2251
+ function ensureAccountExtensionColumns(db) {
2252
+ const columns = getTableInfo(db, "accounts");
2253
+ const identityColumns = [
2254
+ ["account_uuid", "TEXT"],
2255
+ ["organization_uuid", "TEXT"],
2256
+ ["quota_state", "TEXT"],
2257
+ ["priority", "INTEGER NOT NULL DEFAULT 0"],
2258
+ ["switch_threshold", "TEXT"],
2259
+ ["error_reason", "TEXT"]
2260
+ ];
2261
+ for (const [columnName, columnType] of identityColumns) {
2262
+ if (!hasColumn(columns, columnName)) {
2263
+ db.run(`ALTER TABLE accounts ADD COLUMN ${columnName} ${columnType}`);
2264
+ }
2265
+ }
2266
+ }
2267
+ function backfillRequestLinkageColumns(db) {
2268
+ const missingCount = db.query(`
2269
+ SELECT COUNT(*) AS count
2270
+ FROM requests
2271
+ WHERE response_chain_id IS NULL OR client_session_id IS NULL
2272
+ `).get();
2273
+ if (!missingCount || missingCount.count === 0) {
2274
+ return;
2275
+ }
2276
+ const rows = db.query(`
2277
+ SELECT
2278
+ r.id,
2279
+ r.timestamp,
2280
+ r.response_id,
2281
+ r.previous_response_id,
2282
+ r.response_chain_id,
2283
+ r.client_session_id,
2284
+ rp.json
2285
+ FROM requests r
2286
+ LEFT JOIN request_payloads rp ON rp.id = r.id
2287
+ ORDER BY r.timestamp ASC
2288
+ `).all();
2289
+ const responseToChain = new Map;
2290
+ let updatedRows = 0;
2291
+ const updateStmt = db.prepare(`
2292
+ UPDATE requests
2293
+ SET
2294
+ previous_response_id = ?,
2295
+ response_id = ?,
2296
+ response_chain_id = ?,
2297
+ client_session_id = ?
2298
+ WHERE id = ?
2299
+ `);
2300
+ db.run("BEGIN");
2301
+ try {
2302
+ for (const row of rows) {
2303
+ let payload = null;
2304
+ if (typeof row.json === "string") {
2305
+ try {
2306
+ payload = JSON.parse(row.json);
2307
+ } catch {
2308
+ payload = null;
2309
+ }
2310
+ }
2311
+ const extracted = extractRequestLinkageFromPayload(payload);
2312
+ const previousResponseId = extracted.previousResponseId ?? row.previous_response_id ?? null;
2313
+ const responseId = extracted.responseId ?? row.response_id ?? null;
2314
+ let responseChainId = row.response_chain_id;
2315
+ const clientSessionId = extracted.clientSessionId ?? row.client_session_id ?? null;
2316
+ if (!responseChainId) {
2317
+ if (previousResponseId) {
2318
+ responseChainId = responseToChain.get(previousResponseId) ?? previousResponseId;
2319
+ } else {
2320
+ responseChainId = responseId ?? row.id;
2321
+ }
2322
+ }
2323
+ if (responseId && responseChainId) {
2324
+ responseToChain.set(responseId, responseChainId);
2325
+ }
2326
+ if (row.previous_response_id !== previousResponseId || row.response_id !== responseId || row.response_chain_id !== responseChainId || row.client_session_id !== clientSessionId) {
2327
+ updateStmt.run(previousResponseId, responseId, responseChainId, clientSessionId, row.id);
2328
+ updatedRows++;
2329
+ }
2330
+ }
2331
+ db.run("COMMIT");
2332
+ } catch (e) {
2333
+ db.run("ROLLBACK");
2334
+ throw e;
2335
+ }
2336
+ if (updatedRows > 0) {
2337
+ log5.info(`Backfilled request linkage for ${updatedRows} requests`);
2338
+ }
2339
+ }
2340
+ function migrateAccountsTable(db, columns) {
2341
+ const weightExpression = hasColumn(columns, "weight") ? hasColumn(columns, "account_tier") ? "COALESCE(weight, account_tier, 1)" : "COALESCE(weight, 1)" : hasColumn(columns, "account_tier") ? "COALESCE(account_tier, 1)" : "1";
2342
+ db.run(`
2343
+ CREATE TABLE accounts_v2 (
2344
+ id TEXT PRIMARY KEY,
2345
+ name TEXT NOT NULL,
2346
+ provider TEXT NOT NULL,
2347
+ auth_method TEXT NOT NULL,
2348
+ base_url TEXT,
2349
+ api_key TEXT,
2350
+ refresh_token TEXT,
2351
+ access_token TEXT,
2352
+ expires_at INTEGER,
2353
+ created_at INTEGER NOT NULL,
2354
+ last_used INTEGER,
2355
+ request_count INTEGER DEFAULT 0,
2356
+ total_requests INTEGER DEFAULT 0,
2357
+ weight INTEGER NOT NULL DEFAULT 1,
2358
+ priority INTEGER NOT NULL DEFAULT 0,
2359
+ switch_threshold TEXT,
2360
+ rate_limited_until INTEGER,
2361
+ session_start INTEGER,
2362
+ session_request_count INTEGER DEFAULT 0,
2363
+ paused INTEGER DEFAULT 0,
2364
+ error_reason TEXT,
2365
+ rate_limit_reset INTEGER,
2366
+ rate_limit_status TEXT,
2367
+ rate_limit_remaining INTEGER,
2368
+ account_uuid TEXT,
2369
+ organization_uuid TEXT,
2370
+ quota_state TEXT
2371
+ )
2372
+ `);
2373
+ db.run(`
2374
+ INSERT INTO accounts_v2 (
2375
+ id, name, provider, auth_method, base_url, api_key, refresh_token,
2376
+ access_token, expires_at, created_at, last_used, request_count,
2377
+ total_requests, weight, priority, switch_threshold, rate_limited_until, session_start,
2378
+ session_request_count, paused, error_reason, rate_limit_reset, rate_limit_status,
2379
+ rate_limit_remaining, account_uuid, organization_uuid, quota_state
2380
+ )
2381
+ SELECT
2382
+ id,
2383
+ name,
2384
+ ${hasColumn(columns, "provider") ? "COALESCE(provider, 'anthropic')" : "'anthropic'"},
2385
+ ${hasColumn(columns, "auth_method") ? "COALESCE(auth_method, 'oauth')" : "'oauth'"},
2386
+ ${columnOr(columns, "base_url", "NULL")},
2387
+ ${columnOr(columns, "api_key", "NULL")},
2388
+ ${columnOr(columns, "refresh_token", "NULL")},
2389
+ ${columnOr(columns, "access_token", "NULL")},
2390
+ ${columnOr(columns, "expires_at", "NULL")},
2391
+ ${columnOr(columns, "created_at", "CAST(unixepoch('subsec') * 1000 AS INTEGER)")},
2392
+ ${columnOr(columns, "last_used", "NULL")},
2393
+ COALESCE(${columnOr(columns, "request_count", "NULL")}, 0),
2394
+ COALESCE(${columnOr(columns, "total_requests", "NULL")}, 0),
2395
+ ${weightExpression},
2396
+ COALESCE(${columnOr(columns, "priority", "NULL")}, 0),
2397
+ ${columnOr(columns, "switch_threshold", "NULL")},
2398
+ ${columnOr(columns, "rate_limited_until", "NULL")},
2399
+ ${columnOr(columns, "session_start", "NULL")},
2400
+ COALESCE(${columnOr(columns, "session_request_count", "NULL")}, 0),
2401
+ COALESCE(${columnOr(columns, "paused", "NULL")}, 0),
2402
+ ${columnOr(columns, "error_reason", "NULL")},
2403
+ ${columnOr(columns, "rate_limit_reset", "NULL")},
2404
+ ${columnOr(columns, "rate_limit_status", "NULL")},
2405
+ ${columnOr(columns, "rate_limit_remaining", "NULL")},
2406
+ ${columnOr(columns, "account_uuid", "NULL")},
2407
+ ${columnOr(columns, "organization_uuid", "NULL")},
2408
+ ${columnOr(columns, "quota_state", "NULL")}
2409
+ FROM accounts
2410
+ `);
2411
+ db.run("DROP TABLE accounts");
2412
+ db.run("ALTER TABLE accounts_v2 RENAME TO accounts");
2413
+ log5.info("Migrated accounts table to v2 schema");
2414
+ }
2415
+ function migrateRequestsTable(db, columns) {
2416
+ db.run(`
2417
+ CREATE TABLE requests_v2 (
2418
+ id TEXT PRIMARY KEY,
2419
+ timestamp INTEGER NOT NULL,
2420
+ method TEXT NOT NULL,
2421
+ path TEXT NOT NULL,
2422
+ provider TEXT NOT NULL DEFAULT '',
2423
+ upstream_path TEXT NOT NULL DEFAULT '',
2424
+ account_used TEXT,
2425
+ status_code INTEGER,
2426
+ success BOOLEAN,
2427
+ error_message TEXT,
2428
+ response_time_ms INTEGER,
2429
+ failover_attempts INTEGER DEFAULT 0,
2430
+ model TEXT,
2431
+ prompt_tokens INTEGER DEFAULT 0,
2432
+ completion_tokens INTEGER DEFAULT 0,
2433
+ total_tokens INTEGER DEFAULT 0,
2434
+ cost_usd REAL DEFAULT 0,
2435
+ output_tokens_per_second REAL,
2436
+ input_tokens INTEGER DEFAULT 0,
2437
+ cache_read_input_tokens INTEGER DEFAULT 0,
2438
+ cache_creation_input_tokens INTEGER DEFAULT 0,
2439
+ output_tokens INTEGER DEFAULT 0,
2440
+ reasoning_tokens INTEGER DEFAULT 0,
2441
+ response_id TEXT,
2442
+ previous_response_id TEXT,
2443
+ response_chain_id TEXT,
2444
+ client_session_id TEXT,
2445
+ ttft_ms INTEGER,
2446
+ proxy_overhead_ms INTEGER,
2447
+ upstream_ttfb_ms INTEGER,
2448
+ streaming_duration_ms INTEGER
2449
+ )
2450
+ `);
2451
+ db.run(`
2452
+ INSERT INTO requests_v2 (
2453
+ id, timestamp, method, path, provider, upstream_path, account_used,
2454
+ status_code, success, error_message, response_time_ms,
2455
+ failover_attempts, model, prompt_tokens, completion_tokens,
2456
+ total_tokens, cost_usd, output_tokens_per_second, input_tokens,
2457
+ cache_read_input_tokens, cache_creation_input_tokens, output_tokens,
2458
+ reasoning_tokens, response_id, previous_response_id, response_chain_id,
2459
+ client_session_id, ttft_ms, proxy_overhead_ms, upstream_ttfb_ms,
2460
+ streaming_duration_ms
2461
+ )
2462
+ SELECT
2463
+ id,
2464
+ timestamp,
2465
+ method,
2466
+ path,
2467
+ ${hasColumn(columns, "provider") ? "COALESCE(provider, '')" : "''"},
2468
+ ${hasColumn(columns, "upstream_path") ? "COALESCE(upstream_path, '')" : "''"},
2469
+ ${columnOr(columns, "account_used", "NULL")},
2470
+ ${columnOr(columns, "status_code", "NULL")},
2471
+ ${columnOr(columns, "success", "0")},
2472
+ ${columnOr(columns, "error_message", "NULL")},
2473
+ ${columnOr(columns, "response_time_ms", "NULL")},
2474
+ COALESCE(${columnOr(columns, "failover_attempts", "NULL")}, 0),
2475
+ ${columnOr(columns, "model", "NULL")},
2476
+ COALESCE(${columnOr(columns, "prompt_tokens", "NULL")}, 0),
2477
+ COALESCE(${columnOr(columns, "completion_tokens", "NULL")}, 0),
2478
+ COALESCE(${columnOr(columns, "total_tokens", "NULL")}, 0),
2479
+ COALESCE(${columnOr(columns, "cost_usd", "NULL")}, 0),
2480
+ ${columnOr(columns, "output_tokens_per_second", "NULL")},
2481
+ COALESCE(${columnOr(columns, "input_tokens", "NULL")}, 0),
2482
+ COALESCE(${columnOr(columns, "cache_read_input_tokens", "NULL")}, 0),
2483
+ COALESCE(${columnOr(columns, "cache_creation_input_tokens", "NULL")}, 0),
2484
+ COALESCE(${columnOr(columns, "output_tokens", "NULL")}, 0),
2485
+ COALESCE(${columnOr(columns, "reasoning_tokens", "NULL")}, 0),
2486
+ ${columnOr(columns, "response_id", "NULL")},
2487
+ ${columnOr(columns, "previous_response_id", "NULL")},
2488
+ ${hasColumn(columns, "response_chain_id") ? "response_chain_id" : columnOr(columns, "conversation_id", "NULL")},
2489
+ ${columnOr(columns, "client_session_id", "NULL")},
2490
+ ${columnOr(columns, "ttft_ms", "NULL")},
2491
+ ${columnOr(columns, "proxy_overhead_ms", "NULL")},
2492
+ ${columnOr(columns, "upstream_ttfb_ms", "NULL")},
2493
+ ${columnOr(columns, "streaming_duration_ms", "NULL")}
2494
+ FROM requests
2495
+ `);
2496
+ db.run("DROP TABLE requests");
2497
+ db.run("ALTER TABLE requests_v2 RENAME TO requests");
2498
+ log5.info("Migrated requests table to v2 schema");
2499
+ }
2500
+ function ensureAuthSessionsTable(db) {
2501
+ db.run(`
2502
+ CREATE TABLE IF NOT EXISTS auth_sessions (
2503
+ id TEXT PRIMARY KEY,
2504
+ provider TEXT NOT NULL,
2505
+ auth_method TEXT NOT NULL,
2506
+ account_name TEXT NOT NULL,
2507
+ state_json TEXT NOT NULL,
2508
+ created_at TEXT NOT NULL,
2509
+ expires_at TEXT NOT NULL
2510
+ )
2511
+ `);
2512
+ db.run(`CREATE INDEX IF NOT EXISTS idx_auth_sessions_expires ON auth_sessions(expires_at)`);
2513
+ }
2514
+ function ensureConversationPinsTable(db) {
2515
+ db.run(`
2516
+ CREATE TABLE IF NOT EXISTS conversation_pins (
2517
+ pin_key TEXT PRIMARY KEY,
2518
+ account_id TEXT NOT NULL,
2519
+ last_seen_at INTEGER NOT NULL
2520
+ )
2521
+ `);
2522
+ db.run(`CREATE INDEX IF NOT EXISTS idx_conversation_pins_account ON conversation_pins(account_id)`);
2523
+ db.run(`CREATE INDEX IF NOT EXISTS idx_conversation_pins_last_seen ON conversation_pins(last_seen_at)`);
2524
+ }
2525
+ function remediateDuplicateAccountNames(db) {
2526
+ const accounts = db.query(`
2527
+ SELECT id, name, created_at
2528
+ FROM accounts
2529
+ ORDER BY name ASC, created_at ASC, id ASC
2530
+ `).all();
2531
+ if (accounts.length < 2) {
2532
+ return;
2533
+ }
2534
+ const duplicateGroups = new Map;
2535
+ const usedNames = new Set(accounts.map((account2) => account2.name));
2536
+ for (const account2 of accounts) {
2537
+ const group = duplicateGroups.get(account2.name);
2538
+ if (group) {
2539
+ group.push(account2);
2540
+ continue;
2541
+ }
2542
+ duplicateGroups.set(account2.name, [account2]);
2543
+ }
2544
+ let renamedCount = 0;
2545
+ for (const [name, group] of duplicateGroups) {
2546
+ if (group.length < 2) {
2547
+ continue;
2548
+ }
2549
+ for (const account2 of group.slice(1)) {
2550
+ let suffix = 2;
2551
+ let candidate = `${name}-${suffix}`;
2552
+ while (usedNames.has(candidate)) {
2553
+ suffix += 1;
2554
+ candidate = `${name}-${suffix}`;
2555
+ }
2556
+ db.run(`UPDATE accounts SET name = ? WHERE id = ?`, [
2557
+ candidate,
2558
+ account2.id
2559
+ ]);
2560
+ usedNames.add(candidate);
2561
+ renamedCount += 1;
2562
+ }
2563
+ }
2564
+ if (renamedCount > 0) {
2565
+ log5.warn(`Renamed ${renamedCount} duplicate account name${renamedCount === 1 ? "" : "s"} before creating the accounts.name unique index`);
2566
+ }
2567
+ }
2568
+ function ensureAccountsNameUniqueness(db) {
2569
+ remediateDuplicateAccountNames(db);
2570
+ db.run(`CREATE UNIQUE INDEX IF NOT EXISTS idx_accounts_name_unique ON accounts(name)`);
2571
+ }
2572
+ function ensureSchema(db) {
2573
+ db.run(`
2574
+ CREATE TABLE IF NOT EXISTS accounts (
2575
+ id TEXT PRIMARY KEY,
2576
+ name TEXT NOT NULL,
2577
+ provider TEXT NOT NULL,
2578
+ auth_method TEXT NOT NULL,
2579
+ base_url TEXT,
2580
+ api_key TEXT,
2581
+ refresh_token TEXT,
2582
+ access_token TEXT,
2583
+ expires_at INTEGER,
2584
+ created_at INTEGER NOT NULL,
2585
+ last_used INTEGER,
2586
+ request_count INTEGER DEFAULT 0,
2587
+ total_requests INTEGER DEFAULT 0,
2588
+ weight INTEGER NOT NULL DEFAULT 1,
2589
+ priority INTEGER NOT NULL DEFAULT 0,
2590
+ switch_threshold TEXT,
2591
+ rate_limited_until INTEGER,
2592
+ session_start INTEGER,
2593
+ session_request_count INTEGER DEFAULT 0,
2594
+ paused INTEGER DEFAULT 0,
2595
+ error_reason TEXT,
2596
+ rate_limit_reset INTEGER,
2597
+ rate_limit_status TEXT,
2598
+ rate_limit_remaining INTEGER,
2599
+ account_uuid TEXT,
2600
+ organization_uuid TEXT,
2601
+ quota_state TEXT
2602
+ )
2603
+ `);
2604
+ ensureAccountsNameUniqueness(db);
2605
+ db.run(`
2606
+ CREATE TABLE IF NOT EXISTS requests (
2607
+ id TEXT PRIMARY KEY,
2608
+ timestamp INTEGER NOT NULL,
2609
+ method TEXT NOT NULL,
2610
+ path TEXT NOT NULL,
2611
+ provider TEXT NOT NULL DEFAULT '',
2612
+ upstream_path TEXT NOT NULL DEFAULT '',
2613
+ account_used TEXT,
2614
+ status_code INTEGER,
2615
+ success BOOLEAN,
2616
+ error_message TEXT,
2617
+ response_time_ms INTEGER,
2618
+ failover_attempts INTEGER DEFAULT 0,
2619
+ model TEXT,
2620
+ prompt_tokens INTEGER DEFAULT 0,
2621
+ completion_tokens INTEGER DEFAULT 0,
2622
+ total_tokens INTEGER DEFAULT 0,
2623
+ cost_usd REAL DEFAULT 0,
2624
+ output_tokens_per_second REAL,
2625
+ input_tokens INTEGER DEFAULT 0,
2626
+ cache_read_input_tokens INTEGER DEFAULT 0,
2627
+ cache_creation_input_tokens INTEGER DEFAULT 0,
2628
+ output_tokens INTEGER DEFAULT 0,
2629
+ reasoning_tokens INTEGER DEFAULT 0,
2630
+ response_id TEXT,
2631
+ previous_response_id TEXT,
2632
+ response_chain_id TEXT,
2633
+ client_session_id TEXT,
2634
+ ttft_ms INTEGER,
2635
+ proxy_overhead_ms INTEGER,
2636
+ upstream_ttfb_ms INTEGER,
2637
+ streaming_duration_ms INTEGER
2638
+ )
2639
+ `);
2640
+ db.run(`CREATE INDEX IF NOT EXISTS idx_requests_timestamp ON requests(timestamp DESC)`);
2641
+ db.run(`
2642
+ CREATE TABLE IF NOT EXISTS request_payloads (
2643
+ id TEXT PRIMARY KEY,
2644
+ json TEXT NOT NULL,
2645
+ FOREIGN KEY (id) REFERENCES requests(id) ON DELETE CASCADE
2646
+ )
2647
+ `);
2648
+ ensureAuthSessionsTable(db);
2649
+ ensureConversationPinsTable(db);
2650
+ }
2651
+ function runMigrations(db) {
2652
+ ensureSchema(db);
2653
+ const accountsInfo = getTableInfo(db, "accounts");
2654
+ if (shouldMigrateAccountsTable(accountsInfo)) {
2655
+ migrateAccountsTable(db, accountsInfo);
2656
+ }
2657
+ ensureAccountExtensionColumns(db);
2658
+ const requestsInfo = getTableInfo(db, "requests");
2659
+ if (shouldMigrateRequestsTable(requestsInfo)) {
2660
+ migrateRequestsTable(db, requestsInfo);
2661
+ }
2662
+ ensureRequestLinkageColumns(db);
2663
+ backfillRequestLinkageColumns(db);
2664
+ db.run("DROP TABLE IF EXISTS agent_preferences");
2665
+ db.run("DROP TABLE IF EXISTS oauth_sessions");
2666
+ db.run("DROP INDEX IF EXISTS idx_oauth_sessions_expires");
2667
+ ensureAuthSessionsTable(db);
2668
+ ensureConversationPinsTable(db);
2669
+ ensureAccountsNameUniqueness(db);
2670
+ db.run(`CREATE TABLE IF NOT EXISTS enrollment_contacts (
2671
+ account_id TEXT PRIMARY KEY REFERENCES accounts(id) ON DELETE CASCADE,
2672
+ email TEXT, slack_user_id TEXT
2673
+ )`);
2674
+ db.run(`CREATE INDEX IF NOT EXISTS idx_requests_timestamp ON requests(timestamp DESC)`);
2675
+ addPerformanceIndexes(db);
2676
+ }
2677
+ var log5;
2678
+ var init_migrations = __esm(() => {
2679
+ init_src3();
2680
+ init_src();
2681
+ init_performance_indexes();
2682
+ log5 = new Logger("DatabaseMigrations");
2683
+ });
2684
+
2685
+ // ../../packages/database/src/models/account-row.ts
2686
+ function parseQuotaState(value) {
2687
+ if (!value)
2688
+ return null;
2689
+ try {
2690
+ const parsed = JSON.parse(value);
2691
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && typeof parsed.windows === "object") {
2692
+ return parsed;
2693
+ }
2694
+ } catch {}
2695
+ return null;
2696
+ }
2697
+ function parseSwitchThreshold(value) {
2698
+ if (!value)
2699
+ return null;
2700
+ try {
2701
+ const parsed = JSON.parse(value);
2702
+ if (typeof parsed === "number") {
2703
+ return Number.isFinite(parsed) && parsed > 0 && parsed <= 1 ? parsed : null;
2704
+ }
2705
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2706
+ return null;
2707
+ }
2708
+ const entries = Object.entries(parsed).filter(([, entry]) => typeof entry === "number" && Number.isFinite(entry) && entry > 0 && entry <= 1);
2709
+ return entries.length > 0 ? Object.fromEntries(entries) : null;
2710
+ } catch {
2711
+ return null;
2712
+ }
2713
+ }
2714
+ function toAccount(row) {
2715
+ return {
2716
+ id: row.id,
2717
+ name: row.name,
2718
+ provider: row.provider,
2719
+ auth_method: row.auth_method,
2720
+ base_url: row.base_url,
2721
+ api_key: row.api_key,
2722
+ refresh_token: row.refresh_token,
2723
+ access_token: row.access_token,
2724
+ expires_at: row.expires_at,
2725
+ created_at: row.created_at,
2726
+ last_used: row.last_used,
2727
+ request_count: row.request_count,
2728
+ total_requests: row.total_requests,
2729
+ rate_limited_until: row.rate_limited_until ?? null,
2730
+ session_start: row.session_start ?? null,
2731
+ session_request_count: row.session_request_count ?? 0,
2732
+ weight: row.weight || 1,
2733
+ priority: row.priority ?? 0,
2734
+ switch_threshold: parseSwitchThreshold(row.switch_threshold),
2735
+ error_reason: row.error_reason ?? null,
2736
+ paused: row.paused === 1,
2737
+ rate_limit_reset: row.rate_limit_reset ?? null,
2738
+ rate_limit_status: row.rate_limit_status ?? null,
2739
+ rate_limit_remaining: row.rate_limit_remaining ?? null,
2740
+ account_uuid: row.account_uuid ?? null,
2741
+ organization_uuid: row.organization_uuid ?? null,
2742
+ quota_state: parseQuotaState(row.quota_state)
2743
+ };
2744
+ }
2745
+
2746
+ // ../../packages/database/src/repositories/base.repository.ts
2747
+ class BaseRepository {
2748
+ db;
2749
+ constructor(db) {
2750
+ this.db = db;
2751
+ }
2752
+ query(sql, params = []) {
2753
+ return this.db.query(sql).all(...params);
2754
+ }
2755
+ get(sql, params = []) {
2756
+ const result = this.db.query(sql).get(...params);
2757
+ return result;
2758
+ }
2759
+ run(sql, params = []) {
2760
+ this.db.run(sql, params);
2761
+ }
2762
+ runWithChanges(sql, params = []) {
2763
+ const result = this.db.run(sql, params);
2764
+ return result.changes;
2765
+ }
2766
+ }
2767
+
2768
+ // ../../packages/database/src/repositories/account.repository.ts
2769
+ var accountSelectFields = `
2770
+ id, name, provider, auth_method, base_url, api_key, refresh_token, access_token,
2771
+ expires_at, created_at, last_used, request_count, total_requests,
2772
+ rate_limited_until, session_start, session_request_count,
2773
+ COALESCE(weight, 1) as weight, COALESCE(priority, 0) as priority, switch_threshold,
2774
+ error_reason,
2775
+ COALESCE(paused, 0) as paused,
2776
+ rate_limit_reset, rate_limit_status, rate_limit_remaining,
2777
+ account_uuid, organization_uuid, quota_state
2778
+ `, AccountRepository;
2779
+ var init_account_repository = __esm(() => {
2780
+ AccountRepository = class AccountRepository extends BaseRepository {
2781
+ setEnrollmentContact(accountId, contact) {
2782
+ this.run("INSERT INTO enrollment_contacts (account_id, email, slack_user_id) VALUES (?, ?, ?)", [accountId, contact.email ?? null, contact.slackUserId ?? null]);
2783
+ }
2784
+ getEnrollmentContact(accountId) {
2785
+ return this.get("SELECT email, slack_user_id AS slackUserId FROM enrollment_contacts WHERE account_id = ?", [accountId]) ?? null;
2786
+ }
2787
+ findAll() {
2788
+ const rows = this.query(`
2789
+ SELECT ${accountSelectFields}
2790
+ FROM accounts
2791
+ `);
2792
+ return rows.map(toAccount);
2793
+ }
2794
+ create(data) {
2795
+ const id = crypto.randomUUID();
2796
+ const createdAt = Date.now();
2797
+ this.run(`
2798
+ INSERT INTO accounts (
2799
+ id, name, provider, auth_method, base_url, api_key, refresh_token,
2800
+ access_token, expires_at, account_uuid, organization_uuid, quota_state, switch_threshold,
2801
+ created_at, request_count, total_requests, weight, priority
2802
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, ?, ?)
2803
+ `, [
2804
+ id,
2805
+ data.name,
2806
+ data.provider,
2807
+ data.auth_method,
2808
+ data.base_url ?? null,
2809
+ data.api_key ?? null,
2810
+ data.refresh_token ?? null,
2811
+ data.access_token ?? null,
2812
+ data.expires_at ?? null,
2813
+ data.account_uuid ?? null,
2814
+ data.organization_uuid ?? null,
2815
+ data.quota_state ? JSON.stringify(data.quota_state) : null,
2816
+ data.switch_threshold ? JSON.stringify(data.switch_threshold) : null,
2817
+ createdAt,
2818
+ data.weight ?? 1,
2819
+ data.priority ?? 0
2820
+ ]);
2821
+ return this.findById(id);
2822
+ }
2823
+ createApiKeyAccount(opts) {
2824
+ if (this.findByName(opts.name)) {
2825
+ throw new Error(`Account '${opts.name}' already exists`);
2826
+ }
2827
+ return this.create({
2828
+ name: opts.name,
2829
+ provider: opts.provider,
2830
+ auth_method: "api_key",
2831
+ api_key: opts.apiKey,
2832
+ base_url: opts.baseUrl,
2833
+ weight: opts.weight
2834
+ });
2835
+ }
2836
+ createOAuthAccount(opts) {
2837
+ if (this.findByName(opts.name)) {
2838
+ throw new Error(`Account '${opts.name}' already exists`);
2839
+ }
2840
+ return this.create({
2841
+ name: opts.name,
2842
+ provider: opts.provider,
2843
+ auth_method: "oauth",
2844
+ access_token: opts.accessToken,
2845
+ refresh_token: opts.refreshToken,
2846
+ expires_at: opts.expiresAt,
2847
+ account_uuid: opts.accountUuid,
2848
+ organization_uuid: opts.organizationUuid,
2849
+ quota_state: opts.quotaState,
2850
+ base_url: opts.baseUrl,
2851
+ priority: opts.priority,
2852
+ switch_threshold: opts.switchThreshold,
2853
+ weight: opts.weight
2854
+ });
2855
+ }
2856
+ findById(accountId) {
2857
+ const row = this.get(`
2858
+ SELECT
2859
+ ${accountSelectFields}
2860
+ FROM accounts
2861
+ WHERE id = ?
2862
+ `, [accountId]);
2863
+ return row ? toAccount(row) : null;
2864
+ }
2865
+ findByName(name) {
2866
+ const row = this.get(`
2867
+ SELECT
2868
+ ${accountSelectFields}
2869
+ FROM accounts
2870
+ WHERE name = ?
2871
+ `, [name]);
2872
+ return row ? toAccount(row) : null;
2873
+ }
2874
+ findByProvider(provider) {
2875
+ const rows = this.query(`
2876
+ SELECT
2877
+ ${accountSelectFields}
2878
+ FROM accounts
2879
+ WHERE provider = ?
2880
+ `, [provider]);
2881
+ return rows.map(toAccount);
2882
+ }
2883
+ findAvailableForProvider(provider) {
2884
+ const now = Date.now();
2885
+ const rows = this.query(`
2886
+ SELECT
2887
+ ${accountSelectFields}
2888
+ FROM accounts
2889
+ WHERE provider = ?
2890
+ AND COALESCE(paused, 0) = 0
2891
+ AND (rate_limited_until IS NULL OR rate_limited_until < ?)
2892
+ `, [provider, now]);
2893
+ return rows.map(toAccount);
2894
+ }
2895
+ update(accountId, data) {
2896
+ const updates = [];
2897
+ const params = [];
2898
+ if (data.name !== undefined) {
2899
+ updates.push("name = ?");
2900
+ params.push(data.name);
2901
+ }
2902
+ if ("base_url" in data) {
2903
+ updates.push("base_url = ?");
2904
+ params.push(data.base_url ?? null);
2905
+ }
2906
+ if (data.priority !== undefined) {
2907
+ updates.push("priority = ?");
2908
+ params.push(data.priority);
2909
+ }
2910
+ if ("switch_threshold" in data) {
2911
+ updates.push("switch_threshold = ?");
2912
+ params.push(data.switch_threshold ? JSON.stringify(data.switch_threshold) : null);
2913
+ }
2914
+ if (updates.length === 0) {
2915
+ return this.findById(accountId);
2916
+ }
2917
+ params.push(accountId);
2918
+ const changes = this.runWithChanges(`UPDATE accounts SET ${updates.join(", ")} WHERE id = ?`, params);
2919
+ return changes > 0 ? this.findById(accountId) : null;
2920
+ }
2921
+ delete(accountId) {
2922
+ return this.runWithChanges(`DELETE FROM accounts WHERE id = ?`, [accountId]) > 0;
2923
+ }
2924
+ count() {
2925
+ const result = this.get("SELECT COUNT(*) as count FROM accounts");
2926
+ return result?.count ?? 0;
2927
+ }
2928
+ updateTokens(accountId, accessToken, expiresAt, refreshToken) {
2929
+ if (refreshToken) {
2930
+ this.run(`UPDATE accounts SET access_token = ?, expires_at = ?, refresh_token = ? WHERE id = ?`, [accessToken, expiresAt, refreshToken, accountId]);
2931
+ } else {
2932
+ this.run(`UPDATE accounts SET access_token = ?, expires_at = ? WHERE id = ?`, [accessToken, expiresAt, accountId]);
2933
+ }
2934
+ }
2935
+ updateQuotaState(accountId, quotaState) {
2936
+ this.run(`UPDATE accounts SET quota_state = ? WHERE id = ?`, [
2937
+ JSON.stringify(quotaState),
2938
+ accountId
2939
+ ]);
2940
+ }
2941
+ incrementUsage(accountId, sessionDurationMs) {
2942
+ const now = Date.now();
2943
+ this.run(`
2944
+ UPDATE accounts
2945
+ SET
2946
+ last_used = ?,
2947
+ request_count = request_count + 1,
2948
+ total_requests = total_requests + 1,
2949
+ session_start = CASE
2950
+ WHEN session_start IS NULL OR ? - session_start >= ? THEN ?
2951
+ ELSE session_start
2952
+ END,
2953
+ session_request_count = CASE
2954
+ WHEN session_start IS NULL OR ? - session_start >= ? THEN 1
2955
+ ELSE session_request_count + 1
2956
+ END
2957
+ WHERE id = ?
2958
+ `, [now, now, sessionDurationMs, now, now, sessionDurationMs, accountId]);
2959
+ }
2960
+ setRateLimited(accountId, until) {
2961
+ this.run(`UPDATE accounts SET rate_limited_until = ? WHERE id = ?`, [
2962
+ until,
2963
+ accountId
2964
+ ]);
2965
+ }
2966
+ updateRateLimitMeta(accountId, status, reset, remaining) {
2967
+ this.run(`UPDATE accounts SET rate_limit_status = ?, rate_limit_reset = ?, rate_limit_remaining = ? WHERE id = ?`, [status, reset, remaining ?? null, accountId]);
2968
+ }
2969
+ pause(accountId) {
2970
+ this.run(`UPDATE accounts SET paused = 1, error_reason = NULL WHERE id = ?`, [accountId]);
2971
+ }
2972
+ markError(accountId, reason) {
2973
+ this.run(`UPDATE accounts SET paused = 1, error_reason = ? WHERE id = ?`, [
2974
+ reason,
2975
+ accountId
2976
+ ]);
2977
+ }
2978
+ resume(accountId) {
2979
+ this.run(`UPDATE accounts SET paused = 0, error_reason = NULL WHERE id = ?`, [accountId]);
2980
+ }
2981
+ resetSession(accountId, timestamp) {
2982
+ this.run(`UPDATE accounts SET session_start = ?, session_request_count = 0 WHERE id = ?`, [timestamp, accountId]);
2983
+ }
2984
+ updateRequestCount(accountId, count) {
2985
+ this.run(`UPDATE accounts SET session_request_count = ? WHERE id = ?`, [
2986
+ count,
2987
+ accountId
2988
+ ]);
2989
+ }
2990
+ resetStatistics(resetSessionStart = false) {
2991
+ if (resetSessionStart) {
2992
+ this.run(`UPDATE accounts SET request_count = 0, session_start = NULL, session_request_count = 0`);
2993
+ return;
2994
+ }
2995
+ this.run(`UPDATE accounts SET request_count = 0, session_request_count = 0`);
2996
+ }
2997
+ };
2998
+ });
2999
+
3000
+ // ../../packages/database/src/repositories/analytics.repository.ts
3001
+ var AnalyticsRepository;
3002
+ var init_analytics_repository = __esm(() => {
3003
+ init_src();
3004
+ AnalyticsRepository = class AnalyticsRepository extends BaseRepository {
3005
+ getAnalytics(options) {
3006
+ const includeModelBreakdown = options.includeModelBreakdown === true;
3007
+ const { whereClause, queryParams } = this.buildFilters(options);
3008
+ const combined = this.get(`
3009
+ SELECT
3010
+ COUNT(*) as total_requests,
3011
+ SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) * 100.0 /
3012
+ NULLIF(SUM(CASE WHEN success IS NOT NULL THEN 1 ELSE 0 END), 0) as success_rate,
3013
+ AVG(response_time_ms) as avg_response_time,
3014
+ SUM(COALESCE(total_tokens, 0)) as total_tokens,
3015
+ SUM(COALESCE(cost_usd, 0)) as total_cost_usd,
3016
+ AVG(output_tokens_per_second) as avg_tokens_per_second,
3017
+ COUNT(DISTINCT COALESCE(account_used, ?)) as active_accounts,
3018
+ SUM(COALESCE(input_tokens, 0)) as input_tokens,
3019
+ SUM(COALESCE(cache_read_input_tokens, 0)) as cache_read_input_tokens,
3020
+ SUM(COALESCE(cache_creation_input_tokens, 0)) as cache_creation_input_tokens,
3021
+ SUM(COALESCE(output_tokens, 0)) as output_tokens
3022
+ FROM requests r
3023
+ WHERE ${whereClause}
3024
+ `, [NO_ACCOUNT_ID, ...queryParams]);
3025
+ const timeSeries = this.query(`
3026
+ SELECT
3027
+ (timestamp / ?) * ? as ts,
3028
+ ${includeModelBreakdown ? "model," : ""}
3029
+ COUNT(*) as requests,
3030
+ SUM(COALESCE(total_tokens, 0)) as tokens,
3031
+ SUM(COALESCE(cost_usd, 0)) as cost_usd,
3032
+ SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) * 100.0 /
3033
+ NULLIF(SUM(CASE WHEN success IS NOT NULL THEN 1 ELSE 0 END), 0) as success_rate,
3034
+ SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) * 100.0 /
3035
+ NULLIF(SUM(CASE WHEN success IS NOT NULL THEN 1 ELSE 0 END), 0) as error_rate,
3036
+ SUM(COALESCE(cache_read_input_tokens, 0)) * 100.0 /
3037
+ NULLIF(SUM(COALESCE(input_tokens, 0) + COALESCE(cache_read_input_tokens, 0) + COALESCE(cache_creation_input_tokens, 0)), 0) as cache_hit_rate,
3038
+ AVG(response_time_ms) as avg_response_time,
3039
+ AVG(output_tokens_per_second) as avg_tokens_per_second
3040
+ FROM requests r
3041
+ WHERE ${whereClause} ${includeModelBreakdown ? "AND model IS NOT NULL" : ""}
3042
+ GROUP BY ts${includeModelBreakdown ? ", model" : ""}
3043
+ ORDER BY ts${includeModelBreakdown ? ", model" : ""}
3044
+ `, [options.bucketMs, options.bucketMs, ...queryParams]);
3045
+ const modelDistribution = this.query(`
3046
+ SELECT
3047
+ model,
3048
+ COUNT(*) as count
3049
+ FROM requests r
3050
+ WHERE ${whereClause} AND model IS NOT NULL
3051
+ GROUP BY model
3052
+ ORDER BY count DESC
3053
+ LIMIT 10
3054
+ `, queryParams);
3055
+ const accountPerformance = this.query(`
3056
+ SELECT
3057
+ COALESCE(a.name, ?) as name,
3058
+ COUNT(r.id) as requests,
3059
+ SUM(CASE WHEN r.success = 1 THEN 1 ELSE 0 END) * 100.0 /
3060
+ NULLIF(SUM(CASE WHEN r.success IS NOT NULL THEN 1 ELSE 0 END), 0) as success_rate
3061
+ FROM requests r
3062
+ LEFT JOIN accounts a ON a.id = r.account_used
3063
+ WHERE ${whereClause}
3064
+ GROUP BY name
3065
+ HAVING requests > 0
3066
+ ORDER BY requests DESC
3067
+ `, [NO_ACCOUNT_ID, ...queryParams]);
3068
+ const providerBreakdown = this.query(`
3069
+ SELECT
3070
+ r.provider as provider,
3071
+ COUNT(*) as requests,
3072
+ SUM(CASE WHEN r.success = 1 THEN 1 ELSE 0 END) * 100.0 /
3073
+ NULLIF(SUM(CASE WHEN r.success IS NOT NULL THEN 1 ELSE 0 END), 0) as success_rate,
3074
+ SUM(COALESCE(r.total_tokens, 0)) as total_tokens,
3075
+ SUM(COALESCE(r.cost_usd, 0)) as total_cost_usd
3076
+ FROM requests r
3077
+ WHERE ${whereClause}
3078
+ GROUP BY r.provider
3079
+ ORDER BY requests DESC
3080
+ `, queryParams).map((row) => ({
3081
+ provider: row.provider,
3082
+ requests: row.requests,
3083
+ successRate: row.success_rate ?? 0,
3084
+ totalTokens: row.total_tokens ?? 0,
3085
+ totalCostUsd: row.total_cost_usd ?? 0
3086
+ }));
3087
+ const modelPerformance = this.query(`
3088
+ WITH model_stats AS (
3089
+ SELECT
3090
+ model,
3091
+ AVG(response_time_ms) as avg_response_time,
3092
+ COUNT(*) as total_requests,
3093
+ SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) * 100.0 /
3094
+ NULLIF(SUM(CASE WHEN success IS NOT NULL THEN 1 ELSE 0 END), 0) as error_rate,
3095
+ AVG(output_tokens_per_second) as avg_tokens_per_second,
3096
+ MIN(CASE WHEN output_tokens_per_second > 0 THEN output_tokens_per_second ELSE NULL END) as min_tokens_per_second,
3097
+ MAX(output_tokens_per_second) as max_tokens_per_second
3098
+ FROM requests r
3099
+ WHERE ${whereClause} AND model IS NOT NULL
3100
+ GROUP BY model
3101
+ ORDER BY total_requests DESC
3102
+ LIMIT 10
3103
+ ),
3104
+ ranked_times AS (
3105
+ SELECT
3106
+ r.model,
3107
+ r.response_time_ms,
3108
+ ROW_NUMBER() OVER (PARTITION BY r.model ORDER BY r.response_time_ms) as row_num,
3109
+ COUNT(*) OVER (PARTITION BY r.model) as total_count
3110
+ FROM requests r
3111
+ INNER JOIN model_stats ms ON ms.model = r.model
3112
+ WHERE ${whereClause} AND r.response_time_ms IS NOT NULL
3113
+ )
3114
+ SELECT
3115
+ ms.*,
3116
+ rt.response_time_ms as p95_response_time
3117
+ FROM model_stats ms
3118
+ LEFT JOIN ranked_times rt
3119
+ ON rt.model = ms.model
3120
+ AND rt.row_num = CAST(CEIL(rt.total_count * 0.95) AS INTEGER)
3121
+ ORDER BY ms.total_requests DESC
3122
+ `, [...queryParams, ...queryParams]).map((row) => ({
3123
+ model: row.model,
3124
+ avgResponseTime: row.avg_response_time ?? 0,
3125
+ p95ResponseTime: row.p95_response_time ?? row.avg_response_time ?? 0,
3126
+ errorRate: row.error_rate ?? 0,
3127
+ avgTokensPerSecond: row.avg_tokens_per_second ?? null,
3128
+ minTokensPerSecond: row.min_tokens_per_second ?? null,
3129
+ maxTokensPerSecond: row.max_tokens_per_second ?? null
3130
+ }));
3131
+ const costByModel = this.query(`
3132
+ SELECT
3133
+ model,
3134
+ SUM(COALESCE(cost_usd, 0)) as cost_usd,
3135
+ COUNT(*) as requests,
3136
+ SUM(COALESCE(total_tokens, 0)) as total_tokens
3137
+ FROM requests r
3138
+ WHERE ${whereClause} AND COALESCE(cost_usd, 0) > 0 AND model IS NOT NULL
3139
+ GROUP BY model
3140
+ ORDER BY cost_usd DESC
3141
+ LIMIT 10
3142
+ `, queryParams).map((row) => ({
3143
+ model: row.model,
3144
+ costUsd: row.cost_usd ?? 0,
3145
+ requests: row.requests,
3146
+ totalTokens: row.total_tokens ?? 0
3147
+ }));
3148
+ return {
3149
+ totals: {
3150
+ requests: combined?.total_requests ?? 0,
3151
+ successRate: combined?.success_rate ?? 0,
3152
+ activeAccounts: combined?.active_accounts ?? 0,
3153
+ avgResponseTime: combined?.avg_response_time ?? 0,
3154
+ totalTokens: combined?.total_tokens ?? 0,
3155
+ totalCostUsd: combined?.total_cost_usd ?? 0,
3156
+ avgTokensPerSecond: combined?.avg_tokens_per_second ?? null
3157
+ },
3158
+ timeSeries: timeSeries.map((point) => ({
3159
+ ts: point.ts,
3160
+ ...point.model ? { model: point.model } : {},
3161
+ requests: point.requests,
3162
+ tokens: point.tokens ?? 0,
3163
+ costUsd: point.cost_usd ?? 0,
3164
+ successRate: point.success_rate ?? 0,
3165
+ errorRate: point.error_rate ?? 0,
3166
+ cacheHitRate: point.cache_hit_rate ?? 0,
3167
+ avgResponseTime: point.avg_response_time ?? 0,
3168
+ avgTokensPerSecond: point.avg_tokens_per_second ?? null
3169
+ })),
3170
+ tokenBreakdown: {
3171
+ inputTokens: combined?.input_tokens ?? 0,
3172
+ cacheReadInputTokens: combined?.cache_read_input_tokens ?? 0,
3173
+ cacheCreationInputTokens: combined?.cache_creation_input_tokens ?? 0,
3174
+ outputTokens: combined?.output_tokens ?? 0
3175
+ },
3176
+ modelDistribution,
3177
+ accountPerformance: accountPerformance.map((account2) => ({
3178
+ name: account2.name,
3179
+ requests: account2.requests,
3180
+ successRate: account2.success_rate ?? 0
3181
+ })),
3182
+ providerBreakdown,
3183
+ costByModel,
3184
+ modelPerformance
3185
+ };
3186
+ }
3187
+ buildFilters(options) {
3188
+ const conditions = ["timestamp > ?"];
3189
+ const queryParams = [options.startMs];
3190
+ const accounts = options.accounts ?? [];
3191
+ const models = options.models ?? [];
3192
+ const providers = options.providers ?? [];
3193
+ const status = options.status ?? "all";
3194
+ if (accounts.length > 0) {
3195
+ const placeholders = accounts.map(() => "?").join(",");
3196
+ conditions.push(`(
3197
+ r.account_used IN (SELECT id FROM accounts WHERE name IN (${placeholders}))
3198
+ OR (r.account_used = ? AND ? IN (${placeholders}))
3199
+ )`);
3200
+ queryParams.push(...accounts, NO_ACCOUNT_ID, NO_ACCOUNT_ID, ...accounts);
3201
+ }
3202
+ if (models.length > 0) {
3203
+ const placeholders = models.map(() => "?").join(",");
3204
+ conditions.push(`model IN (${placeholders})`);
3205
+ queryParams.push(...models);
3206
+ }
3207
+ if (providers.length > 0) {
3208
+ const placeholders = providers.map(() => "?").join(",");
3209
+ conditions.push(`r.provider IN (${placeholders})`);
3210
+ queryParams.push(...providers);
3211
+ }
3212
+ if (status === "success") {
3213
+ conditions.push("success = 1");
3214
+ } else if (status === "error") {
3215
+ conditions.push("success = 0");
3216
+ }
3217
+ return {
3218
+ whereClause: conditions.join(" AND "),
3219
+ queryParams
3220
+ };
3221
+ }
3222
+ };
3223
+ });
3224
+
3225
+ // ../../packages/database/src/repositories/auth-session.repository.ts
3226
+ var AuthSessionRepository;
3227
+ var init_auth_session_repository = __esm(() => {
3228
+ AuthSessionRepository = class AuthSessionRepository extends BaseRepository {
3229
+ getActiveRowById(id) {
3230
+ return this.get(`
3231
+ SELECT
3232
+ id,
3233
+ provider,
3234
+ auth_method,
3235
+ account_name,
3236
+ state_json,
3237
+ created_at,
3238
+ expires_at
3239
+ FROM auth_sessions
3240
+ WHERE id = ? AND expires_at > ?
3241
+ `, [id, new Date().toISOString()]);
3242
+ }
3243
+ getActiveRowByState(state) {
3244
+ return this.get(`
3245
+ SELECT
3246
+ id,
3247
+ provider,
3248
+ auth_method,
3249
+ account_name,
3250
+ state_json,
3251
+ created_at,
3252
+ expires_at
3253
+ FROM auth_sessions
3254
+ WHERE expires_at > ?
3255
+ AND json_valid(state_json) = 1
3256
+ AND json_extract(state_json, '$.state') = ?
3257
+ `, [new Date().toISOString(), state]);
3258
+ }
3259
+ toAuthSession(row) {
3260
+ return {
3261
+ id: row.id,
3262
+ provider: row.provider,
3263
+ authMethod: row.auth_method,
3264
+ accountName: row.account_name,
3265
+ stateJson: row.state_json,
3266
+ createdAt: row.created_at,
3267
+ expiresAt: row.expires_at
3268
+ };
3269
+ }
3270
+ createSession(provider, authMethod, accountName, stateJson, expiresAt) {
3271
+ const id = crypto.randomUUID();
3272
+ const createdAt = new Date().toISOString();
3273
+ const expiresAtIso = new Date(expiresAt).toISOString();
3274
+ this.run(`
3275
+ INSERT INTO auth_sessions (
3276
+ id,
3277
+ provider,
3278
+ auth_method,
3279
+ account_name,
3280
+ state_json,
3281
+ created_at,
3282
+ expires_at
3283
+ )
3284
+ VALUES (?, ?, ?, ?, ?, ?, ?)
3285
+ `, [
3286
+ id,
3287
+ provider,
3288
+ authMethod,
3289
+ accountName,
3290
+ stateJson,
3291
+ createdAt,
3292
+ expiresAtIso
3293
+ ]);
3294
+ return id;
3295
+ }
3296
+ getSession(id) {
3297
+ const row = this.getActiveRowById(id);
3298
+ if (!row) {
3299
+ return null;
3300
+ }
3301
+ return this.toAuthSession(row);
3302
+ }
3303
+ getSessionByState(state) {
3304
+ const row = this.getActiveRowByState(state);
3305
+ if (!row) {
3306
+ return null;
3307
+ }
3308
+ return this.toAuthSession(row);
3309
+ }
3310
+ updateSessionState(id, stateJson, expiresAt) {
3311
+ if (typeof expiresAt === "number") {
3312
+ this.run("UPDATE auth_sessions SET state_json = ?, expires_at = ? WHERE id = ?", [stateJson, new Date(expiresAt).toISOString(), id]);
3313
+ return;
3314
+ }
3315
+ this.run("UPDATE auth_sessions SET state_json = ? WHERE id = ?", [
3316
+ stateJson,
3317
+ id
3318
+ ]);
3319
+ }
3320
+ deleteSession(id) {
3321
+ this.run("DELETE FROM auth_sessions WHERE id = ?", [id]);
3322
+ }
3323
+ deleteExpiredSessions() {
3324
+ return this.runWithChanges("DELETE FROM auth_sessions WHERE expires_at <= ?", [new Date().toISOString()]);
3325
+ }
3326
+ };
3327
+ });
3328
+
3329
+ // ../../packages/database/src/repositories/conversation-pin.repository.ts
3330
+ var ConversationPinRepository;
3331
+ var init_conversation_pin_repository = __esm(() => {
3332
+ ConversationPinRepository = class ConversationPinRepository extends BaseRepository {
3333
+ find(pinKey) {
3334
+ const row = this.get(`SELECT account_id, last_seen_at FROM conversation_pins WHERE pin_key = ?`, [pinKey]);
3335
+ return row ? { accountId: row.account_id, lastSeenAt: row.last_seen_at } : null;
3336
+ }
3337
+ upsert(pinKey, accountId, lastSeenAt) {
3338
+ this.run(`
3339
+ INSERT INTO conversation_pins (pin_key, account_id, last_seen_at)
3340
+ VALUES (?, ?, ?)
3341
+ ON CONFLICT(pin_key) DO UPDATE SET
3342
+ account_id = excluded.account_id,
3343
+ last_seen_at = excluded.last_seen_at
3344
+ `, [pinKey, accountId, lastSeenAt]);
3345
+ }
3346
+ countByAccount(activeSince) {
3347
+ const rows = this.query(`
3348
+ SELECT account_id, COUNT(*) AS count
3349
+ FROM conversation_pins
3350
+ WHERE last_seen_at >= ?
3351
+ GROUP BY account_id
3352
+ `, [activeSince]);
3353
+ return new Map(rows.map((row) => [row.account_id, row.count]));
3354
+ }
3355
+ deleteExpired(before) {
3356
+ this.run(`DELETE FROM conversation_pins WHERE last_seen_at < ?`, [before]);
3357
+ }
3358
+ deleteForAccount(accountId) {
3359
+ this.run(`DELETE FROM conversation_pins WHERE account_id = ?`, [accountId]);
3360
+ }
3361
+ };
3362
+ });
3363
+
3364
+ // ../../packages/database/src/models/request-row.ts
3365
+ function toRequest(row) {
3366
+ return {
3367
+ id: row.id,
3368
+ timestamp: row.timestamp,
3369
+ method: row.method,
3370
+ path: row.path,
3371
+ provider: row.provider,
3372
+ upstreamPath: row.upstream_path,
3373
+ accountUsed: row.account_used,
3374
+ statusCode: row.status_code,
3375
+ success: row.success === null ? null : row.success === 1,
3376
+ errorMessage: row.error_message,
3377
+ responseTimeMs: row.response_time_ms,
3378
+ failoverAttempts: row.failover_attempts,
3379
+ model: row.model ?? null,
3380
+ promptTokens: row.prompt_tokens ?? null,
3381
+ completionTokens: row.completion_tokens ?? null,
3382
+ totalTokens: row.total_tokens ?? null,
3383
+ costUsd: row.cost_usd ?? null,
3384
+ inputTokens: row.input_tokens ?? null,
3385
+ cacheReadInputTokens: row.cache_read_input_tokens ?? null,
3386
+ cacheCreationInputTokens: row.cache_creation_input_tokens ?? null,
3387
+ outputTokens: row.output_tokens ?? null,
3388
+ reasoningTokens: row.reasoning_tokens ?? null,
3389
+ tokensPerSecond: row.output_tokens_per_second ?? null,
3390
+ ttftMs: row.ttft_ms ?? null,
3391
+ proxyOverheadMs: row.proxy_overhead_ms ?? null,
3392
+ upstreamTtfbMs: row.upstream_ttfb_ms ?? null,
3393
+ streamingDurationMs: row.streaming_duration_ms ?? null,
3394
+ responseId: row.response_id ?? null,
3395
+ previousResponseId: row.previous_response_id ?? null,
3396
+ responseChainId: row.response_chain_id ?? null,
3397
+ clientSessionId: row.client_session_id ?? null
3398
+ };
3399
+ }
3400
+ function toRequestWithAccountName(row) {
3401
+ return {
3402
+ ...toRequest(row),
3403
+ accountName: row.account_name ?? null
3404
+ };
3405
+ }
3406
+
3407
+ // ../../packages/database/src/repositories/request.repository.ts
3408
+ function requestLinkageFromPayload(payload) {
3409
+ const fromBodies = extractRequestLinkageFromPayload(payload);
3410
+ const trace = isRecord(payload) && isRecord(payload.meta) ? payload.meta.trace : null;
3411
+ if (!isRecord(trace)) {
3412
+ return fromBodies;
3413
+ }
3414
+ return {
3415
+ previousResponseId: typeof trace.previousResponseId === "string" ? trace.previousResponseId : fromBodies.previousResponseId,
3416
+ responseId: typeof trace.responseId === "string" ? trace.responseId : fromBodies.responseId,
3417
+ clientSessionId: typeof trace.clientSessionId === "string" ? trace.clientSessionId : fromBodies.clientSessionId
3418
+ };
3419
+ }
3420
+ var log6, RequestRepository;
3421
+ var init_request_repository = __esm(() => {
3422
+ init_src3();
3423
+ init_src();
3424
+ log6 = new Logger("RequestRepository");
3425
+ RequestRepository = class RequestRepository extends BaseRepository {
3426
+ saveMeta(id, method, path, provider, upstreamPath, accountUsed, statusCode, timestamp) {
3427
+ this.run(`
3428
+ INSERT INTO requests (
3429
+ id, timestamp, method, path, provider, upstream_path, account_used,
3430
+ status_code, success, error_message, response_time_ms, failover_attempts
3431
+ )
3432
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, NULL, 0)
3433
+ `, [
3434
+ id,
3435
+ timestamp ?? Date.now(),
3436
+ method,
3437
+ path,
3438
+ provider,
3439
+ upstreamPath,
3440
+ accountUsed,
3441
+ statusCode
3442
+ ]);
3443
+ }
3444
+ save(data) {
3445
+ const { usage, timings } = data;
3446
+ const payloadJson = data.payload === undefined ? undefined : JSON.stringify(data.payload);
3447
+ const linkage = requestLinkageFromPayload(data.payload);
3448
+ const responseChainId = this.resolveResponseChainId(linkage.previousResponseId, linkage.responseId, data.id);
3449
+ this.db.run("BEGIN");
3450
+ try {
3451
+ this.run(`
3452
+ INSERT OR IGNORE INTO requests (
3453
+ id, timestamp, method, path, provider, upstream_path, account_used,
3454
+ status_code, success, error_message, response_time_ms, failover_attempts
3455
+ )
3456
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, NULL, 0)
3457
+ `, [
3458
+ data.id,
3459
+ data.timestamp ?? Date.now(),
3460
+ data.method,
3461
+ data.path,
3462
+ data.provider,
3463
+ data.upstreamPath,
3464
+ data.accountUsed,
3465
+ data.statusCode
3466
+ ]);
3467
+ this.run(`
3468
+ UPDATE requests
3469
+ SET
3470
+ method = ?,
3471
+ path = ?,
3472
+ provider = ?,
3473
+ upstream_path = ?,
3474
+ account_used = ?,
3475
+ status_code = ?,
3476
+ success = ?,
3477
+ error_message = ?,
3478
+ response_time_ms = ?,
3479
+ failover_attempts = ?,
3480
+ model = ?,
3481
+ prompt_tokens = ?,
3482
+ completion_tokens = ?,
3483
+ total_tokens = ?,
3484
+ cost_usd = ?,
3485
+ input_tokens = ?,
3486
+ cache_read_input_tokens = ?,
3487
+ cache_creation_input_tokens = ?,
3488
+ output_tokens = ?,
3489
+ reasoning_tokens = ?,
3490
+ output_tokens_per_second = ?,
3491
+ ttft_ms = ?,
3492
+ proxy_overhead_ms = ?,
3493
+ upstream_ttfb_ms = ?,
3494
+ streaming_duration_ms = ?,
3495
+ response_id = ?,
3496
+ previous_response_id = ?,
3497
+ response_chain_id = ?,
3498
+ client_session_id = ?
3499
+ WHERE id = ?
3500
+ `, [
3501
+ data.method,
3502
+ data.path,
3503
+ data.provider,
3504
+ data.upstreamPath,
3505
+ data.accountUsed,
3506
+ data.statusCode,
3507
+ data.success ? 1 : 0,
3508
+ data.errorMessage,
3509
+ data.responseTime,
3510
+ data.failoverAttempts,
3511
+ usage?.model ?? null,
3512
+ usage?.promptTokens ?? null,
3513
+ usage?.completionTokens ?? null,
3514
+ usage?.totalTokens ?? null,
3515
+ usage?.costUsd ?? null,
3516
+ usage?.inputTokens ?? null,
3517
+ usage?.cacheReadInputTokens ?? null,
3518
+ usage?.cacheCreationInputTokens ?? null,
3519
+ usage?.outputTokens ?? null,
3520
+ usage?.reasoningTokens ?? null,
3521
+ usage?.tokensPerSecond ?? null,
3522
+ timings?.ttftMs ?? null,
3523
+ timings?.proxyOverheadMs ?? null,
3524
+ timings?.upstreamTtfbMs ?? null,
3525
+ timings?.streamingDurationMs ?? null,
3526
+ linkage.responseId,
3527
+ linkage.previousResponseId,
3528
+ responseChainId,
3529
+ linkage.clientSessionId,
3530
+ data.id
3531
+ ]);
3532
+ if (payloadJson !== undefined) {
3533
+ this.run(`
3534
+ INSERT INTO request_payloads (id, json)
3535
+ VALUES (?, ?)
3536
+ ON CONFLICT(id) DO UPDATE SET json = excluded.json
3537
+ `, [data.id, payloadJson]);
3538
+ }
3539
+ this.db.run("COMMIT");
3540
+ } catch (error) {
3541
+ this.db.run("ROLLBACK");
3542
+ throw error;
3543
+ }
3544
+ }
3545
+ resolveResponseChainId(previousResponseId, responseId, requestId) {
3546
+ if (previousResponseId) {
3547
+ const parentRow = this.get(`
3548
+ SELECT response_chain_id
3549
+ FROM requests
3550
+ WHERE response_id = ?
3551
+ LIMIT 1
3552
+ `, [previousResponseId]);
3553
+ return parentRow?.response_chain_id ?? previousResponseId;
3554
+ }
3555
+ return responseId ?? requestId;
3556
+ }
3557
+ updateUsage(requestId, usage) {
3558
+ if (!usage)
3559
+ return;
3560
+ this.run(`
3561
+ UPDATE requests
3562
+ SET
3563
+ model = COALESCE(?, model),
3564
+ prompt_tokens = COALESCE(?, prompt_tokens),
3565
+ completion_tokens = COALESCE(?, completion_tokens),
3566
+ total_tokens = COALESCE(?, total_tokens),
3567
+ cost_usd = COALESCE(?, cost_usd),
3568
+ input_tokens = COALESCE(?, input_tokens),
3569
+ cache_read_input_tokens = COALESCE(?, cache_read_input_tokens),
3570
+ cache_creation_input_tokens = COALESCE(?, cache_creation_input_tokens),
3571
+ output_tokens = COALESCE(?, output_tokens),
3572
+ reasoning_tokens = COALESCE(?, reasoning_tokens),
3573
+ output_tokens_per_second = COALESCE(?, output_tokens_per_second)
3574
+ WHERE id = ?
3575
+ `, [
3576
+ usage.model ?? null,
3577
+ usage.promptTokens ?? null,
3578
+ usage.completionTokens ?? null,
3579
+ usage.totalTokens ?? null,
3580
+ usage.costUsd ?? null,
3581
+ usage.inputTokens ?? null,
3582
+ usage.cacheReadInputTokens ?? null,
3583
+ usage.cacheCreationInputTokens ?? null,
3584
+ usage.outputTokens ?? null,
3585
+ usage.reasoningTokens ?? null,
3586
+ usage.tokensPerSecond ?? null,
3587
+ requestId
3588
+ ]);
3589
+ }
3590
+ savePayload(id, data) {
3591
+ const json = JSON.stringify(data);
3592
+ this.run(`
3593
+ INSERT INTO request_payloads (id, json)
3594
+ VALUES (?, ?)
3595
+ ON CONFLICT(id) DO UPDATE SET json = excluded.json
3596
+ `, [id, json]);
3597
+ }
3598
+ getPayload(id) {
3599
+ const row = this.get(`SELECT json FROM request_payloads WHERE id = ?`, [id]);
3600
+ if (!row)
3601
+ return null;
3602
+ try {
3603
+ return JSON.parse(row.json);
3604
+ } catch (error) {
3605
+ log6.warn(`Failed to parse request payload for ${id}`, error);
3606
+ return null;
3607
+ }
3608
+ }
3609
+ listPayloads(limit = 50) {
3610
+ return this.query(`
3611
+ SELECT rp.id, rp.json
3612
+ FROM request_payloads rp
3613
+ JOIN requests r ON rp.id = r.id
3614
+ ORDER BY r.timestamp DESC
3615
+ LIMIT ?
3616
+ `, [limit]);
3617
+ }
3618
+ listPayloadsWithAccountNames(limit = 50) {
3619
+ return this.query(`
3620
+ SELECT rp.id, rp.json, a.name as account_name
3621
+ FROM request_payloads rp
3622
+ JOIN requests r ON rp.id = r.id
3623
+ LEFT JOIN accounts a ON r.account_used = a.id
3624
+ ORDER BY r.timestamp DESC
3625
+ LIMIT ?
3626
+ `, [limit]);
3627
+ }
3628
+ listResponseChainPayloadsWithAccountNames(requestId) {
3629
+ return this.query(`
3630
+ WITH RECURSIVE chain AS (
3631
+ SELECT
3632
+ r.id,
3633
+ r.previous_response_id,
3634
+ 0 AS depth,
3635
+ (',' || r.id || ',') AS visited
3636
+ FROM requests r
3637
+ WHERE r.id = ?
3638
+ UNION ALL
3639
+ SELECT
3640
+ parent.id,
3641
+ parent.previous_response_id,
3642
+ chain.depth + 1 AS depth,
3643
+ (chain.visited || parent.id || ',') AS visited
3644
+ FROM chain
3645
+ JOIN requests parent
3646
+ ON parent.response_id = chain.previous_response_id
3647
+ WHERE
3648
+ chain.previous_response_id IS NOT NULL
3649
+ AND instr(chain.visited, ',' || parent.id || ',') = 0
3650
+ )
3651
+ SELECT rp.id, rp.json, a.name AS account_name
3652
+ FROM chain
3653
+ JOIN requests r ON r.id = chain.id
3654
+ JOIN request_payloads rp ON rp.id = r.id
3655
+ LEFT JOIN accounts a ON r.account_used = a.id
3656
+ ORDER BY chain.depth DESC, r.timestamp ASC
3657
+ `, [requestId]);
3658
+ }
3659
+ getRecentRequests(limit = 100) {
3660
+ return this.query(`
3661
+ SELECT *
3662
+ FROM requests
3663
+ ORDER BY timestamp DESC
3664
+ LIMIT ?
3665
+ `, [limit]).flatMap((row) => {
3666
+ if (!isHttpMethod(row.method) || row.provider.length === 0) {
3667
+ return [];
3668
+ }
3669
+ return [toRequest(row)];
3670
+ });
3671
+ }
3672
+ listSummaries(limit = 100) {
3673
+ return this.query(`
3674
+ SELECT r.*, a.name as account_name
3675
+ FROM requests r
3676
+ LEFT JOIN accounts a ON r.account_used = a.id
3677
+ ORDER BY r.timestamp DESC
3678
+ LIMIT ?
3679
+ `, [limit]).flatMap((row) => {
3680
+ if (!isHttpMethod(row.method) || row.provider.length === 0) {
3681
+ return [];
3682
+ }
3683
+ return [
3684
+ {
3685
+ ...toRequestSummary(toRequest(row)),
3686
+ accountName: row.account_name ?? null
3687
+ }
3688
+ ];
3689
+ });
3690
+ }
3691
+ listWithAccountNames(limit = 100) {
3692
+ return this.query(`
3693
+ SELECT r.*, a.name as account_name
3694
+ FROM requests r
3695
+ LEFT JOIN accounts a ON r.account_used = a.id
3696
+ ORDER BY r.timestamp DESC
3697
+ LIMIT ?
3698
+ `, [limit]).flatMap((row) => {
3699
+ if (!isHttpMethod(row.method) || row.provider.length === 0) {
3700
+ return [];
3701
+ }
3702
+ return [toRequestWithAccountName(row)];
3703
+ });
3704
+ }
3705
+ getRequestStats(since) {
3706
+ const whereClause = since ? "WHERE timestamp > ?" : "";
3707
+ const params = since ? [since] : [];
3708
+ const result = this.get(`
3709
+ SELECT
3710
+ COUNT(*) as total_requests,
3711
+ SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successful_requests,
3712
+ SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as failed_requests,
3713
+ AVG(response_time_ms) as avg_response_time
3714
+ FROM requests
3715
+ ${whereClause}
3716
+ `, params);
3717
+ return {
3718
+ totalRequests: result?.total_requests ?? 0,
3719
+ successfulRequests: result?.successful_requests ?? 0,
3720
+ failedRequests: result?.failed_requests ?? 0,
3721
+ avgResponseTime: result?.avg_response_time ?? null
3722
+ };
3723
+ }
3724
+ aggregateStats(rangeMs) {
3725
+ const whereClause = rangeMs ? "WHERE timestamp > ?" : "";
3726
+ const params = rangeMs ? [Date.now() - rangeMs] : [];
3727
+ const result = this.get(`
3728
+ SELECT
3729
+ COUNT(*) as total_requests,
3730
+ SUM(CASE WHEN success IS NOT NULL THEN 1 ELSE 0 END) as completed_requests,
3731
+ SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successful_requests,
3732
+ AVG(response_time_ms) as avg_response_time,
3733
+ SUM(
3734
+ COALESCE(
3735
+ total_tokens,
3736
+ COALESCE(input_tokens, 0) +
3737
+ COALESCE(output_tokens, 0) +
3738
+ COALESCE(cache_read_input_tokens, 0) +
3739
+ COALESCE(cache_creation_input_tokens, 0)
3740
+ )
3741
+ ) as total_tokens,
3742
+ SUM(cost_usd) as total_cost_usd,
3743
+ SUM(input_tokens) as input_tokens,
3744
+ SUM(output_tokens) as output_tokens,
3745
+ SUM(cache_read_input_tokens) as cache_read_input_tokens,
3746
+ SUM(cache_creation_input_tokens) as cache_creation_input_tokens,
3747
+ AVG(output_tokens_per_second) as avg_tokens_per_second
3748
+ FROM requests
3749
+ ${whereClause}
3750
+ `, params);
3751
+ return {
3752
+ totalRequests: result?.total_requests ?? 0,
3753
+ completedRequests: result?.completed_requests ?? 0,
3754
+ successfulRequests: result?.successful_requests ?? 0,
3755
+ avgResponseTime: result?.avg_response_time ?? null,
3756
+ totalTokens: result?.total_tokens ?? 0,
3757
+ totalCostUsd: result?.total_cost_usd ?? 0,
3758
+ inputTokens: result?.input_tokens ?? 0,
3759
+ outputTokens: result?.output_tokens ?? 0,
3760
+ cacheReadInputTokens: result?.cache_read_input_tokens ?? 0,
3761
+ cacheCreationInputTokens: result?.cache_creation_input_tokens ?? 0,
3762
+ avgTokensPerSecond: result?.avg_tokens_per_second ?? null
3763
+ };
3764
+ }
3765
+ getTopModels(limit = 10) {
3766
+ return this.query(`
3767
+ SELECT model, COUNT(*) as count
3768
+ FROM requests
3769
+ WHERE model IS NOT NULL
3770
+ GROUP BY model
3771
+ ORDER BY count DESC
3772
+ LIMIT ?
3773
+ `, [limit]);
3774
+ }
3775
+ getRecentErrors(limit = 10) {
3776
+ const errors = this.query(`
3777
+ SELECT error_message
3778
+ FROM (
3779
+ SELECT
3780
+ error_message,
3781
+ MAX(timestamp) as latest_timestamp
3782
+ FROM requests
3783
+ WHERE error_message IS NOT NULL
3784
+ AND error_message != ''
3785
+ GROUP BY error_message
3786
+ )
3787
+ ORDER BY latest_timestamp DESC
3788
+ LIMIT ?
3789
+ `, [limit]);
3790
+ return errors.map((e) => e.error_message);
3791
+ }
3792
+ getRequestsByAccount(since) {
3793
+ const whereClause = since ? "WHERE r.timestamp > ?" : "";
3794
+ const params = since ? [since] : [];
3795
+ return this.query(`
3796
+ SELECT
3797
+ r.account_used as account_id,
3798
+ a.name as account_name,
3799
+ COUNT(*) as request_count,
3800
+ SUM(CASE WHEN r.success = 1 THEN 1 ELSE 0 END) * 100.0 /
3801
+ NULLIF(SUM(CASE WHEN r.success IS NOT NULL THEN 1 ELSE 0 END), 0) as success_rate
3802
+ FROM requests r
3803
+ LEFT JOIN accounts a ON r.account_used = a.id
3804
+ ${whereClause}
3805
+ GROUP BY r.account_used
3806
+ ORDER BY request_count DESC
3807
+ `, params).map((row) => ({
3808
+ accountId: row.account_id,
3809
+ accountName: row.account_name,
3810
+ requestCount: row.request_count,
3811
+ successRate: row.success_rate
3812
+ }));
3813
+ }
3814
+ deleteOlderThan(cutoffTs) {
3815
+ return this.runWithChanges(`DELETE FROM requests WHERE timestamp < ?`, [
3816
+ cutoffTs
3817
+ ]);
3818
+ }
3819
+ clear() {
3820
+ return this.runWithChanges(`DELETE FROM requests`);
3821
+ }
3822
+ deleteOrphanedPayloads() {
3823
+ return this.runWithChanges(`DELETE FROM request_payloads WHERE id NOT IN (SELECT id FROM requests)`);
3824
+ }
3825
+ deletePayloadsOlderThan(cutoffTs) {
3826
+ return this.runWithChanges(`DELETE FROM request_payloads WHERE id IN (SELECT id FROM requests WHERE timestamp < ?)`, [cutoffTs]);
3827
+ }
3828
+ };
3829
+ });
3830
+
3831
+ // ../../packages/database/src/repositories/stats.repository.ts
3832
+ class StatsRepository {
3833
+ db;
3834
+ requests;
3835
+ constructor(db) {
3836
+ this.db = db;
3837
+ this.requests = new RequestRepository(db);
3838
+ }
3839
+ getAggregatedStats() {
3840
+ const stats2 = this.requests.aggregateStats();
3841
+ const successRate = stats2.completedRequests > 0 ? Math.round(stats2.successfulRequests / stats2.completedRequests * 100) : 0;
3842
+ return {
3843
+ totalRequests: stats2.totalRequests,
3844
+ completedRequests: stats2.completedRequests,
3845
+ successfulRequests: stats2.successfulRequests,
3846
+ successRate,
3847
+ avgResponseTime: stats2.avgResponseTime ?? 0,
3848
+ totalTokens: stats2.totalTokens,
3849
+ totalCostUsd: stats2.totalCostUsd,
3850
+ inputTokens: stats2.inputTokens,
3851
+ outputTokens: stats2.outputTokens,
3852
+ cacheReadInputTokens: stats2.cacheReadInputTokens,
3853
+ cacheCreationInputTokens: stats2.cacheCreationInputTokens,
3854
+ avgTokensPerSecond: stats2.avgTokensPerSecond
3855
+ };
3856
+ }
3857
+ getAccountStats(limit = 10, includeUnauthenticated = true) {
3858
+ const query = includeUnauthenticated ? `
3859
+ SELECT
3860
+ COALESCE(a.name, ?) as name,
3861
+ COUNT(r.id) as requestCount,
3862
+ COALESCE(a.total_requests, 0) as totalRequests,
3863
+ CASE
3864
+ WHEN SUM(CASE WHEN r.success IS NOT NULL THEN 1 ELSE 0 END) > 0
3865
+ THEN CAST(ROUND(SUM(CASE WHEN r.success = 1 THEN 1 ELSE 0 END) * 100.0 /
3866
+ SUM(CASE WHEN r.success IS NOT NULL THEN 1 ELSE 0 END)) AS INTEGER)
3867
+ ELSE 0
3868
+ END as successRate
3869
+ FROM requests r
3870
+ LEFT JOIN accounts a ON a.id = r.account_used
3871
+ GROUP BY COALESCE(a.id, ?), COALESCE(a.name, ?)
3872
+ HAVING requestCount > 0
3873
+ ORDER BY requestCount DESC
3874
+ LIMIT ?
3875
+ ` : `
3876
+ SELECT
3877
+ a.name,
3878
+ COUNT(r.id) as requestCount,
3879
+ a.total_requests as totalRequests,
3880
+ CASE
3881
+ WHEN SUM(CASE WHEN r.success IS NOT NULL THEN 1 ELSE 0 END) > 0
3882
+ THEN CAST(ROUND(SUM(CASE WHEN r.success = 1 THEN 1 ELSE 0 END) * 100.0 /
3883
+ SUM(CASE WHEN r.success IS NOT NULL THEN 1 ELSE 0 END)) AS INTEGER)
3884
+ ELSE 0
3885
+ END as successRate
3886
+ FROM accounts a
3887
+ INNER JOIN requests r ON r.account_used = a.id
3888
+ GROUP BY a.id
3889
+ HAVING requestCount > 0
3890
+ ORDER BY requestCount DESC
3891
+ LIMIT ?
3892
+ `;
3893
+ const params = includeUnauthenticated ? [NO_ACCOUNT_ID, NO_ACCOUNT_ID, NO_ACCOUNT_ID, limit] : [limit];
3894
+ return this.db.query(query).all(...params);
3895
+ }
3896
+ getActiveAccountCount() {
3897
+ const result = this.db.query("SELECT COUNT(*) as count FROM accounts WHERE request_count > 0").get();
3898
+ return result.count;
3899
+ }
3900
+ getRecentErrors(limit = 10) {
3901
+ return this.requests.getRecentErrors(limit);
3902
+ }
3903
+ getTopModels(limit = 5) {
3904
+ const models = this.requests.getTopModels(limit);
3905
+ const total = this.db.query(`SELECT COUNT(*) as total FROM requests WHERE model IS NOT NULL`).get()?.total ?? 0;
3906
+ return models.map((model) => ({
3907
+ ...model,
3908
+ percentage: total > 0 ? Math.round(model.count / total * 1e4) / 100 : 0
3909
+ }));
3910
+ }
3911
+ }
3912
+ var init_stats_repository = __esm(() => {
3913
+ init_src();
3914
+ init_request_repository();
3915
+ });
3916
+
3917
+ // ../../packages/database/src/repositories/strategy.repository.ts
3918
+ var StrategyRepository;
3919
+ var init_strategy_repository = __esm(() => {
3920
+ StrategyRepository = class StrategyRepository extends BaseRepository {
3921
+ getStrategy(name) {
3922
+ const row = super.get(`SELECT name, config, updated_at FROM strategies WHERE name = ?`, [name]);
3923
+ if (!row)
3924
+ return null;
3925
+ return {
3926
+ name: row.name,
3927
+ config: JSON.parse(row.config),
3928
+ updatedAt: row.updated_at
3929
+ };
3930
+ }
3931
+ set(name, config) {
3932
+ const now = Date.now();
3933
+ const configJson = JSON.stringify(config);
3934
+ this.run(`INSERT OR REPLACE INTO strategies (name, config, updated_at) VALUES (?, ?, ?)`, [name, configJson, now]);
3935
+ }
3936
+ list() {
3937
+ const rows = this.query(`SELECT name, config, updated_at FROM strategies ORDER BY name`);
3938
+ return rows.map((row) => ({
3939
+ name: row.name,
3940
+ config: JSON.parse(row.config),
3941
+ updatedAt: row.updated_at
3942
+ }));
3943
+ }
3944
+ delete(name) {
3945
+ const changes = this.runWithChanges(`DELETE FROM strategies WHERE name = ?`, [name]);
3946
+ return changes > 0;
3947
+ }
3948
+ };
3949
+ });
3950
+
3951
+ // ../../packages/database/src/database-operations.ts
3952
+ import { Database } from "bun:sqlite";
3953
+ import { mkdirSync as mkdirSync3 } from "fs";
3954
+ import { dirname as dirname2 } from "path";
3955
+
3956
+ class DatabaseOperations {
3957
+ db;
3958
+ runtime;
3959
+ closed = false;
3960
+ accounts;
3961
+ analytics;
3962
+ requests;
3963
+ authSessions;
3964
+ conversationPins;
3965
+ strategy;
3966
+ stats;
3967
+ constructor(dbPath) {
3968
+ const resolvedPath = dbPath ?? resolveDbPath();
3969
+ const dir = dirname2(resolvedPath);
3970
+ mkdirSync3(dir, { recursive: true });
3971
+ this.db = new Database(resolvedPath, { create: true });
3972
+ this.db.exec("PRAGMA foreign_keys = ON");
3973
+ this.db.exec("PRAGMA journal_mode = WAL");
3974
+ this.db.exec("PRAGMA busy_timeout = 5000");
3975
+ this.db.exec("PRAGMA synchronous = NORMAL");
3976
+ ensureSchema(this.db);
3977
+ runMigrations(this.db);
3978
+ this.accounts = new AccountRepository(this.db);
3979
+ this.analytics = new AnalyticsRepository(this.db);
3980
+ this.requests = new RequestRepository(this.db);
3981
+ this.authSessions = new AuthSessionRepository(this.db);
3982
+ this.conversationPins = new ConversationPinRepository(this.db);
3983
+ this.strategy = new StrategyRepository(this.db);
3984
+ this.stats = new StatsRepository(this.db);
3985
+ }
3986
+ setRuntimeConfig(runtime) {
3987
+ this.runtime = runtime;
3988
+ }
3989
+ getDatabase() {
3990
+ return this.db;
3991
+ }
3992
+ getAllAccounts() {
3993
+ return this.accounts.findAll();
3994
+ }
3995
+ getAccount(accountId) {
3996
+ return this.accounts.findById(accountId);
3997
+ }
3998
+ getAccountByName(name) {
3999
+ return this.accounts.findByName(name);
4000
+ }
4001
+ setEnrollmentContact(accountId, contact) {
4002
+ this.accounts.setEnrollmentContact(accountId, contact);
4003
+ }
4004
+ getEnrollmentContact(accountId) {
4005
+ return this.accounts.getEnrollmentContact(accountId);
4006
+ }
4007
+ getAccountsByProvider(provider) {
4008
+ return this.accounts.findByProvider(provider);
4009
+ }
4010
+ getAvailableAccountsByProvider(provider) {
4011
+ return this.accounts.findAvailableForProvider(provider);
4012
+ }
4013
+ createAccount(data) {
4014
+ return this.accounts.create(data);
4015
+ }
4016
+ createApiKeyAccount(opts) {
4017
+ return this.accounts.createApiKeyAccount(opts);
4018
+ }
4019
+ createOAuthAccount(opts) {
4020
+ return this.accounts.createOAuthAccount(opts);
4021
+ }
4022
+ updateAccount(accountId, data) {
4023
+ return this.accounts.update(accountId, data);
4024
+ }
4025
+ deleteAccount(accountId) {
4026
+ this.conversationPins.deleteForAccount(accountId);
4027
+ return this.accounts.delete(accountId);
4028
+ }
4029
+ getConversationPin(pinKey) {
4030
+ return this.conversationPins.find(pinKey);
4031
+ }
4032
+ upsertConversationPin(pinKey, accountId, lastSeenAt) {
4033
+ this.conversationPins.upsert(pinKey, accountId, lastSeenAt);
4034
+ }
4035
+ countConversationPinsByAccount(activeSince) {
4036
+ return this.conversationPins.countByAccount(activeSince);
4037
+ }
4038
+ deleteExpiredConversationPins(before) {
4039
+ this.conversationPins.deleteExpired(before);
4040
+ }
4041
+ countAccounts() {
4042
+ return this.accounts.count();
4043
+ }
4044
+ updateAccountTokens(accountId, accessToken, expiresAt, refreshToken) {
4045
+ this.accounts.updateTokens(accountId, accessToken, expiresAt, refreshToken);
4046
+ }
4047
+ updateAccountUsage(accountId) {
4048
+ const sessionDuration = this.runtime?.sessionDurationMs || 5 * 60 * 60 * 1000;
4049
+ this.accounts.incrementUsage(accountId, sessionDuration);
4050
+ }
4051
+ markAccountRateLimited(accountId, until) {
4052
+ this.accounts.setRateLimited(accountId, until);
4053
+ }
4054
+ updateAccountRateLimitMeta(accountId, status, reset, remaining) {
4055
+ this.accounts.updateRateLimitMeta(accountId, status, reset, remaining);
4056
+ }
4057
+ updateAccountQuotaState(accountId, quotaState) {
4058
+ this.accounts.updateQuotaState(accountId, quotaState);
4059
+ }
4060
+ pauseAccount(accountId) {
4061
+ this.accounts.pause(accountId);
4062
+ }
4063
+ markAccountError(accountId, reason) {
4064
+ this.accounts.markError(accountId, reason);
4065
+ }
4066
+ resumeAccount(accountId) {
4067
+ this.accounts.resume(accountId);
4068
+ }
4069
+ resetAccountSession(accountId, timestamp) {
4070
+ this.accounts.resetSession(accountId, timestamp);
4071
+ }
4072
+ updateAccountRequestCount(accountId, count) {
4073
+ this.accounts.updateRequestCount(accountId, count);
4074
+ }
4075
+ resetAccountStats(options) {
4076
+ this.accounts.resetStatistics(options?.resetSessionStart === true);
4077
+ }
4078
+ resetStats() {
4079
+ this.clearRequestHistory();
4080
+ this.resetAccountStats({ resetSessionStart: true });
4081
+ }
4082
+ saveRequestMeta(id, method, path, provider, upstreamPath, accountUsed, statusCode, timestamp) {
4083
+ this.requests.saveMeta(id, method, path, provider, upstreamPath, accountUsed, statusCode, timestamp);
4084
+ }
4085
+ saveRequest(id, method, path, provider, upstreamPath, accountUsed, statusCode, success, errorMessage, responseTime, failoverAttempts, usage, options) {
4086
+ this.requests.save({
4087
+ id,
4088
+ method,
4089
+ path,
4090
+ provider,
4091
+ upstreamPath,
4092
+ accountUsed,
4093
+ statusCode,
4094
+ success,
4095
+ errorMessage,
4096
+ responseTime,
4097
+ failoverAttempts,
4098
+ usage,
4099
+ timestamp: options?.timestamp,
4100
+ payload: options?.payload,
4101
+ timings: options?.timings
4102
+ });
4103
+ }
4104
+ updateRequestUsage(requestId, usage) {
4105
+ this.requests.updateUsage(requestId, usage);
4106
+ }
4107
+ saveRequestPayload(id, data) {
4108
+ this.requests.savePayload(id, data);
4109
+ }
4110
+ getRequestPayload(id) {
4111
+ return this.requests.getPayload(id);
4112
+ }
4113
+ listRequestSummaries(limit = 50) {
4114
+ return this.requests.listSummaries(limit);
4115
+ }
4116
+ listRequestsWithAccountNames(limit = 50) {
4117
+ return this.requests.listWithAccountNames(limit);
4118
+ }
4119
+ listRequestPayloads(limit = 50) {
4120
+ return this.requests.listPayloads(limit);
4121
+ }
4122
+ listRequestPayloadsWithAccountNames(limit = 50) {
4123
+ return this.requests.listPayloadsWithAccountNames(limit);
4124
+ }
4125
+ listResponseChainPayloadsWithAccountNames(requestId) {
4126
+ return this.requests.listResponseChainPayloadsWithAccountNames(requestId);
4127
+ }
4128
+ createAuthSession(provider, authMethod, accountName, stateJson, expiresAt) {
4129
+ return this.authSessions.createSession(provider, authMethod, accountName, stateJson, expiresAt);
4130
+ }
4131
+ getAuthSession(sessionId) {
4132
+ return this.authSessions.getSession(sessionId);
4133
+ }
4134
+ getAuthSessionByState(state) {
4135
+ return this.authSessions.getSessionByState(state);
4136
+ }
4137
+ updateAuthSessionState(sessionId, stateJson, expiresAt) {
4138
+ this.authSessions.updateSessionState(sessionId, stateJson, expiresAt);
4139
+ }
4140
+ deleteAuthSession(sessionId) {
4141
+ this.authSessions.deleteSession(sessionId);
4142
+ }
4143
+ deleteExpiredAuthSessions() {
4144
+ return this.authSessions.deleteExpiredSessions();
4145
+ }
4146
+ getStrategy(name) {
4147
+ return this.strategy.getStrategy(name);
4148
+ }
4149
+ setStrategy(name, config) {
4150
+ this.strategy.set(name, config);
4151
+ }
4152
+ listStrategies() {
4153
+ return this.strategy.list();
4154
+ }
4155
+ deleteStrategy(name) {
4156
+ return this.strategy.delete(name);
4157
+ }
4158
+ getRecentRequests(limit = 100) {
4159
+ return this.requests.getRecentRequests(limit);
4160
+ }
4161
+ clearRequestHistory() {
4162
+ return this.requests.clear();
4163
+ }
4164
+ getAnalytics(options) {
4165
+ return this.analytics.getAnalytics(options);
4166
+ }
4167
+ cleanupOldRequests(payloadRetentionMs, requestRetentionMs) {
4168
+ const now = Date.now();
4169
+ const payloadCutoff = now - payloadRetentionMs;
4170
+ let removedRequests = 0;
4171
+ if (typeof requestRetentionMs === "number" && Number.isFinite(requestRetentionMs)) {
4172
+ const requestCutoff = now - requestRetentionMs;
4173
+ removedRequests = this.requests.deleteOlderThan(requestCutoff);
4174
+ }
4175
+ const removedPayloadsByAge = this.requests.deletePayloadsOlderThan(payloadCutoff);
4176
+ const removedOrphans = this.requests.deleteOrphanedPayloads();
4177
+ const removedPayloads = removedPayloadsByAge + removedOrphans;
4178
+ return { removedRequests, removedPayloads };
4179
+ }
4180
+ close() {
4181
+ if (this.closed) {
4182
+ return;
4183
+ }
4184
+ this.closed = true;
4185
+ this.db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
4186
+ this.db.close();
4187
+ }
4188
+ dispose() {
4189
+ this.close();
4190
+ }
4191
+ optimize() {
4192
+ this.db.exec("PRAGMA optimize");
4193
+ this.db.exec("PRAGMA wal_checkpoint(PASSIVE)");
4194
+ }
4195
+ compact() {
4196
+ this.db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
4197
+ this.db.exec("VACUUM");
4198
+ }
4199
+ getStatsRepository() {
4200
+ return this.stats;
4201
+ }
4202
+ }
4203
+ var init_database_operations = __esm(() => {
4204
+ init_migrations();
4205
+ init_paths2();
4206
+ init_account_repository();
4207
+ init_analytics_repository();
4208
+ init_auth_session_repository();
4209
+ init_conversation_pin_repository();
4210
+ init_request_repository();
4211
+ init_stats_repository();
4212
+ init_strategy_repository();
4213
+ });
4214
+
4215
+ // ../../packages/database/src/factory.ts
4216
+ function initialize(dbPathParam, runtimeConfigParam) {
4217
+ dbPath = dbPathParam;
4218
+ runtimeConfig = runtimeConfigParam;
4219
+ }
4220
+ function getInstance() {
4221
+ if (!instance) {
4222
+ instance = new DatabaseOperations(dbPath);
4223
+ if (runtimeConfig) {
4224
+ instance.setRuntimeConfig(runtimeConfig);
4225
+ }
4226
+ registerDisposable(instance);
4227
+ }
4228
+ return instance;
4229
+ }
4230
+ function closeAll() {
4231
+ if (instance) {
4232
+ unregisterDisposable(instance);
4233
+ instance.close();
4234
+ instance = null;
4235
+ }
4236
+ }
4237
+ function reset() {
4238
+ closeAll();
4239
+ }
4240
+ var instance = null, dbPath, runtimeConfig, DatabaseFactory;
4241
+ var init_factory = __esm(() => {
4242
+ init_src2();
4243
+ init_database_operations();
4244
+ DatabaseFactory = {
4245
+ initialize,
4246
+ getInstance,
4247
+ closeAll,
4248
+ reset
4249
+ };
4250
+ });
4251
+
4252
+ // ../../packages/database/src/index.ts
4253
+ var init_src5 = __esm(() => {
4254
+ init_analytics_query();
4255
+ init_analyze_performance();
4256
+ init_async_writer();
4257
+ init_database_operations();
4258
+ init_factory();
4259
+ init_migrations();
4260
+ init_paths2();
4261
+ init_performance_indexes();
4262
+ });
4263
+
4264
+ // ../../packages/proxy/src/openai-usage.ts
4265
+ function getFiniteNumber(value) {
4266
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
4267
+ }
4268
+ function parseUsageDetails(value) {
4269
+ if (value === null) {
4270
+ return null;
4271
+ }
4272
+ if (!isRecord(value)) {
4273
+ return;
4274
+ }
4275
+ const cachedTokens = getFiniteNumber(value.cached_tokens);
4276
+ const reasoningTokens = getFiniteNumber(value.reasoning_tokens);
4277
+ return {
4278
+ ...cachedTokens !== undefined && { cached_tokens: cachedTokens },
4279
+ ...reasoningTokens !== undefined && {
4280
+ reasoning_tokens: reasoningTokens
4281
+ }
4282
+ };
4283
+ }
4284
+ function parseOpenAIUsagePayload(value) {
4285
+ if (!isRecord(value)) {
4286
+ return null;
4287
+ }
4288
+ const parsed = {};
4289
+ const numericFields = [
4290
+ "prompt_tokens",
4291
+ "completion_tokens",
4292
+ "total_tokens",
4293
+ "input_tokens",
4294
+ "cache_read_input_tokens",
4295
+ "cache_creation_input_tokens",
4296
+ "output_tokens",
4297
+ "reasoning_tokens"
4298
+ ];
4299
+ for (const field of numericFields) {
4300
+ const numericValue = getFiniteNumber(value[field]);
4301
+ if (numericValue !== undefined) {
4302
+ parsed[field] = numericValue;
4303
+ }
4304
+ }
4305
+ const inputTokenDetails = parseUsageDetails(value.input_tokens_details);
4306
+ if (inputTokenDetails !== undefined) {
4307
+ parsed.input_tokens_details = inputTokenDetails;
4308
+ }
4309
+ const promptTokenDetails = parseUsageDetails(value.prompt_tokens_details);
4310
+ if (promptTokenDetails !== undefined) {
4311
+ parsed.prompt_tokens_details = promptTokenDetails;
4312
+ }
4313
+ const outputTokenDetails = parseUsageDetails(value.output_tokens_details);
4314
+ if (outputTokenDetails !== undefined) {
4315
+ parsed.output_tokens_details = outputTokenDetails;
4316
+ }
4317
+ return parsed;
4318
+ }
4319
+ function normalizeOpenAIUsage(usage) {
4320
+ if (!usage) {
4321
+ return {};
4322
+ }
4323
+ const normalized = {};
4324
+ const numericFields = [
4325
+ "prompt_tokens",
4326
+ "completion_tokens",
4327
+ "total_tokens",
4328
+ "input_tokens",
4329
+ "cache_read_input_tokens",
4330
+ "cache_creation_input_tokens",
4331
+ "output_tokens",
4332
+ "reasoning_tokens"
4333
+ ];
4334
+ for (const field of numericFields) {
4335
+ const value = getFiniteNumber(usage[field]);
4336
+ if (value !== undefined) {
4337
+ normalized[field] = value;
4338
+ }
4339
+ }
4340
+ const rawCachedTokens = getFiniteNumber(usage.input_tokens_details?.cached_tokens) ?? getFiniteNumber(usage.prompt_tokens_details?.cached_tokens);
4341
+ if (rawCachedTokens !== undefined) {
4342
+ const inputTokens = getFiniteNumber(usage.input_tokens) ?? getFiniteNumber(normalized.input_tokens);
4343
+ const promptTokens = getFiniteNumber(usage.prompt_tokens) ?? getFiniteNumber(normalized.prompt_tokens);
4344
+ const maxCachedTokens = Math.max(0, inputTokens ?? promptTokens ?? rawCachedTokens);
4345
+ const cachedTokens = Math.max(0, Math.min(rawCachedTokens, maxCachedTokens));
4346
+ normalized.cache_read_input_tokens = cachedTokens;
4347
+ if (inputTokens !== undefined) {
4348
+ normalized.input_tokens = Math.max(0, inputTokens - cachedTokens);
4349
+ }
4350
+ if (promptTokens !== undefined) {
4351
+ normalized.prompt_tokens = Math.max(0, promptTokens - cachedTokens);
4352
+ }
4353
+ }
4354
+ const reasoningTokens = getFiniteNumber(usage.output_tokens_details?.reasoning_tokens);
4355
+ if (reasoningTokens !== undefined) {
4356
+ normalized.reasoning_tokens = reasoningTokens;
4357
+ }
4358
+ return normalized;
4359
+ }
4360
+ var init_openai_usage = __esm(() => {
4361
+ init_src();
4362
+ });
4363
+
4364
+ // ../../packages/proxy/src/post-processor.worker.ts
4365
+ init_src2();
4366
+ init_src5();
4367
+ init_src3();
4368
+ init_src();
4369
+ init_openai_usage();
4370
+ import { get_encoding } from "@dqbd/tiktoken";
4371
+
4372
+ // ../../packages/proxy/src/stream-tee.ts
4373
+ init_src2();
4374
+ function combineChunks(chunks) {
4375
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
4376
+ const combined = Buffer.allocUnsafe(totalLength);
4377
+ let offset = 0;
4378
+ for (const chunk of chunks) {
4379
+ combined.set(chunk, offset);
4380
+ offset += chunk.length;
4381
+ }
4382
+ return combined;
4383
+ }
4384
+
4385
+ // ../../packages/proxy/src/tracked-model.ts
4386
+ function normalizeTrackedModel(model) {
4387
+ if (typeof model !== "string") {
4388
+ return;
4389
+ }
4390
+ const trimmed = model.trim();
4391
+ return trimmed || undefined;
4392
+ }
4393
+ var MODEL_FIELD_RE = /"model"\s*:\s*"([^"]+)"/;
4394
+ function extractTrackedModelFromRequestBody(encodedBody) {
4395
+ if (!encodedBody) {
4396
+ return;
4397
+ }
4398
+ try {
4399
+ const slice = encodedBody.slice(0, 700);
4400
+ const decoded = Buffer.from(slice, "base64").toString("utf-8");
4401
+ const match = decoded.match(MODEL_FIELD_RE);
4402
+ return match ? normalizeTrackedModel(match[1]) : undefined;
4403
+ } catch {
4404
+ return;
4405
+ }
4406
+ }
4407
+
4408
+ // ../../packages/proxy/src/post-processor.worker.ts
4409
+ var log7 = new Logger("PostProcessor");
4410
+ var requests = new Map;
4411
+ var tokenEncoder = get_encoding("cl100k_base");
4412
+ var streamDecoder = new TextDecoder("utf-8", { fatal: false });
4413
+ var dbOps = new DatabaseOperations;
4414
+ var asyncWriter = new AsyncDbWriter;
4415
+ var MAX_BUFFER_SIZE = Number(process.env.CF_STREAM_USAGE_BUFFER_KB || BUFFER_SIZES.STREAM_USAGE_BUFFER_KB) * 1024;
4416
+ var TIMEOUT_MS = Number(process.env.CF_STREAM_TIMEOUT_MS || TIME_CONSTANTS.STREAM_TIMEOUT_DEFAULT);
4417
+ var MAX_PAYLOAD_BYTES = Number(process.env.CF_MAX_PAYLOAD_BYTES || 0) || 2 * 1024 * 1024;
4418
+ var MAX_TRACKED_REQUESTS = Number(process.env.CF_MAX_TRACKED_REQUESTS || 0) || 100;
4419
+ var TOKEN_CURVE_SAMPLE_EVERY = Math.max(1, Number(process.env.CF_TOKEN_CURVE_SAMPLE_EVERY || 10) || 10);
4420
+ var TOKEN_CURVE_MAX_SAMPLES = Math.max(1, Number(process.env.CF_TOKEN_CURVE_MAX_SAMPLES || 200) || 200);
4421
+ var RESPONSES_TERMINAL_EVENTS = new Set([
4422
+ "response.completed",
4423
+ "response.failed",
4424
+ "response.incomplete"
4425
+ ]);
4426
+ var cleanupIntervalId = null;
4427
+ var messageProcessingQueue = Promise.resolve();
4428
+ function shouldLogRequest(path, status) {
4429
+ if (path.startsWith("/.well-known/") && status === 404) {
4430
+ return false;
4431
+ }
4432
+ return true;
4433
+ }
4434
+ function parseSSELine(line) {
4435
+ if (line.startsWith("event: ")) {
4436
+ return { event: line.slice(7).trim() };
4437
+ }
4438
+ if (line.startsWith("data: ")) {
4439
+ return { data: line.slice(6).trim() };
4440
+ }
4441
+ return {};
4442
+ }
4443
+ function extractUsageFromJson(json, state) {
4444
+ if (!json)
4445
+ return;
4446
+ const usageObj = json.response?.usage ?? json.usage;
4447
+ if (!usageObj)
4448
+ return;
4449
+ applyUsageData(state, normalizeOpenAIUsage(usageObj), json.response?.model ?? json.model ?? state.usage.model);
4450
+ }
4451
+ function setUsageModel(state, model) {
4452
+ const normalizedModel = normalizeTrackedModel(model);
4453
+ if (normalizedModel) {
4454
+ state.usage.model = normalizedModel;
4455
+ }
4456
+ }
4457
+ function applyUsageData(state, usageObj, model) {
4458
+ setUsageModel(state, model);
4459
+ state.usage.inputTokens = usageObj.prompt_tokens ?? usageObj.input_tokens ?? 0;
4460
+ state.usage.cacheReadInputTokens = usageObj.cache_read_input_tokens ?? 0;
4461
+ state.usage.cacheCreationInputTokens = usageObj.cache_creation_input_tokens ?? 0;
4462
+ state.usage.outputTokens = usageObj.completion_tokens ?? usageObj.output_tokens ?? 0;
4463
+ state.usage.reasoningTokens = usageObj.reasoning_tokens ?? 0;
4464
+ const prompt = (state.usage.inputTokens ?? 0) + (state.usage.cacheReadInputTokens ?? 0) + (state.usage.cacheCreationInputTokens ?? 0);
4465
+ const completion = state.usage.outputTokens ?? 0;
4466
+ state.usage.totalTokens = usageObj.total_tokens ?? prompt + completion;
4467
+ }
4468
+ function computeTokenCount(text) {
4469
+ try {
4470
+ return tokenEncoder.encode(text).length;
4471
+ } catch (err) {
4472
+ log7.debug("Failed to count tokens:", err);
4473
+ return null;
4474
+ }
4475
+ }
4476
+ function recordTokenEvent(state, text, timestamp = Date.now()) {
4477
+ if (!text) {
4478
+ return;
4479
+ }
4480
+ const tokenDelta = computeTokenCount(text);
4481
+ if (!tokenDelta || tokenDelta <= 0) {
4482
+ return;
4483
+ }
4484
+ if (!state.firstTokenTimestamp) {
4485
+ state.firstTokenTimestamp = timestamp;
4486
+ }
4487
+ state.lastTokenTimestamp = timestamp;
4488
+ if (state.providerFinalOutputTokens === undefined) {
4489
+ state.usage.outputTokensComputed = (state.usage.outputTokensComputed || 0) + tokenDelta;
4490
+ }
4491
+ state.tokenChunkCount += 1;
4492
+ if (state.tokenChunkCount % TOKEN_CURVE_SAMPLE_EVERY === 0 && state.tokenCurve.length < TOKEN_CURVE_MAX_SAMPLES) {
4493
+ state.tokenCurve.push({
4494
+ chunkIndex: state.tokenChunkCount,
4495
+ tokenDelta,
4496
+ timestamp
4497
+ });
4498
+ }
4499
+ }
4500
+ function durationBetween(start, end) {
4501
+ if (start === undefined || end === undefined) {
4502
+ return null;
4503
+ }
4504
+ return Math.max(0, end - start);
4505
+ }
4506
+ function isRelevantSseData(data) {
4507
+ return data.includes('"usage"') || data.includes('"message_start"') || data.includes('"message_delta"') || data.includes('"response.created"') || data.includes('"response.completed"') || data.includes('"chat.completion.chunk"') || data.includes('"content_block_start"') || data.includes('"content_block_delta"') || data.includes('"response.output_text.delta"');
4508
+ }
4509
+ function extractUsageFromData(data, state) {
4510
+ if (!isRelevantSseData(data))
4511
+ return;
4512
+ try {
4513
+ const parsed = JSON.parse(data);
4514
+ if (isRecord(parsed) && parsed.type === "response.created" && (typeof parsed.model === "string" || isRecord(parsed.response) && typeof parsed.response.model === "string")) {
4515
+ setUsageModel(state, (isRecord(parsed.response) && typeof parsed.response.model === "string" ? parsed.response.model : undefined) ?? (typeof parsed.model === "string" ? parsed.model : undefined));
4516
+ }
4517
+ if (isRecord(parsed) && parsed.type === "message_start" && isRecord(parsed.message) && isRecord(parsed.message.usage)) {
4518
+ const usage = parsed.message.usage;
4519
+ state.usage.inputTokens = typeof usage.input_tokens === "number" ? usage.input_tokens : 0;
4520
+ state.usage.cacheReadInputTokens = typeof usage.cache_read_input_tokens === "number" ? usage.cache_read_input_tokens : 0;
4521
+ state.usage.cacheCreationInputTokens = typeof usage.cache_creation_input_tokens === "number" ? usage.cache_creation_input_tokens : 0;
4522
+ state.usage.outputTokens = typeof usage.output_tokens === "number" ? usage.output_tokens : 0;
4523
+ if (typeof parsed.message.model === "string") {
4524
+ setUsageModel(state, parsed.message.model);
4525
+ }
4526
+ }
4527
+ if (isRecord(parsed) && typeof parsed.type === "string" && RESPONSES_TERMINAL_EVENTS.has(parsed.type) && isRecord(parsed.response)) {
4528
+ const usage = parseOpenAIUsagePayload(parsed.response.usage);
4529
+ applyUsageData(state, normalizeOpenAIUsage(usage), typeof parsed.response.model === "string" ? parsed.response.model : state.usage.model);
4530
+ }
4531
+ if (isRecord(parsed) && parsed.type === "response.output_text.delta" && typeof parsed.delta === "string") {
4532
+ recordTokenEvent(state, parsed.delta);
4533
+ }
4534
+ if (isRecord(parsed) && parsed.object === "chat.completion.chunk" && Array.isArray(parsed.choices)) {
4535
+ if (typeof parsed.model === "string") {
4536
+ setUsageModel(state, parsed.model);
4537
+ }
4538
+ const firstChoice = isRecord(parsed.choices[0]) ? parsed.choices[0] : null;
4539
+ const delta = firstChoice && isRecord(firstChoice.delta) ? firstChoice.delta : null;
4540
+ if (delta) {
4541
+ if (typeof delta.content === "string") {
4542
+ recordTokenEvent(state, delta.content);
4543
+ }
4544
+ if (typeof delta.reasoning_content === "string") {
4545
+ recordTokenEvent(state, delta.reasoning_content);
4546
+ }
4547
+ if (Array.isArray(delta.tool_calls)) {
4548
+ for (const toolCall of delta.tool_calls) {
4549
+ if (!isRecord(toolCall) || !isRecord(toolCall.function))
4550
+ continue;
4551
+ if (typeof toolCall.function.arguments === "string") {
4552
+ recordTokenEvent(state, toolCall.function.arguments);
4553
+ }
4554
+ }
4555
+ }
4556
+ }
4557
+ if (isRecord(parsed.usage)) {
4558
+ applyUsageData(state, normalizeOpenAIUsage(parseOpenAIUsagePayload(parsed.usage)), typeof parsed.model === "string" ? parsed.model : state.usage.model);
4559
+ }
4560
+ }
4561
+ if (isRecord(parsed) && parsed.type === "message_delta") {
4562
+ if (isRecord(parsed.usage) && typeof parsed.usage.output_tokens === "number") {
4563
+ state.providerFinalOutputTokens = parsed.usage.output_tokens;
4564
+ state.usage.outputTokens = parsed.usage.output_tokens;
4565
+ if (parsed.usage.output_tokens > 0 && state.lastTokenTimestamp === undefined) {
4566
+ state.lastTokenTimestamp = Date.now();
4567
+ }
4568
+ return;
4569
+ }
4570
+ }
4571
+ if (isRecord(parsed) && parsed.type === "content_block_delta" && isRecord(parsed.delta)) {
4572
+ let textToCount;
4573
+ if (parsed.delta.type === "text_delta" && typeof parsed.delta.text === "string") {
4574
+ textToCount = parsed.delta.text;
4575
+ } else if (parsed.delta.type === "thinking_delta" && typeof parsed.delta.thinking === "string") {
4576
+ textToCount = parsed.delta.thinking;
4577
+ }
4578
+ if (textToCount) {
4579
+ recordTokenEvent(state, textToCount);
4580
+ }
4581
+ }
4582
+ if (isRecord(parsed) && parsed.usage) {
4583
+ applyUsageData(state, normalizeOpenAIUsage(parseOpenAIUsagePayload(parsed.usage)), state.usage.model);
4584
+ }
4585
+ } catch {}
4586
+ }
4587
+ function processStreamChunk(chunk, state) {
4588
+ const text = streamDecoder.decode(chunk, { stream: true });
4589
+ state.buffer += text;
4590
+ state.lastActivity = Date.now();
4591
+ if (state.buffer.length > MAX_BUFFER_SIZE) {
4592
+ state.buffer = state.buffer.slice(-MAX_BUFFER_SIZE);
4593
+ }
4594
+ let currentEvent = state.currentSseEvent;
4595
+ let nlIndex = state.buffer.indexOf(`
4596
+ `);
4597
+ while (nlIndex !== -1) {
4598
+ const line = state.buffer.slice(0, nlIndex).trim();
4599
+ state.buffer = state.buffer.slice(nlIndex + 1);
4600
+ if (!line) {
4601
+ currentEvent = "";
4602
+ } else {
4603
+ const parsed = parseSSELine(line);
4604
+ if (parsed.event) {
4605
+ currentEvent = parsed.event;
4606
+ } else if (parsed.data) {
4607
+ extractUsageFromData(parsed.data, state);
4608
+ }
4609
+ }
4610
+ nlIndex = state.buffer.indexOf(`
4611
+ `);
4612
+ }
4613
+ state.currentSseEvent = currentEvent;
4614
+ }
4615
+ function createRequestState(startMessage, overrides = {}) {
4616
+ return {
4617
+ startMessage,
4618
+ buffer: "",
4619
+ chunks: [],
4620
+ chunksBytes: 0,
4621
+ payloadCapped: false,
4622
+ currentSseEvent: "",
4623
+ usage: {},
4624
+ lastActivity: Date.now(),
4625
+ clientSessionId: extractClientSessionIdFromHeaders(startMessage.requestHeaders),
4626
+ tokenChunkCount: 0,
4627
+ tokenCurve: [],
4628
+ ...overrides
4629
+ };
4630
+ }
4631
+ function hasTrackedUsageData(state) {
4632
+ return state.usage.inputTokens !== undefined || state.usage.cacheReadInputTokens !== undefined || state.usage.cacheCreationInputTokens !== undefined || state.usage.outputTokens !== undefined || state.usage.reasoningTokens !== undefined || state.usage.outputTokensComputed !== undefined || state.usage.totalTokens !== undefined || state.providerFinalOutputTokens !== undefined;
4633
+ }
4634
+ async function handleStart(msg) {
4635
+ if (requests.size >= MAX_TRACKED_REQUESTS) {
4636
+ log7.warn(`Tracked request cap reached (${MAX_TRACKED_REQUESTS}), skipping tracking for ${msg.requestId}`);
4637
+ return;
4638
+ }
4639
+ const shouldSkip = !shouldLogRequest(msg.path, msg.responseStatus);
4640
+ const state = createRequestState(msg, {
4641
+ shouldSkipLogging: shouldSkip
4642
+ });
4643
+ requests.set(msg.requestId, state);
4644
+ if (shouldSkip) {
4645
+ log7.debug(`Skipping logging for ${msg.path} (${msg.responseStatus})`);
4646
+ return;
4647
+ }
4648
+ asyncWriter.enqueue(() => dbOps.saveRequestMeta(msg.requestId, msg.method, msg.path, msg.providerName, msg.upstreamPath, msg.accountId, msg.responseStatus, msg.timestamp));
4649
+ if (msg.accountId) {
4650
+ const accountId = msg.accountId;
4651
+ asyncWriter.enqueue(() => dbOps.updateAccountUsage(accountId));
4652
+ }
4653
+ }
4654
+ function handleChunk(msg) {
4655
+ const state = requests.get(msg.requestId);
4656
+ if (!state) {
4657
+ log7.warn(`No state found for request ${msg.requestId}`);
4658
+ return;
4659
+ }
4660
+ if (!state.payloadCapped) {
4661
+ if (state.chunksBytes + msg.data.length <= MAX_PAYLOAD_BYTES) {
4662
+ state.chunks.push(msg.data);
4663
+ state.chunksBytes += msg.data.length;
4664
+ } else {
4665
+ const remaining = MAX_PAYLOAD_BYTES - state.chunksBytes;
4666
+ if (remaining > 0) {
4667
+ state.chunks.push(msg.data.slice(0, remaining));
4668
+ state.chunksBytes = MAX_PAYLOAD_BYTES;
4669
+ }
4670
+ state.payloadCapped = true;
4671
+ log7.debug(`Payload cap reached for request ${msg.requestId} (${MAX_PAYLOAD_BYTES} bytes)`);
4672
+ }
4673
+ }
4674
+ processStreamChunk(msg.data, state);
4675
+ }
4676
+ function buildRequestEndPayload(input) {
4677
+ const { startMessage, state, success, responseBody, timings } = input;
4678
+ const payload = {
4679
+ id: startMessage.requestId,
4680
+ request: {
4681
+ headers: startMessage.requestHeaders,
4682
+ body: startMessage.requestBody
4683
+ },
4684
+ response: {
4685
+ status: startMessage.responseStatus,
4686
+ headers: startMessage.responseHeaders,
4687
+ body: responseBody
4688
+ },
4689
+ meta: {
4690
+ trace: {
4691
+ timestamp: startMessage.timestamp,
4692
+ provider: startMessage.providerName,
4693
+ upstreamPath: startMessage.upstreamPath,
4694
+ path: startMessage.path,
4695
+ method: startMessage.method
4696
+ },
4697
+ account: {
4698
+ id: startMessage.accountId
4699
+ },
4700
+ transport: {
4701
+ success,
4702
+ isStream: startMessage.isStream,
4703
+ retry: startMessage.retryAttempt,
4704
+ ttftMs: timings.ttftMs,
4705
+ proxyOverheadMs: timings.proxyOverheadMs,
4706
+ upstreamTtfbMs: timings.upstreamTtfbMs,
4707
+ streamingDurationMs: timings.streamingDurationMs,
4708
+ tokenCurve: state.tokenCurve.length > 0 ? state.tokenCurve : null
4709
+ }
4710
+ }
4711
+ };
4712
+ const linkage = extractRequestLinkageFromPayload(payload);
4713
+ payload.meta.trace.responseId = linkage.responseId;
4714
+ payload.meta.trace.previousResponseId = linkage.previousResponseId;
4715
+ payload.meta.trace.clientSessionId = linkage.clientSessionId ?? state.clientSessionId ?? null;
4716
+ payload.request.body = null;
4717
+ if (payload.response) {
4718
+ payload.response.body = null;
4719
+ }
4720
+ return payload;
4721
+ }
4722
+ async function handleEnd(msg) {
4723
+ const state = requests.get(msg.requestId);
4724
+ if (!state) {
4725
+ log7.warn(`No state found for request ${msg.requestId}`);
4726
+ return;
4727
+ }
4728
+ const { startMessage } = state;
4729
+ const responseTime = Date.now() - startMessage.timestamp;
4730
+ if (state.shouldSkipLogging) {
4731
+ requests.delete(msg.requestId);
4732
+ return;
4733
+ }
4734
+ if (msg.preExtractedUsage) {
4735
+ applyUsageData(state, msg.preExtractedUsage, msg.preExtractedModel);
4736
+ } else if (!state.usage.model && msg.responseBody) {
4737
+ try {
4738
+ const decoded = Buffer.from(msg.responseBody, "base64").toString("utf-8");
4739
+ const json = JSON.parse(decoded);
4740
+ extractUsageFromJson(json, state);
4741
+ } catch {}
4742
+ }
4743
+ if (msg.preExtractedModel && !state.usage.model) {
4744
+ setUsageModel(state, msg.preExtractedModel);
4745
+ }
4746
+ if (!state.usage.model) {
4747
+ setUsageModel(state, extractTrackedModelFromRequestBody(startMessage.requestBody));
4748
+ }
4749
+ const usageModel = state.usage.model || (hasTrackedUsageData(state) ? "unknown" : undefined);
4750
+ if (usageModel) {
4751
+ if (!state.usage.model) {
4752
+ log7.debug(`Usage model missing for request ${msg.requestId}, using fallback`, {
4753
+ provider: startMessage.providerName,
4754
+ path: startMessage.path
4755
+ });
4756
+ }
4757
+ state.usage.model = usageModel;
4758
+ const finalOutputTokens = state.providerFinalOutputTokens ?? state.usage.outputTokens ?? state.usage.outputTokensComputed ?? 0;
4759
+ state.usage.outputTokens = finalOutputTokens;
4760
+ state.usage.outputTokensComputed = undefined;
4761
+ state.usage.totalTokens = (state.usage.inputTokens || 0) + finalOutputTokens + (state.usage.cacheReadInputTokens || 0) + (state.usage.cacheCreationInputTokens || 0);
4762
+ state.usage.costUsd = usageModel === "unknown" ? 0 : await estimateCostUSD(usageModel, {
4763
+ inputTokens: state.usage.inputTokens,
4764
+ outputTokens: finalOutputTokens,
4765
+ cacheReadInputTokens: state.usage.cacheReadInputTokens,
4766
+ cacheCreationInputTokens: state.usage.cacheCreationInputTokens
4767
+ });
4768
+ if (finalOutputTokens > 0) {
4769
+ const durationMs = durationBetween(state.firstTokenTimestamp, state.lastTokenTimestamp);
4770
+ if (durationMs !== null && durationMs > 0) {
4771
+ state.usage.tokensPerSecond = finalOutputTokens / (durationMs / 1000);
4772
+ } else if (durationMs !== null) {
4773
+ state.usage.tokensPerSecond = finalOutputTokens / 0.001;
4774
+ }
4775
+ }
4776
+ }
4777
+ let responseBody = null;
4778
+ if (msg.responseBody) {
4779
+ responseBody = msg.responseBody;
4780
+ } else if (state.chunks.length > 0) {
4781
+ const combined = combineChunks(state.chunks);
4782
+ if (combined.length > 0) {
4783
+ responseBody = combined.toString("base64");
4784
+ }
4785
+ }
4786
+ const timings = {
4787
+ ttftMs: durationBetween(startMessage.timestamp, state.firstTokenTimestamp),
4788
+ proxyOverheadMs: durationBetween(startMessage.timestamp, startMessage.upstreamRequestStartedAt),
4789
+ upstreamTtfbMs: durationBetween(startMessage.upstreamRequestStartedAt, startMessage.responseHeadersReceivedAt),
4790
+ streamingDurationMs: durationBetween(state.firstTokenTimestamp, state.lastTokenTimestamp)
4791
+ };
4792
+ const payload = buildRequestEndPayload({
4793
+ startMessage,
4794
+ state,
4795
+ success: msg.success,
4796
+ responseBody,
4797
+ timings
4798
+ });
4799
+ asyncWriter.enqueue(() => dbOps.saveRequest(startMessage.requestId, startMessage.method, startMessage.path, startMessage.providerName, startMessage.upstreamPath, startMessage.accountId, startMessage.responseStatus, msg.success, msg.error || null, responseTime, startMessage.failoverAttempts, state.usage.model ? {
4800
+ model: state.usage.model,
4801
+ promptTokens: (state.usage.inputTokens || 0) + (state.usage.cacheReadInputTokens || 0) + (state.usage.cacheCreationInputTokens || 0),
4802
+ completionTokens: state.usage.outputTokens,
4803
+ totalTokens: state.usage.totalTokens,
4804
+ costUsd: state.usage.costUsd,
4805
+ inputTokens: state.usage.inputTokens,
4806
+ outputTokens: state.usage.outputTokens,
4807
+ cacheReadInputTokens: state.usage.cacheReadInputTokens,
4808
+ cacheCreationInputTokens: state.usage.cacheCreationInputTokens,
4809
+ reasoningTokens: state.usage.reasoningTokens,
4810
+ tokensPerSecond: state.usage.tokensPerSecond
4811
+ } : undefined, {
4812
+ timestamp: startMessage.timestamp,
4813
+ payload,
4814
+ timings
4815
+ }));
4816
+ if (state.usage.model && startMessage.accountId !== null) {
4817
+ log7.info(`Usage for request ${startMessage.requestId}: Model: ${state.usage.model}, ` + `Tokens: ${state.usage.totalTokens || 0}, Cost: ${formatCost(state.usage.costUsd)}`);
4818
+ }
4819
+ const summary = {
4820
+ id: startMessage.requestId,
4821
+ timestamp: new Date(startMessage.timestamp).toISOString(),
4822
+ method: startMessage.method,
4823
+ path: startMessage.path,
4824
+ provider: startMessage.providerName,
4825
+ upstreamPath: startMessage.upstreamPath,
4826
+ accountUsed: startMessage.accountId,
4827
+ accountName: null,
4828
+ statusCode: startMessage.responseStatus,
4829
+ success: msg.success,
4830
+ errorMessage: msg.error || null,
4831
+ responseTimeMs: responseTime,
4832
+ failoverAttempts: startMessage.failoverAttempts,
4833
+ model: state.usage.model ?? null,
4834
+ promptTokens: state.usage.inputTokens ?? null,
4835
+ completionTokens: state.usage.outputTokens ?? null,
4836
+ totalTokens: state.usage.totalTokens ?? null,
4837
+ inputTokens: state.usage.inputTokens ?? null,
4838
+ cacheReadInputTokens: state.usage.cacheReadInputTokens ?? null,
4839
+ cacheCreationInputTokens: state.usage.cacheCreationInputTokens ?? null,
4840
+ reasoningTokens: state.usage.reasoningTokens ?? null,
4841
+ outputTokens: state.usage.outputTokens ?? null,
4842
+ costUsd: state.usage.costUsd ?? null,
4843
+ tokensPerSecond: state.usage.tokensPerSecond ?? null,
4844
+ ttftMs: timings.ttftMs,
4845
+ proxyOverheadMs: timings.proxyOverheadMs,
4846
+ upstreamTtfbMs: timings.upstreamTtfbMs,
4847
+ streamingDurationMs: timings.streamingDurationMs,
4848
+ responseId: payload.meta.trace.responseId ?? null,
4849
+ previousResponseId: payload.meta.trace.previousResponseId ?? null,
4850
+ responseChainId: null,
4851
+ clientSessionId: payload.meta.trace.clientSessionId ?? null
4852
+ };
4853
+ self.postMessage({
4854
+ type: "summary",
4855
+ summary
4856
+ });
4857
+ self.postMessage({
4858
+ type: "payload",
4859
+ payload: { ...payload, error: msg.error }
4860
+ });
4861
+ requests.delete(msg.requestId);
4862
+ }
4863
+ async function handleShutdown() {
4864
+ log7.info("Worker shutting down, flushing async writer...");
4865
+ if (cleanupIntervalId) {
4866
+ clearInterval(cleanupIntervalId);
4867
+ cleanupIntervalId = null;
4868
+ }
4869
+ for (const requestId of Array.from(requests.keys())) {
4870
+ await handleEnd({
4871
+ type: "end",
4872
+ requestId,
4873
+ success: false,
4874
+ error: "Request interrupted by shutdown"
4875
+ });
4876
+ }
4877
+ await asyncWriter.dispose();
4878
+ dbOps.close();
4879
+ self.postMessage({
4880
+ type: "shutdown-complete",
4881
+ asyncWriter: {
4882
+ healthy: asyncWriter.isHealthy(),
4883
+ failureCount: asyncWriter.getFailureCount(),
4884
+ queuedJobs: asyncWriter.getQueueSize()
4885
+ }
4886
+ });
4887
+ }
4888
+ function acknowledgeMessage(message) {
4889
+ if (!message.messageId) {
4890
+ return;
4891
+ }
4892
+ self.postMessage({
4893
+ type: "ack",
4894
+ messageId: message.messageId,
4895
+ requestId: "requestId" in message ? message.requestId : undefined,
4896
+ acknowledgedType: message.type
4897
+ });
4898
+ }
4899
+ function cleanupStaleRequests() {
4900
+ const now = Date.now();
4901
+ for (const [id, state] of requests) {
4902
+ if (now - state.lastActivity > TIMEOUT_MS) {
4903
+ log7.warn(`Request ${id} appears orphaned (no activity for ${TIMEOUT_MS}ms), cleaning up...`);
4904
+ handleEnd({
4905
+ type: "end",
4906
+ requestId: id,
4907
+ success: false,
4908
+ error: "Request orphaned - no activity"
4909
+ });
4910
+ }
4911
+ }
4912
+ }
4913
+ if (typeof self !== "undefined" && typeof self.postMessage === "function") {
4914
+ cleanupIntervalId = setInterval(cleanupStaleRequests, TIMEOUT_MS);
4915
+ self.onmessage = (event) => {
4916
+ const msg = event.data;
4917
+ acknowledgeMessage(msg);
4918
+ messageProcessingQueue = messageProcessingQueue.then(async () => {
4919
+ switch (msg.type) {
4920
+ case "start":
4921
+ await handleStart(msg);
4922
+ break;
4923
+ case "chunk":
4924
+ handleChunk(msg);
4925
+ break;
4926
+ case "end":
4927
+ await handleEnd(msg);
4928
+ break;
4929
+ case "shutdown":
4930
+ await handleShutdown();
4931
+ break;
4932
+ }
4933
+ }).catch((error) => {
4934
+ log7.error("Failed to process usage worker message", error);
4935
+ });
4936
+ };
4937
+ self.postMessage({
4938
+ type: "ready"
4939
+ });
4940
+ }
4941
+ export {
4942
+ processStreamChunk,
4943
+ createRequestState,
4944
+ buildRequestEndPayload
4945
+ };