@omnicross/core 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/GeminiCodeAssistProjectResolver.cjs +6 -1
- package/dist/auth/GeminiCodeAssistProjectResolver.js +6 -1
- package/dist/{chunk-SN3YWBX7.cjs → chunk-2FMI2B6V.cjs} +2329 -533
- package/dist/{chunk-XBSYYZIY.cjs → chunk-3QOSDDNI.cjs} +39 -3
- package/dist/{chunk-2FEVTJWG.js → chunk-4DECIFU7.js} +2162 -366
- package/dist/chunk-4IH4EL7M.cjs +9 -0
- package/dist/chunk-5ENKBSWO.js +311 -0
- package/dist/chunk-6RPZADX3.cjs +92 -0
- package/dist/chunk-AEOZIDEB.cjs +32 -0
- package/dist/chunk-BO4RFWBR.js +89 -0
- package/dist/{chunk-MNYKI4CI.js → chunk-CWM2ZDU5.js} +36 -0
- package/dist/chunk-CZHZGRU7.js +383 -0
- package/dist/{chunk-4NBS6KPV.cjs → chunk-DQ5VAYZ6.cjs} +7 -8
- package/dist/chunk-HUZZZ3JL.js +32 -0
- package/dist/{chunk-QOCNX236.js → chunk-IAXWICZU.js} +24 -1
- package/dist/{chunk-V5KPWNYX.cjs → chunk-JMJBSACD.cjs} +3 -3
- package/dist/chunk-L74VDN4A.cjs +32 -0
- package/dist/{chunk-74TMJA7Z.js → chunk-N5QFKSDO.js} +7 -4
- package/dist/{chunk-LKZJEL6E.cjs → chunk-Q27JY5RG.cjs} +6 -4
- package/dist/{chunk-SPVWWUHX.cjs → chunk-QWBWGODS.cjs} +24 -1
- package/dist/chunk-RPII3E6Z.cjs +145 -0
- package/dist/chunk-S6JGLF3G.cjs +89 -0
- package/dist/chunk-SVXC36MU.cjs +383 -0
- package/dist/chunk-TKJMZBAH.js +32 -0
- package/dist/{chunk-ZSVQT3PW.js → chunk-UBWEBKRC.js} +5 -6
- package/dist/chunk-UNEFIWXI.cjs +32 -0
- package/dist/chunk-VQY5W4YW.cjs +311 -0
- package/dist/chunk-WNY4WMRF.js +32 -0
- package/dist/chunk-XEKD23J5.js +9 -0
- package/dist/chunk-XTEI64OU.js +145 -0
- package/dist/chunk-XX6NQJMA.js +92 -0
- package/dist/{chunk-N3V2J5ZO.cjs → chunk-Y6OM7EV7.cjs} +7 -4
- package/dist/{chunk-745DV5FL.js → chunk-Y7FO65VT.js} +3 -3
- package/dist/{chunk-PXUJF5HS.js → chunk-ZY6KU6P3.js} +4 -2
- package/dist/completion/CompletionService.cjs +22 -15
- package/dist/completion/CompletionService.js +21 -14
- package/dist/completion/NativeSearchInjector.cjs +3 -3
- package/dist/completion/NativeSearchInjector.js +2 -2
- package/dist/completion/openrouter-headers.cjs +3 -6
- package/dist/completion/openrouter-headers.d.cts +9 -12
- package/dist/completion/openrouter-headers.d.ts +9 -12
- package/dist/completion/openrouter-headers.js +2 -5
- package/dist/completion/openrouter-models.cjs +4 -3
- package/dist/completion/openrouter-models.js +2 -1
- package/dist/completion.cjs +22 -15
- package/dist/completion.js +21 -14
- package/dist/index.cjs +139 -17
- package/dist/index.d.cts +40 -10
- package/dist/index.d.ts +40 -10
- package/dist/index.js +146 -24
- package/dist/outbound-api/routeResolver.cjs +5 -2
- package/dist/outbound-api/routeResolver.d.cts +8 -2
- package/dist/outbound-api/routeResolver.d.ts +8 -2
- package/dist/outbound-api/routeResolver.js +4 -1
- package/dist/outbound-api/types.cjs +6 -1
- package/dist/outbound-api/types.d.cts +11 -144
- package/dist/outbound-api/types.d.ts +11 -144
- package/dist/outbound-api/types.js +6 -0
- package/dist/outbound-api.cjs +133 -15
- package/dist/outbound-api.d.cts +786 -25
- package/dist/outbound-api.d.ts +786 -25
- package/dist/outbound-api.js +137 -19
- package/dist/pipeline/AuthSource.d.cts +23 -1
- package/dist/pipeline/AuthSource.d.ts +23 -1
- package/dist/pipeline/LlmConfigProviderAuth.cjs +3 -3
- package/dist/pipeline/LlmConfigProviderAuth.js +2 -2
- package/dist/pipeline/SubscriptionAccountHealth.cjs +22 -0
- package/dist/pipeline/SubscriptionAccountHealth.d.cts +235 -0
- package/dist/pipeline/SubscriptionAccountHealth.d.ts +235 -0
- package/dist/pipeline/SubscriptionAccountHealth.js +22 -0
- package/dist/pipeline/SubscriptionAuthSource.cjs +2 -2
- package/dist/pipeline/SubscriptionAuthSource.d.cts +3 -2
- package/dist/pipeline/SubscriptionAuthSource.d.ts +3 -2
- package/dist/pipeline/SubscriptionAuthSource.js +1 -1
- package/dist/pipeline/SubscriptionAuthStrategy.d.cts +24 -1
- package/dist/pipeline/SubscriptionAuthStrategy.d.ts +24 -1
- package/dist/pipeline/auditSink.cjs +14 -0
- package/dist/pipeline/auditSink.d.cts +48 -0
- package/dist/pipeline/auditSink.d.ts +48 -0
- package/dist/pipeline/auditSink.js +14 -0
- package/dist/pipeline/billingEmit.cjs +14 -0
- package/dist/pipeline/billingEmit.d.cts +49 -0
- package/dist/pipeline/billingEmit.d.ts +49 -0
- package/dist/pipeline/billingEmit.js +14 -0
- package/dist/ports/usage-event-store.d.cts +26 -1
- package/dist/ports/usage-event-store.d.ts +26 -1
- package/dist/ports.d.cts +7 -1
- package/dist/ports.d.ts +7 -1
- package/dist/provider-proxy/ProviderProxy.cjs +22 -15
- package/dist/provider-proxy/ProviderProxy.js +21 -14
- package/dist/provider-proxy/identity/SubscriptionIdentityStore.cjs +15 -0
- package/dist/provider-proxy/identity/SubscriptionIdentityStore.d.cts +121 -0
- package/dist/provider-proxy/identity/SubscriptionIdentityStore.d.ts +121 -0
- package/dist/provider-proxy/identity/SubscriptionIdentityStore.js +15 -0
- package/dist/provider-proxy/identity/fingerprintHeaders.cjs +20 -0
- package/dist/provider-proxy/identity/fingerprintHeaders.d.cts +89 -0
- package/dist/provider-proxy/identity/fingerprintHeaders.d.ts +89 -0
- package/dist/provider-proxy/identity/fingerprintHeaders.js +20 -0
- package/dist/provider-proxy/ingress/providerProxyShared.cjs +22 -15
- package/dist/provider-proxy/ingress/providerProxyShared.d.cts +11 -2
- package/dist/provider-proxy/ingress/providerProxyShared.d.ts +11 -2
- package/dist/provider-proxy/ingress/providerProxyShared.js +21 -14
- package/dist/provider-proxy/matchText.cjs +4 -2
- package/dist/provider-proxy/matchText.d.cts +14 -1
- package/dist/provider-proxy/matchText.d.ts +14 -1
- package/dist/provider-proxy/matchText.js +3 -1
- package/dist/provider-proxy/types.d.cts +23 -0
- package/dist/provider-proxy/types.d.ts +23 -0
- package/dist/provider-proxy.cjs +22 -15
- package/dist/provider-proxy.js +21 -14
- package/dist/{routeResolver-HE-ZO0fO.d.ts → routeResolver-B1NglTfe.d.ts} +36 -10
- package/dist/{routeResolver-BrbK6ja9.d.cts → routeResolver-CbG5uBgm.d.cts} +36 -10
- package/dist/transformer/transformers.cjs +7 -7
- package/dist/transformer/transformers.js +7 -7
- package/dist/transformer.cjs +5 -5
- package/dist/transformer.js +4 -4
- package/dist/types-6OUY_xfA.d.ts +830 -0
- package/dist/types-aBFFXoBz.d.cts +830 -0
- package/dist/usage/usage-recorder.cjs +2 -2
- package/dist/usage/usage-recorder.d.cts +19 -1
- package/dist/usage/usage-recorder.d.ts +19 -1
- package/dist/usage/usage-recorder.js +1 -1
- package/dist/usage.cjs +2 -2
- package/dist/usage.js +1 -1
- package/package.json +4 -2
- package/dist/chunk-3MEACFK3.js +0 -193
- package/dist/chunk-6VIXXLMX.cjs +0 -14
- package/dist/chunk-E3WHL7CO.js +0 -14
- package/dist/chunk-G2FUJNA2.cjs +0 -193
- package/dist/{chunk-UYPEN5XE.cjs → chunk-FFALUUZZ.cjs} +5 -5
- package/dist/{chunk-O466Y272.js → chunk-MQNDWYPN.js} +6 -6
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5;
|
|
2
|
-
|
|
3
|
-
var _chunkSPVWWUHXcjs = require('./chunk-SPVWWUHX.cjs');
|
|
4
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9; var _class10; var _class11; var _class12; var _class13;
|
|
5
2
|
|
|
6
3
|
var _chunkHQP3K7PUcjs = require('./chunk-HQP3K7PU.cjs');
|
|
7
4
|
|
|
@@ -9,7 +6,7 @@ var _chunkHQP3K7PUcjs = require('./chunk-HQP3K7PU.cjs');
|
|
|
9
6
|
var _chunk46ZIFWYGcjs = require('./chunk-46ZIFWYG.cjs');
|
|
10
7
|
|
|
11
8
|
|
|
12
|
-
var
|
|
9
|
+
var _chunk4VAVYUZScjs = require('./chunk-4VAVYUZS.cjs');
|
|
13
10
|
|
|
14
11
|
|
|
15
12
|
var _chunk7YN22L2Hcjs = require('./chunk-7YN22L2H.cjs');
|
|
@@ -18,52 +15,88 @@ var _chunk7YN22L2Hcjs = require('./chunk-7YN22L2H.cjs');
|
|
|
18
15
|
var _chunk2DCNB7DFcjs = require('./chunk-2DCNB7DF.cjs');
|
|
19
16
|
|
|
20
17
|
|
|
21
|
-
var
|
|
18
|
+
var _chunk3QOSDDNIcjs = require('./chunk-3QOSDDNI.cjs');
|
|
22
19
|
|
|
23
20
|
|
|
24
|
-
var
|
|
21
|
+
var _chunkGBHYAGX3cjs = require('./chunk-GBHYAGX3.cjs');
|
|
25
22
|
|
|
26
23
|
|
|
27
|
-
var
|
|
24
|
+
var _chunkRPII3E6Zcjs = require('./chunk-RPII3E6Z.cjs');
|
|
28
25
|
|
|
29
26
|
|
|
30
27
|
|
|
31
|
-
var
|
|
28
|
+
var _chunk6RPZADX3cjs = require('./chunk-6RPZADX3.cjs');
|
|
32
29
|
|
|
33
30
|
|
|
34
|
-
|
|
31
|
+
|
|
32
|
+
var _chunkQWBWGODScjs = require('./chunk-QWBWGODS.cjs');
|
|
35
33
|
|
|
36
34
|
|
|
37
35
|
var _chunkNYUNU75Acjs = require('./chunk-NYUNU75A.cjs');
|
|
38
36
|
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
|
|
39
|
+
var _chunkVQY5W4YWcjs = require('./chunk-VQY5W4YW.cjs');
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
var _chunkY6OM7EV7cjs = require('./chunk-Y6OM7EV7.cjs');
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
var _chunkSVNDB62Dcjs = require('./chunk-SVNDB62D.cjs');
|
|
44
46
|
|
|
45
47
|
|
|
48
|
+
var _chunkR3HK6R4Bcjs = require('./chunk-R3HK6R4B.cjs');
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
var _chunkAEOZIDEBcjs = require('./chunk-AEOZIDEB.cjs');
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
var _chunkL74VDN4Acjs = require('./chunk-L74VDN4A.cjs');
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
var _chunkKUU2RNG6cjs = require('./chunk-KUU2RNG6.cjs');
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
var _chunkQ27JY5RGcjs = require('./chunk-Q27JY5RG.cjs');
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
var _chunkQXD4MV2Xcjs = require('./chunk-QXD4MV2X.cjs');
|
|
67
|
+
|
|
68
|
+
|
|
46
69
|
|
|
47
70
|
|
|
48
71
|
var _chunkA2YMUCBTcjs = require('./chunk-A2YMUCBT.cjs');
|
|
49
72
|
|
|
50
73
|
|
|
51
|
-
var _chunkG2FUJNA2cjs = require('./chunk-G2FUJNA2.cjs');
|
|
52
74
|
|
|
53
75
|
|
|
54
76
|
|
|
55
|
-
var _chunkSUEGJ75Dcjs = require('./chunk-SUEGJ75D.cjs');
|
|
56
77
|
|
|
57
78
|
|
|
58
|
-
var _chunkV5KPWNYXcjs = require('./chunk-V5KPWNYX.cjs');
|
|
59
79
|
|
|
60
80
|
|
|
81
|
+
var _chunkSVXC36MUcjs = require('./chunk-SVXC36MU.cjs');
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
var _chunkSUEGJ75Dcjs = require('./chunk-SUEGJ75D.cjs');
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
var _chunkS6JGLF3Gcjs = require('./chunk-S6JGLF3G.cjs');
|
|
89
|
+
|
|
61
90
|
|
|
62
|
-
var
|
|
91
|
+
var _chunkJMJBSACDcjs = require('./chunk-JMJBSACD.cjs');
|
|
63
92
|
|
|
64
93
|
|
|
65
94
|
var _chunkGMMT7RVNcjs = require('./chunk-GMMT7RVN.cjs');
|
|
66
95
|
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
var _chunkUNEFIWXIcjs = require('./chunk-UNEFIWXI.cjs');
|
|
99
|
+
|
|
67
100
|
// src/completion/StreamEventBuffer.ts
|
|
68
101
|
var TERMINAL_TYPES = /* @__PURE__ */ new Set(["done", "error", "abort"]);
|
|
69
102
|
var QUEUE_CAP = 200;
|
|
@@ -325,7 +358,7 @@ function convertMessageToGemini(msg) {
|
|
|
325
358
|
|
|
326
359
|
// src/completion/openrouter-utils.ts
|
|
327
360
|
function getOpenRouterProviderConfig(provider, modelId) {
|
|
328
|
-
if (!
|
|
361
|
+
if (!_chunkUNEFIWXIcjs.isOpenRouterProvider.call(void 0, provider)) {
|
|
329
362
|
return void 0;
|
|
330
363
|
}
|
|
331
364
|
const modelConfig = _optionalChain([provider, 'access', _ => _.modelConfigs, 'optionalAccess', _2 => _2.find, 'call', _3 => _3((m) => m.id === modelId)]);
|
|
@@ -462,10 +495,11 @@ function recordAnthropicNonStreamUsage(recorder, bodyText, attribution) {
|
|
|
462
495
|
sessionId: attribution.sessionId,
|
|
463
496
|
providerId: attribution.providerId,
|
|
464
497
|
model: attribution.model,
|
|
465
|
-
apiKeyId:
|
|
498
|
+
apiKeyId: attribution.apiKeyId,
|
|
466
499
|
engineOrigin: "codex-ingress",
|
|
467
500
|
usage: tapped,
|
|
468
|
-
rawUsage: parsed.usage
|
|
501
|
+
rawUsage: parsed.usage,
|
|
502
|
+
auditResponse: attribution.auditResponse
|
|
469
503
|
});
|
|
470
504
|
} catch (e4) {
|
|
471
505
|
}
|
|
@@ -502,7 +536,84 @@ function readBody(req) {
|
|
|
502
536
|
req.on("error", reject);
|
|
503
537
|
});
|
|
504
538
|
}
|
|
505
|
-
|
|
539
|
+
function rewriteJsonModel(bodyText, rewriteModel) {
|
|
540
|
+
try {
|
|
541
|
+
const obj = JSON.parse(bodyText);
|
|
542
|
+
if (!obj || typeof obj !== "object") return bodyText;
|
|
543
|
+
let changed = false;
|
|
544
|
+
if (typeof obj.model === "string") {
|
|
545
|
+
obj.model = rewriteModel;
|
|
546
|
+
changed = true;
|
|
547
|
+
}
|
|
548
|
+
const nested = obj.response;
|
|
549
|
+
if (nested && typeof nested === "object" && typeof nested.model === "string") {
|
|
550
|
+
nested.model = rewriteModel;
|
|
551
|
+
changed = true;
|
|
552
|
+
}
|
|
553
|
+
return changed ? JSON.stringify(obj) : bodyText;
|
|
554
|
+
} catch (e5) {
|
|
555
|
+
return bodyText;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function rewriteSseLine(line, rewriteModel) {
|
|
559
|
+
if (!line.startsWith("data:") || !line.includes('"model"')) return line;
|
|
560
|
+
const nl = /\r?\n$/.exec(line);
|
|
561
|
+
const newline = nl ? nl[0] : "";
|
|
562
|
+
const withoutNl = newline ? line.slice(0, line.length - newline.length) : line;
|
|
563
|
+
const afterData = withoutNl.slice("data:".length);
|
|
564
|
+
const leadingSpace = afterData.startsWith(" ") ? " " : "";
|
|
565
|
+
const payload = afterData.slice(leadingSpace.length);
|
|
566
|
+
if (payload === "" || payload === "[DONE]") return line;
|
|
567
|
+
try {
|
|
568
|
+
const obj = JSON.parse(payload);
|
|
569
|
+
if (!obj || typeof obj !== "object") return line;
|
|
570
|
+
const type = typeof obj.type === "string" ? obj.type : "";
|
|
571
|
+
let changed = false;
|
|
572
|
+
if (type === "message_start") {
|
|
573
|
+
const message = obj.message;
|
|
574
|
+
if (message && typeof message === "object" && typeof message.model === "string") {
|
|
575
|
+
message.model = rewriteModel;
|
|
576
|
+
changed = true;
|
|
577
|
+
}
|
|
578
|
+
} else if (type === "response.created" || type === "response.in_progress" || type === "response.completed" || type === "response.failed" || type === "response.incomplete") {
|
|
579
|
+
const response = obj.response;
|
|
580
|
+
if (response && typeof response === "object" && typeof response.model === "string") {
|
|
581
|
+
response.model = rewriteModel;
|
|
582
|
+
changed = true;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (!changed) return line;
|
|
586
|
+
return `data:${leadingSpace}${JSON.stringify(obj)}${newline}`;
|
|
587
|
+
} catch (e6) {
|
|
588
|
+
return line;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
async function relayStreamWithModelRewrite(res, body, rewriteModel) {
|
|
592
|
+
const reader = body.getReader();
|
|
593
|
+
const decoder = new TextDecoder();
|
|
594
|
+
let buffer = "";
|
|
595
|
+
try {
|
|
596
|
+
for (; ; ) {
|
|
597
|
+
const { done, value } = await reader.read();
|
|
598
|
+
if (done) break;
|
|
599
|
+
buffer += decoder.decode(value, { stream: true });
|
|
600
|
+
let out = "";
|
|
601
|
+
let nlIndex;
|
|
602
|
+
while ((nlIndex = buffer.indexOf("\n")) !== -1) {
|
|
603
|
+
const linePart = buffer.slice(0, nlIndex + 1);
|
|
604
|
+
buffer = buffer.slice(nlIndex + 1);
|
|
605
|
+
out += rewriteSseLine(linePart, rewriteModel);
|
|
606
|
+
}
|
|
607
|
+
if (out) res.write(out);
|
|
608
|
+
}
|
|
609
|
+
const tail = buffer + decoder.decode();
|
|
610
|
+
if (tail) res.write(rewriteSseLine(tail, rewriteModel));
|
|
611
|
+
} finally {
|
|
612
|
+
reader.releaseLock();
|
|
613
|
+
res.end();
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
async function relayResponse(res, providerResponse, isStream, rewriteModel) {
|
|
506
617
|
const contentType = _nullishCoalesce(providerResponse.headers.get("Content-Type"), () => ( ""));
|
|
507
618
|
const status = providerResponse.status && providerResponse.status >= 100 ? providerResponse.status : 200;
|
|
508
619
|
if (isStream || contentType.includes("text/event-stream")) {
|
|
@@ -515,6 +626,10 @@ async function relayResponse(res, providerResponse, isStream) {
|
|
|
515
626
|
res.end();
|
|
516
627
|
return null;
|
|
517
628
|
}
|
|
629
|
+
if (rewriteModel) {
|
|
630
|
+
await relayStreamWithModelRewrite(res, providerResponse.body, rewriteModel);
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
518
633
|
const reader = providerResponse.body.getReader();
|
|
519
634
|
try {
|
|
520
635
|
for (; ; ) {
|
|
@@ -529,8 +644,9 @@ async function relayResponse(res, providerResponse, isStream) {
|
|
|
529
644
|
return null;
|
|
530
645
|
}
|
|
531
646
|
const bodyText = await providerResponse.text();
|
|
647
|
+
const clientText = rewriteModel ? rewriteJsonModel(bodyText, rewriteModel) : bodyText;
|
|
532
648
|
res.writeHead(status, { "Content-Type": contentType.includes("json") ? contentType : "application/json" });
|
|
533
|
-
res.end(
|
|
649
|
+
res.end(clientText);
|
|
534
650
|
return bodyText;
|
|
535
651
|
}
|
|
536
652
|
function writeError(res, status, message) {
|
|
@@ -568,17 +684,18 @@ async function buildSubscriptionPlan(res, route, deps, anthropicBody, routeModel
|
|
|
568
684
|
resolvedModel = mapped.resolvedModel;
|
|
569
685
|
scenario = mapped.scenario;
|
|
570
686
|
}
|
|
571
|
-
const
|
|
687
|
+
const sessionKey = _chunkQWBWGODScjs.deriveSubscriptionSessionKey.call(void 0, anthropicBody);
|
|
688
|
+
const plan = buildSubscriptionIterationPlan(profile, route, deps, resolvedModel, isStream, scenario, sessionKey);
|
|
572
689
|
if (!plan) {
|
|
573
690
|
writeError(res, 502, "Subscription profile is missing resolveUpstreamUrl");
|
|
574
691
|
return null;
|
|
575
692
|
}
|
|
576
693
|
return plan;
|
|
577
694
|
}
|
|
578
|
-
function buildSubscriptionIterationPlan(profile, route, deps, resolvedModel, isStream, scenario) {
|
|
695
|
+
function buildSubscriptionIterationPlan(profile, route, deps, resolvedModel, isStream, scenario, sessionKey) {
|
|
579
696
|
const upstreamUrl = _optionalChain([profile, 'access', _8 => _8.resolveUpstreamUrl, 'optionalCall', _9 => _9(resolvedModel, route.subscriptionConfig)]);
|
|
580
697
|
if (!upstreamUrl) return null;
|
|
581
|
-
const auth = _nullishCoalesce(route.auth, () => ( new (0,
|
|
698
|
+
const auth = _nullishCoalesce(route.auth, () => ( new (0, _chunkY6OM7EV7cjs.SubscriptionAuthSource)(profile)));
|
|
582
699
|
const sameFormat = profile.mode === "pass-through" || upstreamUrl.endsWith("/v1/messages");
|
|
583
700
|
const overrideNames = _optionalChain([profile, 'access', _10 => _10.resolveProviderTransformerNames, 'optionalCall', _11 => _11(
|
|
584
701
|
resolvedModel,
|
|
@@ -622,7 +739,8 @@ function buildSubscriptionIterationPlan(profile, route, deps, resolvedModel, isS
|
|
|
622
739
|
upstreamUrl,
|
|
623
740
|
sameFormat,
|
|
624
741
|
isSubscription: true,
|
|
625
|
-
scenario
|
|
742
|
+
scenario,
|
|
743
|
+
sessionKey
|
|
626
744
|
};
|
|
627
745
|
}
|
|
628
746
|
function usesResponsesChain(names) {
|
|
@@ -641,7 +759,7 @@ function buildSubscriptionRequestSummary(anthropicBody) {
|
|
|
641
759
|
estimatedInputTokens: Math.ceil(totalChars / 4),
|
|
642
760
|
// Single source of truth (shared with the dispatcher builder) so both
|
|
643
761
|
// ingress paths yield identical `matchText` for the same body.
|
|
644
|
-
matchText:
|
|
762
|
+
matchText: _chunkQWBWGODScjs.collectMatchText.call(void 0, anthropicBody)
|
|
645
763
|
};
|
|
646
764
|
}
|
|
647
765
|
function collectText(value) {
|
|
@@ -657,12 +775,24 @@ function collectText(value) {
|
|
|
657
775
|
}
|
|
658
776
|
return 0;
|
|
659
777
|
}
|
|
660
|
-
|
|
778
|
+
function proxyProviderId(plan) {
|
|
779
|
+
return plan.isSubscription ? plan.transformerProvider.name : "byo";
|
|
780
|
+
}
|
|
781
|
+
async function runPipeline(anthropicBody, plan, reportSelection) {
|
|
661
782
|
const executor = getSharedExecutor();
|
|
662
783
|
const endpointTransformer = getAnthropicEndpointTransformer();
|
|
663
784
|
const { auth, chain, transformerProvider, resolvedModel, isStream, resolveUrl, upstreamUrl } = plan;
|
|
785
|
+
let proxyAccountId;
|
|
664
786
|
const authHeaders = {};
|
|
665
|
-
await auth.applyHeaders(authHeaders, {
|
|
787
|
+
await auth.applyHeaders(authHeaders, {
|
|
788
|
+
upstreamUrl,
|
|
789
|
+
model: resolvedModel,
|
|
790
|
+
sessionKey: plan.sessionKey,
|
|
791
|
+
reportSelection: (accountId, isActive) => {
|
|
792
|
+
proxyAccountId = accountId;
|
|
793
|
+
_optionalChain([reportSelection, 'optionalCall', _12 => _12(accountId, isActive)]);
|
|
794
|
+
}
|
|
795
|
+
});
|
|
666
796
|
let rawStatus = null;
|
|
667
797
|
const { response } = await _chunkKUU2RNG6cjs.executeProviderCall.call(void 0, {
|
|
668
798
|
executor,
|
|
@@ -682,7 +812,11 @@ async function runPipeline(anthropicBody, plan) {
|
|
|
682
812
|
},
|
|
683
813
|
fetchFn: (url, headers, body) => {
|
|
684
814
|
console.info(`[ProviderProxy:anthropic] -> ${url} model=${resolvedModel} stream=${isStream}`);
|
|
685
|
-
return
|
|
815
|
+
return _chunkS6JGLF3Gcjs.fetchUpstream.call(void 0,
|
|
816
|
+
url,
|
|
817
|
+
{ method: "POST", headers, body: JSON.stringify(body) },
|
|
818
|
+
{ providerId: proxyProviderId(plan), accountId: proxyAccountId }
|
|
819
|
+
).then((r) => {
|
|
686
820
|
rawStatus = r.status;
|
|
687
821
|
return r;
|
|
688
822
|
});
|
|
@@ -691,29 +825,95 @@ async function runPipeline(anthropicBody, plan) {
|
|
|
691
825
|
});
|
|
692
826
|
return { response, rawStatus };
|
|
693
827
|
}
|
|
694
|
-
async function runSubscriptionSameFormatFetch(rawBody, plan) {
|
|
828
|
+
async function runSubscriptionSameFormatFetch(rawBody, plan, reportSelection, options = {}) {
|
|
829
|
+
let proxyAccountId;
|
|
830
|
+
let remappedModel;
|
|
695
831
|
const headers = { "content-type": "application/json" };
|
|
696
832
|
await plan.auth.applyHeaders(headers, {
|
|
697
833
|
upstreamUrl: plan.upstreamUrl,
|
|
698
|
-
model: plan.resolvedModel
|
|
834
|
+
model: plan.resolvedModel,
|
|
835
|
+
sessionKey: plan.sessionKey,
|
|
836
|
+
reportSelection: (accountId, isActive, remapped) => {
|
|
837
|
+
proxyAccountId = accountId;
|
|
838
|
+
remappedModel = remapped;
|
|
839
|
+
_optionalChain([reportSelection, 'optionalCall', _13 => _13(accountId, isActive, remapped)]);
|
|
840
|
+
}
|
|
699
841
|
});
|
|
842
|
+
if (plan.isSubscription && plan.transformerProvider.name === "claude") {
|
|
843
|
+
_chunk6RPZADX3cjs.applyFingerprint.call(void 0,
|
|
844
|
+
_chunkRPII3E6Zcjs.getSharedIdentityStore.call(void 0, ),
|
|
845
|
+
headers,
|
|
846
|
+
plan.transformerProvider.name,
|
|
847
|
+
proxyAccountId,
|
|
848
|
+
options.callerIdentity
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
const applyRemap = remappedModel !== void 0 && outboundRemapApplies(plan);
|
|
852
|
+
const outboundBody = applyRemap ? rewriteBodyModel(rawBody, remappedModel) : rawBody;
|
|
853
|
+
const outboundModel = applyRemap ? remappedModel : plan.resolvedModel;
|
|
700
854
|
console.info(
|
|
701
|
-
`[ProviderProxy:anthropic] (subscription same-format) -> ${plan.upstreamUrl} model=${
|
|
855
|
+
`[ProviderProxy:anthropic] (subscription same-format) -> ${plan.upstreamUrl} model=${outboundModel} stream=${plan.isStream}`
|
|
856
|
+
);
|
|
857
|
+
const response = await _chunkS6JGLF3Gcjs.fetchUpstream.call(void 0,
|
|
858
|
+
plan.upstreamUrl,
|
|
859
|
+
{ method: "POST", headers, body: outboundBody },
|
|
860
|
+
{ providerId: proxyProviderId(plan), accountId: proxyAccountId }
|
|
702
861
|
);
|
|
703
|
-
const response = await fetch(plan.upstreamUrl, { method: "POST", headers, body: rawBody });
|
|
704
862
|
return { response, rawStatus: response.status };
|
|
705
863
|
}
|
|
706
|
-
|
|
707
|
-
|
|
864
|
+
function outboundRemapApplies(plan) {
|
|
865
|
+
return plan.isSubscription && plan.transformerProvider.name === "claude";
|
|
866
|
+
}
|
|
867
|
+
function rewriteBodyModel(rawBody, model) {
|
|
868
|
+
try {
|
|
869
|
+
const parsed = JSON.parse(rawBody);
|
|
870
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return rawBody;
|
|
871
|
+
parsed.model = model;
|
|
872
|
+
return JSON.stringify(parsed);
|
|
873
|
+
} catch (e7) {
|
|
874
|
+
return rawBody;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
async function runSubscriptionAttemptWith401Retry(anthropicBody, relayBody, plan, options = {}) {
|
|
878
|
+
let selectedAccountId;
|
|
879
|
+
const reportSelection = (accountId) => {
|
|
880
|
+
selectedAccountId = accountId;
|
|
881
|
+
};
|
|
882
|
+
const runOnce = () => plan.sameFormat ? runSubscriptionSameFormatFetch(relayBody, plan, reportSelection, options) : runPipeline(anthropicBody, plan, reportSelection);
|
|
708
883
|
const first = await runOnce();
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
884
|
+
let result = first;
|
|
885
|
+
if (first.rawStatus === 401) {
|
|
886
|
+
const refreshed = await _optionalChain([plan, 'access', _14 => _14.auth, 'access', _15 => _15.onUnauthorized, 'optionalCall', _16 => _16(plan.sessionKey)]);
|
|
887
|
+
if (refreshed) {
|
|
888
|
+
console.info("[ProviderProxy:anthropic] 401 \u2192 token refreshed; retrying once");
|
|
889
|
+
result = await runOnce();
|
|
890
|
+
} else {
|
|
891
|
+
console.warn("[ProviderProxy:anthropic] 401 not recoverable (onUnauthorized returned false)");
|
|
892
|
+
}
|
|
714
893
|
}
|
|
715
|
-
|
|
716
|
-
return
|
|
894
|
+
await markSubscriptionHealth(plan, selectedAccountId, result);
|
|
895
|
+
return result;
|
|
896
|
+
}
|
|
897
|
+
async function readBoundedBody(response, max = 2048) {
|
|
898
|
+
try {
|
|
899
|
+
const text = await response.clone().text();
|
|
900
|
+
return text.slice(0, max);
|
|
901
|
+
} catch (e8) {
|
|
902
|
+
return void 0;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
async function markSubscriptionHealth(plan, selectedAccountId, result) {
|
|
906
|
+
if (!plan.isSubscription || selectedAccountId === void 0) return;
|
|
907
|
+
const providerId = plan.transformerProvider.name;
|
|
908
|
+
const status = result.rawStatus;
|
|
909
|
+
const { resetHeaderSeconds, retryAfterSeconds } = _chunkVQY5W4YWcjs.resolveResetSeconds.call(void 0, providerId, result.response.headers);
|
|
910
|
+
const bodyText = status === 403 ? await readBoundedBody(result.response) : void 0;
|
|
911
|
+
_chunkVQY5W4YWcjs.getSharedAccountHealth.call(void 0, ).recordUpstreamOutcome(providerId, selectedAccountId, {
|
|
912
|
+
status,
|
|
913
|
+
resetHeaderSeconds,
|
|
914
|
+
retryAfterSeconds,
|
|
915
|
+
bodyText
|
|
916
|
+
});
|
|
717
917
|
}
|
|
718
918
|
function isFallbackEligibleStatus(rawStatus) {
|
|
719
919
|
return rawStatus === null || rawStatus >= 500 || rawStatus === 429;
|
|
@@ -726,11 +926,11 @@ function breakerOutcome(rawStatus) {
|
|
|
726
926
|
function recordBreakerOutcome(profile, modelId, rawStatus) {
|
|
727
927
|
const outcome = breakerOutcome(rawStatus);
|
|
728
928
|
if (outcome === "neutral") return;
|
|
729
|
-
_optionalChain([profile, 'optionalAccess',
|
|
929
|
+
_optionalChain([profile, 'optionalAccess', _17 => _17.recordModelOutcome, 'optionalCall', _18 => _18(modelId, outcome === "success")]);
|
|
730
930
|
}
|
|
731
|
-
async function runSubscriptionAttemptOutcome(anthropicBody, relayBody, plan) {
|
|
931
|
+
async function runSubscriptionAttemptOutcome(anthropicBody, relayBody, plan, options = {}) {
|
|
732
932
|
try {
|
|
733
|
-
return { kind: "result", result: await runSubscriptionAttemptWith401Retry(anthropicBody, relayBody, plan) };
|
|
933
|
+
return { kind: "result", result: await runSubscriptionAttemptWith401Retry(anthropicBody, relayBody, plan, options) };
|
|
734
934
|
} catch (error) {
|
|
735
935
|
return { kind: "thrown", error };
|
|
736
936
|
}
|
|
@@ -742,11 +942,11 @@ function settleOutcome(outcome) {
|
|
|
742
942
|
if (outcome.kind === "thrown") throw outcome.error;
|
|
743
943
|
return outcome.result;
|
|
744
944
|
}
|
|
745
|
-
async function runPipelineWithSubscriptionRetry(anthropicBody, rawBody, initialPlan, route, deps) {
|
|
945
|
+
async function runPipelineWithSubscriptionRetry(anthropicBody, rawBody, initialPlan, route, deps, options = {}) {
|
|
746
946
|
const profile = route.subscriptionProfile;
|
|
747
947
|
const scenario = initialPlan.scenario;
|
|
748
|
-
if (!_optionalChain([profile, 'optionalAccess',
|
|
749
|
-
const loneOutcome = await runSubscriptionAttemptOutcome(anthropicBody, rawBody, initialPlan);
|
|
948
|
+
if (!_optionalChain([profile, 'optionalAccess', _19 => _19.nextFallback]) || scenario === void 0) {
|
|
949
|
+
const loneOutcome = await runSubscriptionAttemptOutcome(anthropicBody, rawBody, initialPlan, options);
|
|
750
950
|
recordBreakerOutcome(profile, initialPlan.resolvedModel, outcomeStatus(loneOutcome));
|
|
751
951
|
return settleOutcome(loneOutcome);
|
|
752
952
|
}
|
|
@@ -757,7 +957,7 @@ async function runPipelineWithSubscriptionRetry(anthropicBody, rawBody, initialP
|
|
|
757
957
|
if (profile.allowModel && !profile.allowModel(initialPlan.resolvedModel)) {
|
|
758
958
|
attempted.push(initialPlan.resolvedModel);
|
|
759
959
|
const firstAdmitting = profile.nextFallback(scenario, attempted, route.subscriptionConfig);
|
|
760
|
-
const gatedPlan = firstAdmitting ? buildSubscriptionIterationPlan(profile, route, deps, firstAdmitting.modelId, initialPlan.isStream, scenario) : null;
|
|
960
|
+
const gatedPlan = firstAdmitting ? buildSubscriptionIterationPlan(profile, route, deps, firstAdmitting.modelId, initialPlan.isStream, scenario, initialPlan.sessionKey) : null;
|
|
761
961
|
if (firstAdmitting && gatedPlan) {
|
|
762
962
|
console.warn(
|
|
763
963
|
`[ProviderProxy:anthropic] subscription primary ${initialPlan.resolvedModel} circuit open -> first admitting fallback ${firstAdmitting.modelId}`
|
|
@@ -772,7 +972,7 @@ async function runPipelineWithSubscriptionRetry(anthropicBody, rawBody, initialP
|
|
|
772
972
|
);
|
|
773
973
|
}
|
|
774
974
|
}
|
|
775
|
-
let outcome = await runSubscriptionAttemptOutcome(firstBodyObj, firstRelayBody, plan);
|
|
975
|
+
let outcome = await runSubscriptionAttemptOutcome(firstBodyObj, firstRelayBody, plan, options);
|
|
776
976
|
recordBreakerOutcome(profile, plan.resolvedModel, outcomeStatus(outcome));
|
|
777
977
|
if (!attempted.includes(plan.resolvedModel)) attempted.push(plan.resolvedModel);
|
|
778
978
|
while (attempted.length < MAX_FALLBACK_ATTEMPTS) {
|
|
@@ -785,7 +985,8 @@ async function runPipelineWithSubscriptionRetry(anthropicBody, rawBody, initialP
|
|
|
785
985
|
deps,
|
|
786
986
|
next.modelId,
|
|
787
987
|
plan.isStream,
|
|
788
|
-
scenario
|
|
988
|
+
scenario,
|
|
989
|
+
initialPlan.sessionKey
|
|
789
990
|
);
|
|
790
991
|
if (!nextPlan) return settleOutcome(outcome);
|
|
791
992
|
const sinceLabel = outcome.kind === "thrown" ? "thrown error" : `status ${String(outcome.result.rawStatus)}`;
|
|
@@ -796,7 +997,7 @@ async function runPipelineWithSubscriptionRetry(anthropicBody, rawBody, initialP
|
|
|
796
997
|
const fallbackRelayBody = nextPlan.sameFormat ? JSON.stringify(fallbackBodyObj) : rawBody;
|
|
797
998
|
plan = nextPlan;
|
|
798
999
|
attempted.push(next.modelId);
|
|
799
|
-
outcome = await runSubscriptionAttemptOutcome(fallbackBodyObj, fallbackRelayBody, plan);
|
|
1000
|
+
outcome = await runSubscriptionAttemptOutcome(fallbackBodyObj, fallbackRelayBody, plan, options);
|
|
800
1001
|
recordBreakerOutcome(profile, plan.resolvedModel, outcomeStatus(outcome));
|
|
801
1002
|
}
|
|
802
1003
|
return settleOutcome(outcome);
|
|
@@ -808,7 +1009,7 @@ async function handleAnthropicMessagesByo(res, rawBody, route, deps, options = {
|
|
|
808
1009
|
let anthropicBody;
|
|
809
1010
|
try {
|
|
810
1011
|
anthropicBody = JSON.parse(rawBody);
|
|
811
|
-
} catch (
|
|
1012
|
+
} catch (e9) {
|
|
812
1013
|
writeError(res, 400, "Invalid JSON in request body");
|
|
813
1014
|
return;
|
|
814
1015
|
}
|
|
@@ -819,13 +1020,16 @@ async function handleAnthropicMessagesByo(res, rawBody, route, deps, options = {
|
|
|
819
1020
|
const plan = route.authMode === "subscription" ? await buildSubscriptionPlan(res, route, deps, anthropicBody, resolvedModel, isStream) : await buildByoPlan(res, route, deps, resolvedModel, isStream);
|
|
820
1021
|
if (!plan) return;
|
|
821
1022
|
anthropicBody.model = plan.resolvedModel;
|
|
822
|
-
const providerResponse = route.authMode === "subscription" ? await runPipelineWithSubscriptionRetry(anthropicBody, rawBody, plan, route, deps) : await runPipelineWithPoolReporting(anthropicBody, rawBody, plan, options);
|
|
823
|
-
const bodyText = await relayResponse(res, providerResponse.response, isStream);
|
|
1023
|
+
const providerResponse = route.authMode === "subscription" ? await runPipelineWithSubscriptionRetry(anthropicBody, rawBody, plan, route, deps, options) : await runPipelineWithPoolReporting(anthropicBody, rawBody, plan, options);
|
|
1024
|
+
const bodyText = await relayResponse(res, providerResponse.response, isStream, route.requestedModel);
|
|
824
1025
|
if (bodyText && deps.usageRecorder) {
|
|
825
1026
|
recordAnthropicNonStreamUsage(deps.usageRecorder, bodyText, {
|
|
826
1027
|
sessionId: route.sessionId,
|
|
827
1028
|
providerId: _nullishCoalesce(route.providerId, () => ( "anthropic")),
|
|
828
|
-
model: plan.resolvedModel
|
|
1029
|
+
model: plan.resolvedModel,
|
|
1030
|
+
apiKeyId: _nullishCoalesce(route.apiKeyId, () => ( null)),
|
|
1031
|
+
// request-audit-log: correlate this request's tokens/cost to its audit record.
|
|
1032
|
+
auditResponse: res
|
|
829
1033
|
});
|
|
830
1034
|
}
|
|
831
1035
|
} catch (err) {
|
|
@@ -850,7 +1054,7 @@ async function buildByoPlan(res, route, deps, resolvedModel, isStream) {
|
|
|
850
1054
|
writeError(res, 502, "API key not configured");
|
|
851
1055
|
return null;
|
|
852
1056
|
}
|
|
853
|
-
const auth = _nullishCoalesce(route.auth, () => ( new (0,
|
|
1057
|
+
const auth = _nullishCoalesce(route.auth, () => ( new (0, _chunkQ27JY5RGcjs.LlmConfigProviderAuth)({
|
|
854
1058
|
provider,
|
|
855
1059
|
apiKey,
|
|
856
1060
|
apiKeyPool: _nullishCoalesce(deps.apiKeyPool, () => ( null)),
|
|
@@ -878,17 +1082,17 @@ async function buildByoPlan(res, route, deps, resolvedModel, isStream) {
|
|
|
878
1082
|
isSubscription: false,
|
|
879
1083
|
provider,
|
|
880
1084
|
apiKey,
|
|
881
|
-
extendedContextEnabled: _nullishCoalesce(_optionalChain([route, 'access',
|
|
1085
|
+
extendedContextEnabled: _nullishCoalesce(_optionalChain([route, 'access', _20 => _20.anthropicSdkHints, 'optionalAccess', _21 => _21.extendedContext, 'optionalAccess', _22 => _22.enabled]), () => ( false))
|
|
882
1086
|
};
|
|
883
1087
|
}
|
|
884
|
-
async function runSameFormatFetch(
|
|
1088
|
+
async function runSameFormatFetch(bodyToSend, plan, options, keyOverride) {
|
|
885
1089
|
const { provider, apiKey, resolvedModel, isStream, extendedContextEnabled } = plan;
|
|
886
1090
|
if (!provider) {
|
|
887
1091
|
throw new Error("[ProviderProxy:anthropic] same-format BYO path requires a provider row");
|
|
888
1092
|
}
|
|
889
1093
|
const effectiveKey = _nullishCoalesce(_nullishCoalesce(keyOverride, () => ( apiKey)), () => ( ""));
|
|
890
|
-
const headers =
|
|
891
|
-
const callerBeta = _optionalChain([options, 'access',
|
|
1094
|
+
const headers = _chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, effectiveKey);
|
|
1095
|
+
const callerBeta = _optionalChain([options, 'access', _23 => _23.callerAnthropicBeta, 'optionalAccess', _24 => _24.trim, 'call', _25 => _25()]);
|
|
892
1096
|
if (callerBeta) {
|
|
893
1097
|
const existing = _nullishCoalesce(headers["anthropic-beta"], () => ( headers["Anthropic-Beta"]));
|
|
894
1098
|
const parts = new Set(
|
|
@@ -902,14 +1106,29 @@ async function runSameFormatFetch(rawBody, plan, options, keyOverride) {
|
|
|
902
1106
|
_chunkGBHYAGX3cjs.injectExtendedContextBeta.call(void 0, headers, resolvedModel, _nullishCoalesce(extendedContextEnabled, () => ( false)));
|
|
903
1107
|
const url = _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: resolvedModel, stream: isStream });
|
|
904
1108
|
console.info(`[ProviderProxy:anthropic] (same-format) -> ${url} model=${resolvedModel} stream=${isStream}`);
|
|
905
|
-
const response = await
|
|
1109
|
+
const response = await _chunkS6JGLF3Gcjs.fetchUpstream.call(void 0,
|
|
1110
|
+
url,
|
|
1111
|
+
{ method: "POST", headers, body: bodyToSend },
|
|
1112
|
+
{ providerId: "byo" }
|
|
1113
|
+
);
|
|
906
1114
|
return { response, rawStatus: response.status };
|
|
907
1115
|
}
|
|
1116
|
+
function resolveSameFormatBody(anthropicBody, rawBody, plan) {
|
|
1117
|
+
let originalModel;
|
|
1118
|
+
try {
|
|
1119
|
+
const m = JSON.parse(rawBody).model;
|
|
1120
|
+
originalModel = typeof m === "string" ? m : void 0;
|
|
1121
|
+
} catch (e10) {
|
|
1122
|
+
originalModel = void 0;
|
|
1123
|
+
}
|
|
1124
|
+
return plan.resolvedModel !== originalModel ? JSON.stringify(anthropicBody) : rawBody;
|
|
1125
|
+
}
|
|
908
1126
|
async function runPipelineWithPoolReporting(anthropicBody, rawBody, plan, options) {
|
|
909
|
-
const
|
|
1127
|
+
const sameFormatBody = plan.sameFormat ? resolveSameFormatBody(anthropicBody, rawBody, plan) : rawBody;
|
|
1128
|
+
const runOnce = (keyOverride) => plan.sameFormat ? runSameFormatFetch(sameFormatBody, plan, options, keyOverride) : runPipeline(anthropicBody, plan);
|
|
910
1129
|
const first = await runOnce();
|
|
911
|
-
const outcome = await _optionalChain([plan, 'access',
|
|
912
|
-
if (_optionalChain([outcome, 'optionalAccess',
|
|
1130
|
+
const outcome = await _optionalChain([plan, 'access', _26 => _26.auth, 'access', _27 => _27.onResult, 'optionalCall', _28 => _28(first.rawStatus)]);
|
|
1131
|
+
if (_optionalChain([outcome, 'optionalAccess', _29 => _29.rebound])) {
|
|
913
1132
|
console.info(
|
|
914
1133
|
"[ProviderProxy:anthropic] pool re-bound key after status",
|
|
915
1134
|
first.rawStatus,
|
|
@@ -930,7 +1149,8 @@ async function handleAnthropicMessagesRequest(req, res, route, deps) {
|
|
|
930
1149
|
const rawBody = await readBody(req);
|
|
931
1150
|
const callerBetaRaw = req.headers["anthropic-beta"];
|
|
932
1151
|
const callerAnthropicBeta = Array.isArray(callerBetaRaw) ? callerBetaRaw.join(",") : callerBetaRaw;
|
|
933
|
-
|
|
1152
|
+
const callerIdentity = _chunk6RPZADX3cjs.captureCallerIdentity.call(void 0, _chunkRPII3E6Zcjs.getSharedIdentityStore.call(void 0, ), req.headers);
|
|
1153
|
+
await handleAnthropicMessagesByo(res, rawBody, route, deps, { callerAnthropicBeta, callerIdentity });
|
|
934
1154
|
return;
|
|
935
1155
|
}
|
|
936
1156
|
const hints = route.anthropicSdkHints;
|
|
@@ -992,12 +1212,13 @@ function recordGeminiNonStreamUsage(recorder, bodyText, attribution) {
|
|
|
992
1212
|
sessionId: attribution.sessionId,
|
|
993
1213
|
providerId: attribution.providerId,
|
|
994
1214
|
model: attribution.model,
|
|
995
|
-
apiKeyId:
|
|
1215
|
+
apiKeyId: attribution.apiKeyId,
|
|
1216
|
+
auditResponse: attribution.auditResponse,
|
|
996
1217
|
engineOrigin: "codex-ingress",
|
|
997
1218
|
usage: tapped,
|
|
998
1219
|
rawUsage: parsed.usageMetadata
|
|
999
1220
|
});
|
|
1000
|
-
} catch (
|
|
1221
|
+
} catch (e11) {
|
|
1001
1222
|
}
|
|
1002
1223
|
}
|
|
1003
1224
|
|
|
@@ -1006,14 +1227,14 @@ var GENERATE_CONTENT_ACTION = ":generateContent";
|
|
|
1006
1227
|
var STREAM_GENERATE_CONTENT_ACTION = ":streamGenerateContent";
|
|
1007
1228
|
function isGeminiGenerateContentRequest(method, url) {
|
|
1008
1229
|
if (method !== "POST" || !url) return false;
|
|
1009
|
-
const path = _nullishCoalesce(_optionalChain([url, 'access',
|
|
1230
|
+
const path = _nullishCoalesce(_optionalChain([url, 'access', _30 => _30.split, 'call', _31 => _31("?"), 'access', _32 => _32[0], 'optionalAccess', _33 => _33.replace, 'call', _34 => _34(/\/+$/, "")]), () => ( ""));
|
|
1010
1231
|
const lastSegment = _nullishCoalesce(path.split("/").pop(), () => ( ""));
|
|
1011
1232
|
return lastSegment.includes(STREAM_GENERATE_CONTENT_ACTION) || lastSegment.includes(GENERATE_CONTENT_ACTION);
|
|
1012
1233
|
}
|
|
1013
1234
|
function isGeminiStreamRequest(url) {
|
|
1014
1235
|
if (!url) return false;
|
|
1015
1236
|
const [path, query = ""] = url.split("?");
|
|
1016
|
-
const lastSegment = _nullishCoalesce((_nullishCoalesce(_optionalChain([path, 'optionalAccess',
|
|
1237
|
+
const lastSegment = _nullishCoalesce((_nullishCoalesce(_optionalChain([path, 'optionalAccess', _35 => _35.replace, 'call', _36 => _36(/\/+$/, "")]), () => ( ""))).split("/").pop(), () => ( ""));
|
|
1017
1238
|
if (lastSegment.includes(STREAM_GENERATE_CONTENT_ACTION)) return true;
|
|
1018
1239
|
return /(^|&)alt=sse(&|$)/.test(query);
|
|
1019
1240
|
}
|
|
@@ -1021,7 +1242,7 @@ async function handleGeminiGenerateContentRequest(res, rawBody, url, route, deps
|
|
|
1021
1242
|
let geminiBody;
|
|
1022
1243
|
try {
|
|
1023
1244
|
geminiBody = JSON.parse(rawBody);
|
|
1024
|
-
} catch (
|
|
1245
|
+
} catch (e12) {
|
|
1025
1246
|
writeError(res, 400, "Invalid JSON in request body");
|
|
1026
1247
|
return;
|
|
1027
1248
|
}
|
|
@@ -1042,7 +1263,10 @@ async function handleGeminiGenerateContentRequest(res, rawBody, url, route, deps
|
|
|
1042
1263
|
recordGeminiNonStreamUsage(deps.usageRecorder, bodyText, {
|
|
1043
1264
|
sessionId: route.sessionId,
|
|
1044
1265
|
providerId: _nullishCoalesce(route.providerId, () => ( "gemini")),
|
|
1045
|
-
model: resolvedModel
|
|
1266
|
+
model: resolvedModel,
|
|
1267
|
+
apiKeyId: _nullishCoalesce(route.apiKeyId, () => ( null)),
|
|
1268
|
+
// request-audit-log: correlate this request's tokens/cost to its audit record.
|
|
1269
|
+
auditResponse: res
|
|
1046
1270
|
});
|
|
1047
1271
|
}
|
|
1048
1272
|
} catch (err) {
|
|
@@ -1067,7 +1291,7 @@ async function buildByoPlan2(res, route, deps, resolvedModel, isStream) {
|
|
|
1067
1291
|
writeError(res, 502, "API key not configured");
|
|
1068
1292
|
return null;
|
|
1069
1293
|
}
|
|
1070
|
-
const auth = _nullishCoalesce(route.auth, () => ( new (0,
|
|
1294
|
+
const auth = _nullishCoalesce(route.auth, () => ( new (0, _chunkQ27JY5RGcjs.LlmConfigProviderAuth)({
|
|
1071
1295
|
provider,
|
|
1072
1296
|
apiKey,
|
|
1073
1297
|
apiKeyPool: _nullishCoalesce(deps.apiKeyPool, () => ( null)),
|
|
@@ -1117,7 +1341,11 @@ async function runPipeline2(geminiBody, plan) {
|
|
|
1117
1341
|
},
|
|
1118
1342
|
fetchFn: (url, headers, body) => {
|
|
1119
1343
|
console.log(`[ProviderProxy:gemini] -> ${url} model=${resolvedModel} stream=${isStream}`);
|
|
1120
|
-
return
|
|
1344
|
+
return _chunkS6JGLF3Gcjs.fetchUpstream.call(void 0,
|
|
1345
|
+
url,
|
|
1346
|
+
{ method: "POST", headers, body: JSON.stringify(body) },
|
|
1347
|
+
{ providerId: "byo" }
|
|
1348
|
+
).then((r) => {
|
|
1121
1349
|
rawStatus = r.status;
|
|
1122
1350
|
return r;
|
|
1123
1351
|
});
|
|
@@ -1128,8 +1356,8 @@ async function runPipeline2(geminiBody, plan) {
|
|
|
1128
1356
|
}
|
|
1129
1357
|
async function runPipelineWithPoolReporting2(geminiBody, plan) {
|
|
1130
1358
|
const first = await runPipeline2(geminiBody, plan);
|
|
1131
|
-
const outcome = await _optionalChain([plan, 'access',
|
|
1132
|
-
if (_optionalChain([outcome, 'optionalAccess',
|
|
1359
|
+
const outcome = await _optionalChain([plan, 'access', _37 => _37.auth, 'access', _38 => _38.onResult, 'optionalCall', _39 => _39(first.rawStatus)]);
|
|
1360
|
+
if (_optionalChain([outcome, 'optionalAccess', _40 => _40.rebound])) {
|
|
1133
1361
|
console.log(
|
|
1134
1362
|
"[ProviderProxy:gemini] pool re-bound key after status",
|
|
1135
1363
|
first.rawStatus,
|
|
@@ -1169,46 +1397,48 @@ function recordChatCompletionsNonStreamUsage(recorder, bodyText, attribution) {
|
|
|
1169
1397
|
sessionId: attribution.sessionId,
|
|
1170
1398
|
providerId: attribution.providerId,
|
|
1171
1399
|
model: attribution.model,
|
|
1172
|
-
apiKeyId:
|
|
1400
|
+
apiKeyId: attribution.apiKeyId,
|
|
1401
|
+
auditResponse: attribution.auditResponse,
|
|
1173
1402
|
engineOrigin: "codex-ingress",
|
|
1174
1403
|
usage: tapped,
|
|
1175
1404
|
rawUsage: parsed.usage
|
|
1176
1405
|
});
|
|
1177
|
-
} catch (
|
|
1406
|
+
} catch (e13) {
|
|
1178
1407
|
}
|
|
1179
1408
|
}
|
|
1180
1409
|
|
|
1181
1410
|
// src/provider-proxy/ingress/openaiChatIngress.ts
|
|
1182
1411
|
function isOpenAIChatRequest(method, url) {
|
|
1183
1412
|
if (method !== "POST" || !url) return false;
|
|
1184
|
-
const path = _nullishCoalesce(_optionalChain([url, 'access',
|
|
1413
|
+
const path = _nullishCoalesce(_optionalChain([url, 'access', _41 => _41.split, 'call', _42 => _42("?"), 'access', _43 => _43[0], 'optionalAccess', _44 => _44.replace, 'call', _45 => _45(/\/+$/, "")]), () => ( ""));
|
|
1185
1414
|
return path.endsWith("/chat/completions");
|
|
1186
1415
|
}
|
|
1416
|
+
var CHAT_BRIDGE_SUBSCRIPTION_PROVIDERS = /* @__PURE__ */ new Set(["claude"]);
|
|
1417
|
+
var IDENTITY_ENDPOINT_TRANSFORMER = { name: "identity-openai-chat" };
|
|
1187
1418
|
async function handleOpenAIChatRequest(res, rawBody, route, deps) {
|
|
1188
1419
|
let chatBody;
|
|
1189
1420
|
try {
|
|
1190
1421
|
chatBody = JSON.parse(rawBody);
|
|
1191
|
-
} catch (
|
|
1422
|
+
} catch (e14) {
|
|
1192
1423
|
writeError(res, 400, "Invalid JSON in request body");
|
|
1193
1424
|
return;
|
|
1194
1425
|
}
|
|
1195
|
-
if (route.authMode === "subscription") {
|
|
1196
|
-
writeError(res, 502, "OpenAI Chat Completions ingress does not support subscription auth");
|
|
1197
|
-
return;
|
|
1198
|
-
}
|
|
1199
1426
|
const isStream = chatBody.stream === true;
|
|
1200
1427
|
const resolvedModel = route.model;
|
|
1201
1428
|
chatBody.model = resolvedModel;
|
|
1202
1429
|
try {
|
|
1203
|
-
const plan = await buildByoPlan3(res, route, deps, resolvedModel, isStream);
|
|
1430
|
+
const plan = route.authMode === "subscription" ? await buildSubscriptionPlan2(res, route, deps, resolvedModel, isStream) : await buildByoPlan3(res, route, deps, resolvedModel, isStream);
|
|
1204
1431
|
if (!plan) return;
|
|
1205
|
-
const providerResponse = await runPipelineWithPoolReporting3(chatBody, plan);
|
|
1432
|
+
const providerResponse = plan.isSubscription ? await runPipelineWithSubscriptionRetry2(chatBody, plan) : await runPipelineWithPoolReporting3(chatBody, plan);
|
|
1206
1433
|
const bodyText = await relayResponse(res, providerResponse.response, isStream);
|
|
1207
1434
|
if (bodyText && deps.usageRecorder) {
|
|
1208
1435
|
recordChatCompletionsNonStreamUsage(deps.usageRecorder, bodyText, {
|
|
1209
1436
|
sessionId: route.sessionId,
|
|
1210
1437
|
providerId: _nullishCoalesce(route.providerId, () => ( "openai")),
|
|
1211
|
-
model: resolvedModel
|
|
1438
|
+
model: resolvedModel,
|
|
1439
|
+
apiKeyId: _nullishCoalesce(route.apiKeyId, () => ( null)),
|
|
1440
|
+
// request-audit-log: correlate this request's tokens/cost to its audit record.
|
|
1441
|
+
auditResponse: res
|
|
1212
1442
|
});
|
|
1213
1443
|
}
|
|
1214
1444
|
} catch (err) {
|
|
@@ -1233,7 +1463,7 @@ async function buildByoPlan3(res, route, deps, resolvedModel, isStream) {
|
|
|
1233
1463
|
writeError(res, 502, "API key not configured");
|
|
1234
1464
|
return null;
|
|
1235
1465
|
}
|
|
1236
|
-
const auth = _nullishCoalesce(route.auth, () => ( new (0,
|
|
1466
|
+
const auth = _nullishCoalesce(route.auth, () => ( new (0, _chunkQ27JY5RGcjs.LlmConfigProviderAuth)({
|
|
1237
1467
|
provider,
|
|
1238
1468
|
apiKey,
|
|
1239
1469
|
apiKeyPool: _nullishCoalesce(deps.apiKeyPool, () => ( null)),
|
|
@@ -1255,14 +1485,67 @@ async function buildByoPlan3(res, route, deps, resolvedModel, isStream) {
|
|
|
1255
1485
|
resolvedModel,
|
|
1256
1486
|
isStream,
|
|
1257
1487
|
resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : byoUrl,
|
|
1258
|
-
upstreamUrl: byoUrl
|
|
1488
|
+
upstreamUrl: byoUrl,
|
|
1489
|
+
proxyProviderId: "byo",
|
|
1490
|
+
isSubscription: false
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
async function buildSubscriptionPlan2(res, route, deps, resolvedModel, isStream) {
|
|
1494
|
+
const profile = route.subscriptionProfile;
|
|
1495
|
+
if (!profile) {
|
|
1496
|
+
writeError(res, 502, "Subscription mode requires a subscription profile");
|
|
1497
|
+
return null;
|
|
1498
|
+
}
|
|
1499
|
+
const providerId = profile.authStrategy.providerId;
|
|
1500
|
+
if (!CHAT_BRIDGE_SUBSCRIPTION_PROVIDERS.has(providerId)) {
|
|
1501
|
+
writeError(
|
|
1502
|
+
res,
|
|
1503
|
+
501,
|
|
1504
|
+
`the OpenAI-chat\u2192subscription bridge currently supports claude only; subscription provider '${providerId}' is not implemented on this endpoint yet (use the /v1/responses endpoint, a Claude subscription, or a BYO provider)`
|
|
1505
|
+
);
|
|
1506
|
+
return null;
|
|
1507
|
+
}
|
|
1508
|
+
const upstreamUrl = _optionalChain([profile, 'access', _46 => _46.resolveUpstreamUrl, 'optionalCall', _47 => _47(resolvedModel)]);
|
|
1509
|
+
if (!upstreamUrl) {
|
|
1510
|
+
writeError(res, 502, "Subscription profile is missing resolveUpstreamUrl");
|
|
1511
|
+
return null;
|
|
1512
|
+
}
|
|
1513
|
+
const auth = _nullishCoalesce(route.auth, () => ( new (0, _chunkY6OM7EV7cjs.SubscriptionAuthSource)(profile)));
|
|
1514
|
+
const chain = _chunkNYUNU75Acjs.resolveSubscriptionChain.call(void 0,
|
|
1515
|
+
profile,
|
|
1516
|
+
deps.llmConfig.getTransformerService(),
|
|
1517
|
+
IDENTITY_ENDPOINT_TRANSFORMER
|
|
1518
|
+
);
|
|
1519
|
+
const transformerProvider = {
|
|
1520
|
+
name: providerId,
|
|
1521
|
+
baseUrl: upstreamUrl,
|
|
1522
|
+
apiKey: "",
|
|
1523
|
+
models: [resolvedModel]
|
|
1524
|
+
};
|
|
1525
|
+
return {
|
|
1526
|
+
auth,
|
|
1527
|
+
chain,
|
|
1528
|
+
transformerProvider,
|
|
1529
|
+
resolvedModel,
|
|
1530
|
+
isStream,
|
|
1531
|
+
resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : typeof config.url === "string" ? config.url : upstreamUrl,
|
|
1532
|
+
upstreamUrl,
|
|
1533
|
+
proxyProviderId: providerId,
|
|
1534
|
+
isSubscription: true
|
|
1259
1535
|
};
|
|
1260
1536
|
}
|
|
1261
1537
|
async function runPipeline3(chatBody, plan) {
|
|
1262
1538
|
const executor = getSharedExecutor();
|
|
1263
1539
|
const { auth, chain, transformerProvider, resolvedModel, isStream, resolveUrl, upstreamUrl } = plan;
|
|
1540
|
+
let proxyAccountId;
|
|
1264
1541
|
const authHeaders = {};
|
|
1265
|
-
await auth.applyHeaders(authHeaders, {
|
|
1542
|
+
await auth.applyHeaders(authHeaders, {
|
|
1543
|
+
upstreamUrl,
|
|
1544
|
+
model: resolvedModel,
|
|
1545
|
+
reportSelection: (accountId) => {
|
|
1546
|
+
proxyAccountId = accountId;
|
|
1547
|
+
}
|
|
1548
|
+
});
|
|
1266
1549
|
let rawStatus = null;
|
|
1267
1550
|
const { response } = await _chunkKUU2RNG6cjs.executeProviderCall.call(void 0, {
|
|
1268
1551
|
executor,
|
|
@@ -1281,7 +1564,11 @@ async function runPipeline3(chatBody, plan) {
|
|
|
1281
1564
|
},
|
|
1282
1565
|
fetchFn: (url, headers, body) => {
|
|
1283
1566
|
console.log(`[ProviderProxy:chat] -> ${url} model=${resolvedModel} stream=${isStream}`);
|
|
1284
|
-
return
|
|
1567
|
+
return _chunkS6JGLF3Gcjs.fetchUpstream.call(void 0,
|
|
1568
|
+
url,
|
|
1569
|
+
{ method: "POST", headers, body: JSON.stringify(body) },
|
|
1570
|
+
{ providerId: plan.proxyProviderId, accountId: proxyAccountId }
|
|
1571
|
+
).then((r) => {
|
|
1285
1572
|
rawStatus = r.status;
|
|
1286
1573
|
return r;
|
|
1287
1574
|
});
|
|
@@ -1292,8 +1579,8 @@ async function runPipeline3(chatBody, plan) {
|
|
|
1292
1579
|
}
|
|
1293
1580
|
async function runPipelineWithPoolReporting3(chatBody, plan) {
|
|
1294
1581
|
const first = await runPipeline3(chatBody, plan);
|
|
1295
|
-
const outcome = await _optionalChain([plan, 'access',
|
|
1296
|
-
if (_optionalChain([outcome, 'optionalAccess',
|
|
1582
|
+
const outcome = await _optionalChain([plan, 'access', _48 => _48.auth, 'access', _49 => _49.onResult, 'optionalCall', _50 => _50(first.rawStatus)]);
|
|
1583
|
+
if (_optionalChain([outcome, 'optionalAccess', _51 => _51.rebound])) {
|
|
1297
1584
|
console.log(
|
|
1298
1585
|
"[ProviderProxy:chat] pool re-bound key after status",
|
|
1299
1586
|
first.rawStatus,
|
|
@@ -1303,6 +1590,17 @@ async function runPipelineWithPoolReporting3(chatBody, plan) {
|
|
|
1303
1590
|
}
|
|
1304
1591
|
return first;
|
|
1305
1592
|
}
|
|
1593
|
+
async function runPipelineWithSubscriptionRetry2(chatBody, plan) {
|
|
1594
|
+
const first = await runPipeline3(chatBody, plan);
|
|
1595
|
+
if (first.rawStatus !== 401) return first;
|
|
1596
|
+
const refreshed = await _optionalChain([plan, 'access', _52 => _52.auth, 'access', _53 => _53.onUnauthorized, 'optionalCall', _54 => _54()]);
|
|
1597
|
+
if (!refreshed) {
|
|
1598
|
+
console.warn("[ProviderProxy:chat] 401 not recoverable (onUnauthorized returned false)");
|
|
1599
|
+
return first;
|
|
1600
|
+
}
|
|
1601
|
+
console.log("[ProviderProxy:chat] 401 \u2192 token refreshed; retrying once");
|
|
1602
|
+
return runPipeline3(chatBody, plan);
|
|
1603
|
+
}
|
|
1306
1604
|
|
|
1307
1605
|
// src/provider-proxy/usage/recordResponsesUsage.ts
|
|
1308
1606
|
function readResponsesUsage(usage) {
|
|
@@ -1333,26 +1631,27 @@ function recordResponsesNonStreamUsage(recorder, bodyText, attribution) {
|
|
|
1333
1631
|
sessionId: attribution.sessionId,
|
|
1334
1632
|
providerId: attribution.providerId,
|
|
1335
1633
|
model: attribution.model,
|
|
1336
|
-
apiKeyId:
|
|
1634
|
+
apiKeyId: attribution.apiKeyId,
|
|
1635
|
+
auditResponse: attribution.auditResponse,
|
|
1337
1636
|
engineOrigin: "codex-ingress",
|
|
1338
1637
|
usage: tapped,
|
|
1339
1638
|
rawUsage: parsed.usage
|
|
1340
1639
|
});
|
|
1341
|
-
} catch (
|
|
1640
|
+
} catch (e15) {
|
|
1342
1641
|
}
|
|
1343
1642
|
}
|
|
1344
1643
|
|
|
1345
1644
|
// src/provider-proxy/ingress/openaiResponsesIngress.ts
|
|
1346
1645
|
function isOpenAIResponsesRequest(method, url) {
|
|
1347
1646
|
if (method !== "POST" || !url) return false;
|
|
1348
|
-
const path = _nullishCoalesce(_optionalChain([url, 'access',
|
|
1647
|
+
const path = _nullishCoalesce(_optionalChain([url, 'access', _55 => _55.split, 'call', _56 => _56("?"), 'access', _57 => _57[0], 'optionalAccess', _58 => _58.replace, 'call', _59 => _59(/\/+$/, "")]), () => ( ""));
|
|
1349
1648
|
return path.endsWith("/responses");
|
|
1350
1649
|
}
|
|
1351
1650
|
async function handleOpenAIResponsesRequest(res, rawBody, route, deps) {
|
|
1352
1651
|
let responsesBody;
|
|
1353
1652
|
try {
|
|
1354
1653
|
responsesBody = JSON.parse(rawBody);
|
|
1355
|
-
} catch (
|
|
1654
|
+
} catch (e16) {
|
|
1356
1655
|
writeError(res, 400, "Invalid JSON in request body");
|
|
1357
1656
|
return;
|
|
1358
1657
|
}
|
|
@@ -1360,15 +1659,18 @@ async function handleOpenAIResponsesRequest(res, rawBody, route, deps) {
|
|
|
1360
1659
|
const resolvedModel = route.model;
|
|
1361
1660
|
responsesBody.model = resolvedModel;
|
|
1362
1661
|
try {
|
|
1363
|
-
const plan = route.authMode === "subscription" ? await
|
|
1662
|
+
const plan = route.authMode === "subscription" ? await buildSubscriptionPlan3(res, route, deps, resolvedModel, isStream) : await buildByoPlan4(res, route, deps, resolvedModel, isStream);
|
|
1364
1663
|
if (!plan) return;
|
|
1365
|
-
const providerResponse = route.authMode === "subscription" ? await
|
|
1366
|
-
const bodyText = await relayResponse(res, providerResponse.response, isStream);
|
|
1664
|
+
const providerResponse = route.authMode === "subscription" ? await runPipelineWithSubscriptionRetry3(responsesBody, plan) : await runPipelineWithPoolReporting4(responsesBody, plan);
|
|
1665
|
+
const bodyText = await relayResponse(res, providerResponse.response, isStream, route.requestedModel);
|
|
1367
1666
|
if (bodyText && deps.usageRecorder) {
|
|
1368
1667
|
recordResponsesNonStreamUsage(deps.usageRecorder, bodyText, {
|
|
1369
1668
|
sessionId: route.sessionId,
|
|
1370
1669
|
providerId: _nullishCoalesce(route.providerId, () => ( "codex")),
|
|
1371
|
-
model: resolvedModel
|
|
1670
|
+
model: resolvedModel,
|
|
1671
|
+
apiKeyId: _nullishCoalesce(route.apiKeyId, () => ( null)),
|
|
1672
|
+
// request-audit-log: correlate this request's tokens/cost to its audit record.
|
|
1673
|
+
auditResponse: res
|
|
1372
1674
|
});
|
|
1373
1675
|
}
|
|
1374
1676
|
} catch (err) {
|
|
@@ -1393,7 +1695,7 @@ async function buildByoPlan4(res, route, deps, resolvedModel, isStream) {
|
|
|
1393
1695
|
writeError(res, 502, "API key not configured");
|
|
1394
1696
|
return null;
|
|
1395
1697
|
}
|
|
1396
|
-
const auth = _nullishCoalesce(route.auth, () => ( new (0,
|
|
1698
|
+
const auth = _nullishCoalesce(route.auth, () => ( new (0, _chunkQ27JY5RGcjs.LlmConfigProviderAuth)({
|
|
1397
1699
|
provider,
|
|
1398
1700
|
apiKey,
|
|
1399
1701
|
apiKeyPool: _nullishCoalesce(deps.apiKeyPool, () => ( null)),
|
|
@@ -1415,21 +1717,22 @@ async function buildByoPlan4(res, route, deps, resolvedModel, isStream) {
|
|
|
1415
1717
|
resolvedModel,
|
|
1416
1718
|
isStream,
|
|
1417
1719
|
resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : byoUrl,
|
|
1418
|
-
upstreamUrl: byoUrl
|
|
1720
|
+
upstreamUrl: byoUrl,
|
|
1721
|
+
proxyProviderId: "byo"
|
|
1419
1722
|
};
|
|
1420
1723
|
}
|
|
1421
|
-
async function
|
|
1724
|
+
async function buildSubscriptionPlan3(res, route, deps, resolvedModel, isStream) {
|
|
1422
1725
|
const profile = route.subscriptionProfile;
|
|
1423
1726
|
if (!profile) {
|
|
1424
1727
|
writeError(res, 502, "Subscription mode requires a codex subscription profile");
|
|
1425
1728
|
return null;
|
|
1426
1729
|
}
|
|
1427
|
-
const upstreamUrl = _optionalChain([profile, 'access',
|
|
1730
|
+
const upstreamUrl = _optionalChain([profile, 'access', _60 => _60.resolveUpstreamUrl, 'optionalCall', _61 => _61(resolvedModel)]);
|
|
1428
1731
|
if (!upstreamUrl) {
|
|
1429
1732
|
writeError(res, 502, "Subscription profile is missing resolveUpstreamUrl");
|
|
1430
1733
|
return null;
|
|
1431
1734
|
}
|
|
1432
|
-
const auth = _nullishCoalesce(route.auth, () => ( new (0,
|
|
1735
|
+
const auth = _nullishCoalesce(route.auth, () => ( new (0, _chunkY6OM7EV7cjs.SubscriptionAuthSource)(profile)));
|
|
1433
1736
|
const chain = _chunkNYUNU75Acjs.resolveSubscriptionChain.call(void 0,
|
|
1434
1737
|
profile,
|
|
1435
1738
|
deps.llmConfig.getTransformerService(),
|
|
@@ -1459,7 +1762,8 @@ async function buildSubscriptionPlan2(res, route, deps, resolvedModel, isStream)
|
|
|
1459
1762
|
// stream vs non-stream colon-method URL in `config.url`); fall back to the
|
|
1460
1763
|
// profile's `resolveUpstreamUrl` for non-Code-Assist subscriptions.
|
|
1461
1764
|
resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : typeof config.url === "string" ? config.url : upstreamUrl,
|
|
1462
|
-
upstreamUrl
|
|
1765
|
+
upstreamUrl,
|
|
1766
|
+
proxyProviderId: profile.authStrategy.providerId
|
|
1463
1767
|
};
|
|
1464
1768
|
}
|
|
1465
1769
|
async function resolveGeminiCodeAssistProject(profile) {
|
|
@@ -1476,8 +1780,15 @@ async function runPipeline4(responsesBody, plan) {
|
|
|
1476
1780
|
const executor = getSharedExecutor();
|
|
1477
1781
|
const endpointTransformer = getResponsesEndpointTransformer();
|
|
1478
1782
|
const { auth, chain, transformerProvider, resolvedModel, isStream, resolveUrl, upstreamUrl } = plan;
|
|
1783
|
+
let proxyAccountId;
|
|
1479
1784
|
const authHeaders = {};
|
|
1480
|
-
await auth.applyHeaders(authHeaders, {
|
|
1785
|
+
await auth.applyHeaders(authHeaders, {
|
|
1786
|
+
upstreamUrl,
|
|
1787
|
+
model: resolvedModel,
|
|
1788
|
+
reportSelection: (accountId) => {
|
|
1789
|
+
proxyAccountId = accountId;
|
|
1790
|
+
}
|
|
1791
|
+
});
|
|
1481
1792
|
let rawStatus = null;
|
|
1482
1793
|
const { response } = await _chunkKUU2RNG6cjs.executeProviderCall.call(void 0, {
|
|
1483
1794
|
executor,
|
|
@@ -1497,7 +1808,11 @@ async function runPipeline4(responsesBody, plan) {
|
|
|
1497
1808
|
},
|
|
1498
1809
|
fetchFn: (url, headers, body) => {
|
|
1499
1810
|
console.log(`[ProviderProxy:responses] -> ${url} model=${resolvedModel} stream=${isStream}`);
|
|
1500
|
-
return
|
|
1811
|
+
return _chunkS6JGLF3Gcjs.fetchUpstream.call(void 0,
|
|
1812
|
+
url,
|
|
1813
|
+
{ method: "POST", headers, body: JSON.stringify(body) },
|
|
1814
|
+
{ providerId: plan.proxyProviderId, accountId: proxyAccountId }
|
|
1815
|
+
).then((r) => {
|
|
1501
1816
|
rawStatus = r.status;
|
|
1502
1817
|
return r;
|
|
1503
1818
|
});
|
|
@@ -1508,8 +1823,8 @@ async function runPipeline4(responsesBody, plan) {
|
|
|
1508
1823
|
}
|
|
1509
1824
|
async function runPipelineWithPoolReporting4(responsesBody, plan) {
|
|
1510
1825
|
const first = await runPipeline4(responsesBody, plan);
|
|
1511
|
-
const outcome = await _optionalChain([plan, 'access',
|
|
1512
|
-
if (_optionalChain([outcome, 'optionalAccess',
|
|
1826
|
+
const outcome = await _optionalChain([plan, 'access', _62 => _62.auth, 'access', _63 => _63.onResult, 'optionalCall', _64 => _64(first.rawStatus)]);
|
|
1827
|
+
if (_optionalChain([outcome, 'optionalAccess', _65 => _65.rebound])) {
|
|
1513
1828
|
console.log(
|
|
1514
1829
|
"[ProviderProxy:responses] pool re-bound key after status",
|
|
1515
1830
|
first.rawStatus,
|
|
@@ -1519,10 +1834,10 @@ async function runPipelineWithPoolReporting4(responsesBody, plan) {
|
|
|
1519
1834
|
}
|
|
1520
1835
|
return first;
|
|
1521
1836
|
}
|
|
1522
|
-
async function
|
|
1837
|
+
async function runPipelineWithSubscriptionRetry3(responsesBody, plan) {
|
|
1523
1838
|
const first = await runPipeline4(responsesBody, plan);
|
|
1524
1839
|
if (first.rawStatus !== 401) return first;
|
|
1525
|
-
const refreshed = await _optionalChain([plan, 'access',
|
|
1840
|
+
const refreshed = await _optionalChain([plan, 'access', _66 => _66.auth, 'access', _67 => _67.onUnauthorized, 'optionalCall', _68 => _68()]);
|
|
1526
1841
|
if (!refreshed) {
|
|
1527
1842
|
console.warn("[ProviderProxy:responses] 401 not recoverable (onUnauthorized returned false)");
|
|
1528
1843
|
return first;
|
|
@@ -1544,7 +1859,7 @@ function resolveRouteToken(req) {
|
|
|
1544
1859
|
if (fromAuth) return fromAuth;
|
|
1545
1860
|
const googKey = req.headers["x-goog-api-key"];
|
|
1546
1861
|
const value = Array.isArray(googKey) ? googKey[0] : googKey;
|
|
1547
|
-
return _optionalChain([value, 'optionalAccess',
|
|
1862
|
+
return _optionalChain([value, 'optionalAccess', _69 => _69.trim, 'call', _70 => _70()]) || void 0;
|
|
1548
1863
|
}
|
|
1549
1864
|
async function routeRequest(req, res, routes, deps) {
|
|
1550
1865
|
const token = resolveRouteToken(req);
|
|
@@ -1696,12 +2011,644 @@ function __resetProviderProxyForTests() {
|
|
|
1696
2011
|
// src/outbound-api/OutboundApiServer.ts
|
|
1697
2012
|
|
|
1698
2013
|
var _os = require('os');
|
|
2014
|
+
var _healthloggingtypes = require('@omnicross/contracts/health-logging-types');
|
|
2015
|
+
|
|
2016
|
+
// src/outbound-api/keyedMutex.ts
|
|
2017
|
+
var KeyedMutex = (_class3 = class {constructor() { _class3.prototype.__init4.call(this); }
|
|
2018
|
+
/** key → the tail of the pending-op chain (resolves when the last op frees). */
|
|
2019
|
+
__init4() {this.tails = /* @__PURE__ */ new Map()}
|
|
2020
|
+
/**
|
|
2021
|
+
* Run `fn` exclusively for `key`: it starts only after every previously
|
|
2022
|
+
* enqueued op for the SAME key has settled, and the next waiter starts only
|
|
2023
|
+
* after `fn` settles. Returns `fn`'s result (or rejection). Never lets one op's
|
|
2024
|
+
* failure wedge the queue (waiters proceed regardless).
|
|
2025
|
+
*/
|
|
2026
|
+
async runExclusive(key, fn) {
|
|
2027
|
+
const prev = _nullishCoalesce(this.tails.get(key), () => ( Promise.resolve()));
|
|
2028
|
+
let release2;
|
|
2029
|
+
const gate = new Promise((resolve) => {
|
|
2030
|
+
release2 = resolve;
|
|
2031
|
+
});
|
|
2032
|
+
const chain = prev.then(() => gate);
|
|
2033
|
+
this.tails.set(key, chain);
|
|
2034
|
+
await prev.catch(() => void 0);
|
|
2035
|
+
try {
|
|
2036
|
+
return await fn();
|
|
2037
|
+
} finally {
|
|
2038
|
+
release2();
|
|
2039
|
+
if (this.tails.get(key) === chain) this.tails.delete(key);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
}, _class3);
|
|
1699
2043
|
|
|
1700
2044
|
// src/outbound-api/outboundApiRouter.ts
|
|
1701
2045
|
var _stream = require('stream');
|
|
1702
2046
|
|
|
2047
|
+
// src/pipeline/webhookEmit.ts
|
|
2048
|
+
var sink = null;
|
|
2049
|
+
function emitWebhookEvent(event) {
|
|
2050
|
+
const active = sink;
|
|
2051
|
+
if (!active) return;
|
|
2052
|
+
try {
|
|
2053
|
+
active(event);
|
|
2054
|
+
} catch (e17) {
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
// src/outbound-api/apiServerConfig.ts
|
|
2059
|
+
var _audittypes = require('@omnicross/contracts/audit-types');
|
|
2060
|
+
var _billingtypes = require('@omnicross/contracts/billing-types');
|
|
2061
|
+
|
|
2062
|
+
|
|
2063
|
+
|
|
2064
|
+
var _webhooktypes = require('@omnicross/contracts/webhook-types');
|
|
2065
|
+
var OUTBOUND_API_SERVER_CONFIG_KEY = "outboundApiServer.config";
|
|
2066
|
+
var DEFAULT_USER_MESSAGE_QUEUE = {
|
|
2067
|
+
enabled: false,
|
|
2068
|
+
delayMs: 200,
|
|
2069
|
+
waitTimeoutMs: 6e4
|
|
2070
|
+
};
|
|
2071
|
+
var DEFAULT_CONCURRENCY_QUEUE = {
|
|
2072
|
+
maxQueueSizeFactor: 2,
|
|
2073
|
+
minQueueSize: 4,
|
|
2074
|
+
waitTimeoutMs: 6e4
|
|
2075
|
+
};
|
|
2076
|
+
var DEFAULT_ACCOUNT_HEALTH = {
|
|
2077
|
+
overloadCooldownEnabled: true,
|
|
2078
|
+
overloadCooldownMs: 10 * 6e4
|
|
2079
|
+
};
|
|
2080
|
+
function normalizeAccountHealth(raw) {
|
|
2081
|
+
const ah = _optionalChain([raw, 'optionalAccess', _71 => _71.accountHealth]);
|
|
2082
|
+
return {
|
|
2083
|
+
overloadCooldownEnabled: _optionalChain([ah, 'optionalAccess', _72 => _72.overloadCooldownEnabled]) !== false,
|
|
2084
|
+
overloadCooldownMs: clampNumber(
|
|
2085
|
+
_optionalChain([ah, 'optionalAccess', _73 => _73.overloadCooldownMs]),
|
|
2086
|
+
6e4,
|
|
2087
|
+
36e5,
|
|
2088
|
+
DEFAULT_ACCOUNT_HEALTH.overloadCooldownMs
|
|
2089
|
+
)
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
var DEFAULT_ACCOUNT_PROBE = {
|
|
2093
|
+
enabled: false,
|
|
2094
|
+
intervalMs: 15 * 6e4,
|
|
2095
|
+
onlyMultiAccount: true,
|
|
2096
|
+
timeoutMs: 5e3,
|
|
2097
|
+
historySize: 10,
|
|
2098
|
+
staggerMs: 500
|
|
2099
|
+
};
|
|
2100
|
+
function normalizeAccountProbe(raw) {
|
|
2101
|
+
const ap = _optionalChain([raw, 'optionalAccess', _74 => _74.accountProbe]);
|
|
2102
|
+
return {
|
|
2103
|
+
enabled: _optionalChain([ap, 'optionalAccess', _75 => _75.enabled]) === true,
|
|
2104
|
+
intervalMs: clampNumber(_optionalChain([ap, 'optionalAccess', _76 => _76.intervalMs]), 6e4, 864e5, DEFAULT_ACCOUNT_PROBE.intervalMs),
|
|
2105
|
+
onlyMultiAccount: _optionalChain([ap, 'optionalAccess', _77 => _77.onlyMultiAccount]) !== false,
|
|
2106
|
+
timeoutMs: clampNumber(_optionalChain([ap, 'optionalAccess', _78 => _78.timeoutMs]), 1e3, 6e4, DEFAULT_ACCOUNT_PROBE.timeoutMs),
|
|
2107
|
+
historySize: Math.trunc(
|
|
2108
|
+
clampNumber(_optionalChain([ap, 'optionalAccess', _79 => _79.historySize]), 1, 200, DEFAULT_ACCOUNT_PROBE.historySize)
|
|
2109
|
+
),
|
|
2110
|
+
staggerMs: clampNumber(_optionalChain([ap, 'optionalAccess', _80 => _80.staggerMs]), 0, 6e4, DEFAULT_ACCOUNT_PROBE.staggerMs)
|
|
2111
|
+
};
|
|
2112
|
+
}
|
|
2113
|
+
function normalizeAudit(raw) {
|
|
2114
|
+
const a = _optionalChain([raw, 'optionalAccess', _81 => _81.audit]);
|
|
2115
|
+
return {
|
|
2116
|
+
enabled: _optionalChain([a, 'optionalAccess', _82 => _82.enabled]) === true,
|
|
2117
|
+
captureBodies: _optionalChain([a, 'optionalAccess', _83 => _83.captureBodies]) === true,
|
|
2118
|
+
maxBodyBytes: Math.trunc(
|
|
2119
|
+
clampNumber(_optionalChain([a, 'optionalAccess', _84 => _84.maxBodyBytes]), 256, 1048576, _audittypes.DEFAULT_AUDIT_CONFIG.maxBodyBytes)
|
|
2120
|
+
),
|
|
2121
|
+
retentionDays: Math.trunc(
|
|
2122
|
+
clampNumber(_optionalChain([a, 'optionalAccess', _85 => _85.retentionDays]), 1, 365, _audittypes.DEFAULT_AUDIT_CONFIG.retentionDays)
|
|
2123
|
+
),
|
|
2124
|
+
trustForwardedFor: _optionalChain([a, 'optionalAccess', _86 => _86.trustForwardedFor]) === true
|
|
2125
|
+
};
|
|
2126
|
+
}
|
|
2127
|
+
function normalizeBilling(raw) {
|
|
2128
|
+
const b = _optionalChain([raw, 'optionalAccess', _87 => _87.billing]);
|
|
2129
|
+
const config = {
|
|
2130
|
+
enabled: _optionalChain([b, 'optionalAccess', _88 => _88.enabled]) === true,
|
|
2131
|
+
maxRetryAgeMs: Math.trunc(
|
|
2132
|
+
clampNumber(_optionalChain([b, 'optionalAccess', _89 => _89.maxRetryAgeMs]), 6e4, 2592e6, _billingtypes.DEFAULT_BILLING_CONFIG.maxRetryAgeMs)
|
|
2133
|
+
)
|
|
2134
|
+
};
|
|
2135
|
+
if (typeof _optionalChain([b, 'optionalAccess', _90 => _90.endpoint]) === "string" && b.endpoint.trim()) config.endpoint = b.endpoint.trim();
|
|
2136
|
+
if (typeof _optionalChain([b, 'optionalAccess', _91 => _91.secret]) === "string" && b.secret.length > 0) config.secret = b.secret;
|
|
2137
|
+
return config;
|
|
2138
|
+
}
|
|
2139
|
+
var DEFAULT_FINGERPRINT = { enabled: false };
|
|
2140
|
+
function normalizeFingerprint(raw) {
|
|
2141
|
+
const f = _optionalChain([raw, 'optionalAccess', _92 => _92.fingerprint]);
|
|
2142
|
+
const out = { enabled: _optionalChain([f, 'optionalAccess', _93 => _93.enabled]) === true };
|
|
2143
|
+
if (typeof _optionalChain([f, 'optionalAccess', _94 => _94.ua]) === "string" && f.ua.trim().length > 0) out.ua = f.ua.trim();
|
|
2144
|
+
return out;
|
|
2145
|
+
}
|
|
2146
|
+
function normalizeVoucher(raw) {
|
|
2147
|
+
return { enabled: _optionalChain([raw, 'optionalAccess', _95 => _95.voucher, 'optionalAccess', _96 => _96.enabled]) === true };
|
|
2148
|
+
}
|
|
2149
|
+
var PROXY_TYPES = ["http", "https", "socks5"];
|
|
2150
|
+
function normalizeProxyConfig(raw) {
|
|
2151
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
2152
|
+
const r = raw;
|
|
2153
|
+
if (typeof r["url"] === "string" && r["url"].trim().length > 0) {
|
|
2154
|
+
return { url: r["url"].trim() };
|
|
2155
|
+
}
|
|
2156
|
+
const type = r["type"];
|
|
2157
|
+
const host = r["host"];
|
|
2158
|
+
const port = r["port"];
|
|
2159
|
+
if (typeof type === "string" && PROXY_TYPES.includes(type) && typeof host === "string" && host.trim().length > 0 && typeof port === "number" && Number.isFinite(port) && port >= 1 && port <= 65535) {
|
|
2160
|
+
const out = {
|
|
2161
|
+
type,
|
|
2162
|
+
host: host.trim(),
|
|
2163
|
+
port: Math.trunc(port)
|
|
2164
|
+
};
|
|
2165
|
+
if (typeof r["username"] === "string" && r["username"].length > 0) out.username = r["username"];
|
|
2166
|
+
if (typeof r["password"] === "string" && r["password"].length > 0) out.password = r["password"];
|
|
2167
|
+
return out;
|
|
2168
|
+
}
|
|
2169
|
+
return void 0;
|
|
2170
|
+
}
|
|
2171
|
+
function normalizeProxySegment(raw) {
|
|
2172
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
2173
|
+
const r = raw;
|
|
2174
|
+
const out = {};
|
|
2175
|
+
const global = normalizeProxyConfig(r["global"]);
|
|
2176
|
+
if (global) out.global = global;
|
|
2177
|
+
if (r["byProvider"] && typeof r["byProvider"] === "object") {
|
|
2178
|
+
const byProvider = {};
|
|
2179
|
+
for (const [key, value] of Object.entries(r["byProvider"])) {
|
|
2180
|
+
if (!key.trim()) continue;
|
|
2181
|
+
const cfg = normalizeProxyConfig(value);
|
|
2182
|
+
if (cfg) byProvider[key] = cfg;
|
|
2183
|
+
}
|
|
2184
|
+
if (Object.keys(byProvider).length > 0) out.byProvider = byProvider;
|
|
2185
|
+
}
|
|
2186
|
+
return out.global || out.byProvider ? out : void 0;
|
|
2187
|
+
}
|
|
2188
|
+
function normalizeWebhookDestination(raw) {
|
|
2189
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
2190
|
+
const r = raw;
|
|
2191
|
+
const id = typeof r["id"] === "string" ? r["id"].trim() : "";
|
|
2192
|
+
const url = typeof r["url"] === "string" ? r["url"].trim() : "";
|
|
2193
|
+
const type = r["type"];
|
|
2194
|
+
if (!id || !url || typeof type !== "string" || !_webhooktypes.WEBHOOK_DESTINATION_TYPES.includes(type)) {
|
|
2195
|
+
return void 0;
|
|
2196
|
+
}
|
|
2197
|
+
const out = {
|
|
2198
|
+
id,
|
|
2199
|
+
type,
|
|
2200
|
+
url,
|
|
2201
|
+
enabled: r["enabled"] !== false
|
|
2202
|
+
};
|
|
2203
|
+
if (typeof r["secret"] === "string" && r["secret"].length > 0) out.secret = r["secret"];
|
|
2204
|
+
if (Array.isArray(r["events"])) {
|
|
2205
|
+
const events = r["events"].filter(
|
|
2206
|
+
(e) => typeof e === "string" && _webhooktypes.WEBHOOK_EVENT_KINDS.includes(e)
|
|
2207
|
+
);
|
|
2208
|
+
if (events.length > 0) out.events = events;
|
|
2209
|
+
}
|
|
2210
|
+
return out;
|
|
2211
|
+
}
|
|
2212
|
+
function normalizeWebhookSegment(raw) {
|
|
2213
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
2214
|
+
const r = raw;
|
|
2215
|
+
const destinations = [];
|
|
2216
|
+
if (Array.isArray(r["destinations"])) {
|
|
2217
|
+
for (const entry of r["destinations"]) {
|
|
2218
|
+
const d = normalizeWebhookDestination(entry);
|
|
2219
|
+
if (d) destinations.push(d);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
const enabled = r["enabled"] === true;
|
|
2223
|
+
if (!enabled && destinations.length === 0) return void 0;
|
|
2224
|
+
return { enabled, destinations };
|
|
2225
|
+
}
|
|
2226
|
+
function clampNumber(value, min, max, fallback) {
|
|
2227
|
+
const n = typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
2228
|
+
return Math.min(max, Math.max(min, n));
|
|
2229
|
+
}
|
|
2230
|
+
function normalizeQueueSegments(raw) {
|
|
2231
|
+
const umq = _optionalChain([raw, 'optionalAccess', _97 => _97.userMessageQueue]);
|
|
2232
|
+
const cq = _optionalChain([raw, 'optionalAccess', _98 => _98.concurrencyQueue]);
|
|
2233
|
+
return {
|
|
2234
|
+
userMessageQueue: {
|
|
2235
|
+
enabled: _optionalChain([umq, 'optionalAccess', _99 => _99.enabled]) === true,
|
|
2236
|
+
delayMs: clampNumber(_optionalChain([umq, 'optionalAccess', _100 => _100.delayMs]), 0, 1e4, DEFAULT_USER_MESSAGE_QUEUE.delayMs),
|
|
2237
|
+
waitTimeoutMs: clampNumber(
|
|
2238
|
+
_optionalChain([umq, 'optionalAccess', _101 => _101.waitTimeoutMs]),
|
|
2239
|
+
1e3,
|
|
2240
|
+
3e5,
|
|
2241
|
+
DEFAULT_USER_MESSAGE_QUEUE.waitTimeoutMs
|
|
2242
|
+
)
|
|
2243
|
+
},
|
|
2244
|
+
concurrencyQueue: {
|
|
2245
|
+
maxQueueSizeFactor: clampNumber(
|
|
2246
|
+
_optionalChain([cq, 'optionalAccess', _102 => _102.maxQueueSizeFactor]),
|
|
2247
|
+
1,
|
|
2248
|
+
10,
|
|
2249
|
+
DEFAULT_CONCURRENCY_QUEUE.maxQueueSizeFactor
|
|
2250
|
+
),
|
|
2251
|
+
minQueueSize: clampNumber(_optionalChain([cq, 'optionalAccess', _103 => _103.minQueueSize]), 1, 100, DEFAULT_CONCURRENCY_QUEUE.minQueueSize),
|
|
2252
|
+
waitTimeoutMs: clampNumber(
|
|
2253
|
+
_optionalChain([cq, 'optionalAccess', _104 => _104.waitTimeoutMs]),
|
|
2254
|
+
1e3,
|
|
2255
|
+
3e5,
|
|
2256
|
+
DEFAULT_CONCURRENCY_QUEUE.waitTimeoutMs
|
|
2257
|
+
)
|
|
2258
|
+
}
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
var ALL_ENDPOINTS = ["chat", "responses", "messages", "gemini"];
|
|
2262
|
+
function blankModelMap(endpoint) {
|
|
2263
|
+
const modelMap = {};
|
|
2264
|
+
for (const kind of _chunkSVXC36MUcjs.modelKindsForEndpoint.call(void 0, endpoint)) modelMap[kind] = "";
|
|
2265
|
+
return modelMap;
|
|
2266
|
+
}
|
|
2267
|
+
function defaultEndpointConfig(endpoint) {
|
|
2268
|
+
if (_chunkSVXC36MUcjs.isKindMappedEndpoint.call(void 0, endpoint)) {
|
|
2269
|
+
return { endpoint, modelMap: blankModelMap(endpoint), useSubscription: false };
|
|
2270
|
+
}
|
|
2271
|
+
if (endpoint === "chat") {
|
|
2272
|
+
return { endpoint, models: [], useSubscription: false };
|
|
2273
|
+
}
|
|
2274
|
+
return {
|
|
2275
|
+
endpoint,
|
|
2276
|
+
defaultModel: "",
|
|
2277
|
+
backgroundModel: "",
|
|
2278
|
+
useSubscription: false
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
function normalizePrefixTargets(raw) {
|
|
2282
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
2283
|
+
const r = raw;
|
|
2284
|
+
const out = {};
|
|
2285
|
+
for (const key of ["claude", "gpt", "gemini"]) {
|
|
2286
|
+
const v = r[key];
|
|
2287
|
+
if (typeof v === "string" && v.trim() !== "") out[key] = v.trim();
|
|
2288
|
+
}
|
|
2289
|
+
return out.claude || out.gpt || out.gemini ? out : void 0;
|
|
2290
|
+
}
|
|
2291
|
+
function normalizeEndpointConfig(e) {
|
|
2292
|
+
const endpoint = e.endpoint;
|
|
2293
|
+
const useSubscription = e.useSubscription === true;
|
|
2294
|
+
if (_chunkSVXC36MUcjs.isKindMappedEndpoint.call(void 0, endpoint)) {
|
|
2295
|
+
const rawMap = e.modelMap && typeof e.modelMap === "object" ? e.modelMap : {};
|
|
2296
|
+
const modelMap = {};
|
|
2297
|
+
for (const kind of _chunkSVXC36MUcjs.modelKindsForEndpoint.call(void 0, endpoint)) {
|
|
2298
|
+
const v = rawMap[kind];
|
|
2299
|
+
modelMap[kind] = typeof v === "string" ? v : "";
|
|
2300
|
+
}
|
|
2301
|
+
return { endpoint, modelMap, useSubscription };
|
|
2302
|
+
}
|
|
2303
|
+
if (endpoint === "chat") {
|
|
2304
|
+
const models = Array.isArray(e.models) ? e.models.filter((m) => typeof m === "string" && m.trim() !== "") : [];
|
|
2305
|
+
const config2 = { endpoint, models, useSubscription };
|
|
2306
|
+
if (e.dispatchMode === "prefix") {
|
|
2307
|
+
config2.dispatchMode = "prefix";
|
|
2308
|
+
const targets = normalizePrefixTargets(e.prefixTargets);
|
|
2309
|
+
if (targets) config2.prefixTargets = targets;
|
|
2310
|
+
}
|
|
2311
|
+
return config2;
|
|
2312
|
+
}
|
|
2313
|
+
const config = {
|
|
2314
|
+
endpoint,
|
|
2315
|
+
defaultModel: typeof e.defaultModel === "string" ? e.defaultModel : "",
|
|
2316
|
+
backgroundModel: typeof e.backgroundModel === "string" ? e.backgroundModel : "",
|
|
2317
|
+
useSubscription
|
|
2318
|
+
};
|
|
2319
|
+
if (Array.isArray(e.backgroundModelIds)) {
|
|
2320
|
+
config.backgroundModelIds = e.backgroundModelIds;
|
|
2321
|
+
}
|
|
2322
|
+
return config;
|
|
2323
|
+
}
|
|
2324
|
+
function defaultServerConfig() {
|
|
2325
|
+
const queues = normalizeQueueSegments(void 0);
|
|
2326
|
+
return {
|
|
2327
|
+
enabled: false,
|
|
2328
|
+
networkBinding: false,
|
|
2329
|
+
endpoints: ALL_ENDPOINTS.map(defaultEndpointConfig),
|
|
2330
|
+
port: DEFAULT_OUTBOUND_PORT,
|
|
2331
|
+
userMessageQueue: queues.userMessageQueue,
|
|
2332
|
+
concurrencyQueue: queues.concurrencyQueue,
|
|
2333
|
+
accountHealth: normalizeAccountHealth(void 0),
|
|
2334
|
+
accountProbe: normalizeAccountProbe(void 0),
|
|
2335
|
+
audit: normalizeAudit(void 0),
|
|
2336
|
+
billing: normalizeBilling(void 0),
|
|
2337
|
+
fingerprint: normalizeFingerprint(void 0),
|
|
2338
|
+
voucher: normalizeVoucher(void 0)
|
|
2339
|
+
};
|
|
2340
|
+
}
|
|
2341
|
+
function normalizeServerConfig(raw) {
|
|
2342
|
+
const base = defaultServerConfig();
|
|
2343
|
+
if (!raw) return base;
|
|
2344
|
+
const byEndpoint = /* @__PURE__ */ new Map();
|
|
2345
|
+
for (const e of _nullishCoalesce(raw.endpoints, () => ( []))) {
|
|
2346
|
+
if (e && ALL_ENDPOINTS.includes(e.endpoint)) {
|
|
2347
|
+
byEndpoint.set(e.endpoint, normalizeEndpointConfig(e));
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
const queues = normalizeQueueSegments(raw);
|
|
2351
|
+
const config = {
|
|
2352
|
+
enabled: raw.enabled === true,
|
|
2353
|
+
networkBinding: raw.networkBinding === true,
|
|
2354
|
+
endpoints: ALL_ENDPOINTS.map(
|
|
2355
|
+
(ep) => _nullishCoalesce(byEndpoint.get(ep), () => ( defaultEndpointConfig(ep)))
|
|
2356
|
+
),
|
|
2357
|
+
port: _nullishCoalesce(raw.port, () => ( base.port)),
|
|
2358
|
+
userMessageQueue: queues.userMessageQueue,
|
|
2359
|
+
concurrencyQueue: queues.concurrencyQueue,
|
|
2360
|
+
accountHealth: normalizeAccountHealth(raw),
|
|
2361
|
+
accountProbe: normalizeAccountProbe(raw),
|
|
2362
|
+
audit: normalizeAudit(raw),
|
|
2363
|
+
billing: normalizeBilling(raw),
|
|
2364
|
+
fingerprint: normalizeFingerprint(raw),
|
|
2365
|
+
voucher: normalizeVoucher(raw)
|
|
2366
|
+
};
|
|
2367
|
+
const proxy = normalizeProxySegment(raw.proxy);
|
|
2368
|
+
if (proxy) config.proxy = proxy;
|
|
2369
|
+
const webhook = normalizeWebhookSegment(raw.webhook);
|
|
2370
|
+
if (webhook) config.webhook = webhook;
|
|
2371
|
+
return config;
|
|
2372
|
+
}
|
|
2373
|
+
async function loadServerConfig(store) {
|
|
2374
|
+
const raw = await store.get(
|
|
2375
|
+
OUTBOUND_API_SERVER_CONFIG_KEY
|
|
2376
|
+
);
|
|
2377
|
+
return normalizeServerConfig(raw);
|
|
2378
|
+
}
|
|
2379
|
+
async function saveServerConfig(store, config) {
|
|
2380
|
+
await store.set(OUTBOUND_API_SERVER_CONFIG_KEY, config);
|
|
2381
|
+
}
|
|
2382
|
+
function mergeServerConfig(current, patch) {
|
|
2383
|
+
return normalizeServerConfig({
|
|
2384
|
+
enabled: _nullishCoalesce(patch.enabled, () => ( current.enabled)),
|
|
2385
|
+
networkBinding: _nullishCoalesce(patch.networkBinding, () => ( current.networkBinding)),
|
|
2386
|
+
endpoints: _nullishCoalesce(patch.endpoints, () => ( current.endpoints)),
|
|
2387
|
+
port: _nullishCoalesce(patch.port, () => ( current.port)),
|
|
2388
|
+
userMessageQueue: _nullishCoalesce(patch.userMessageQueue, () => ( current.userMessageQueue)),
|
|
2389
|
+
concurrencyQueue: _nullishCoalesce(patch.concurrencyQueue, () => ( current.concurrencyQueue)),
|
|
2390
|
+
accountHealth: _nullishCoalesce(patch.accountHealth, () => ( current.accountHealth)),
|
|
2391
|
+
accountProbe: _nullishCoalesce(patch.accountProbe, () => ( current.accountProbe)),
|
|
2392
|
+
audit: _nullishCoalesce(patch.audit, () => ( current.audit)),
|
|
2393
|
+
billing: _nullishCoalesce(patch.billing, () => ( current.billing)),
|
|
2394
|
+
// Proxy is layer-replaced (not deep-merged): a PUT carrying `proxy` swaps the
|
|
2395
|
+
// whole segment; omitting it keeps the current one. `undefined` on both ⇒ absent.
|
|
2396
|
+
proxy: _nullishCoalesce(patch.proxy, () => ( current.proxy)),
|
|
2397
|
+
// Webhook is layer-replaced too (a PUT carrying `webhook` swaps the whole
|
|
2398
|
+
// segment; omitting it keeps the current one). `undefined` on both ⇒ absent.
|
|
2399
|
+
webhook: _nullishCoalesce(patch.webhook, () => ( current.webhook)),
|
|
2400
|
+
// Fingerprint is always-filled (normalizeFingerprint synthesizes a default);
|
|
2401
|
+
// a PUT carrying it replaces the segment, else the current one is kept.
|
|
2402
|
+
fingerprint: _nullishCoalesce(patch.fingerprint, () => ( current.fingerprint)),
|
|
2403
|
+
// Voucher is always-filled (normalizeVoucher synthesizes a default); a PUT
|
|
2404
|
+
// carrying it replaces the segment, else the current one is kept.
|
|
2405
|
+
voucher: _nullishCoalesce(patch.voucher, () => ( current.voucher))
|
|
2406
|
+
});
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
// src/outbound-api/auditCapture.ts
|
|
2410
|
+
|
|
2411
|
+
|
|
2412
|
+
// src/outbound-api/auditRedact.ts
|
|
2413
|
+
var AUDIT_REDACTED = "***REDACTED***";
|
|
2414
|
+
var RULES = [
|
|
2415
|
+
// `sk-…` family (Anthropic sk-ant-, omnicross sk-omnicross-, OpenAI sk-proj-/sk-).
|
|
2416
|
+
{ re: /\bsk-[A-Za-z0-9_-]{6,}/g, replace: AUDIT_REDACTED },
|
|
2417
|
+
// Google API keys.
|
|
2418
|
+
{ re: /\bAIza[A-Za-z0-9_-]{10,}/g, replace: AUDIT_REDACTED },
|
|
2419
|
+
// `Bearer <token>` (auth headers echoed into a body / SSE error).
|
|
2420
|
+
{ re: /\bBearer\s+[A-Za-z0-9._~+/=-]{6,}/gi, replace: `Bearer ${AUDIT_REDACTED}` },
|
|
2421
|
+
// Inline `x-api-key` / `api-key` / `authorization` label followed by a value.
|
|
2422
|
+
{
|
|
2423
|
+
re: /\b(x-api-key|api[_-]?key|authorization)(["']?\s*[:=]\s*["']?)[A-Za-z0-9._~+/=-]{6,}/gi,
|
|
2424
|
+
replace: `$1$2${AUDIT_REDACTED}`
|
|
2425
|
+
},
|
|
2426
|
+
// `x-goog-api-key` header echoed inline.
|
|
2427
|
+
{
|
|
2428
|
+
re: /\b(x-goog-api-key)(["']?\s*[:=]\s*["']?)[A-Za-z0-9._~+/=-]{6,}/gi,
|
|
2429
|
+
replace: `$1$2${AUDIT_REDACTED}`
|
|
2430
|
+
}
|
|
2431
|
+
];
|
|
2432
|
+
function redactAuditText(text) {
|
|
2433
|
+
if (typeof text !== "string" || text.length === 0) return _nullishCoalesce(text, () => ( ""));
|
|
2434
|
+
let out = text;
|
|
2435
|
+
for (const { re, replace } of RULES) {
|
|
2436
|
+
out = out.replace(re, replace);
|
|
2437
|
+
}
|
|
2438
|
+
return out;
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
// src/outbound-api/auditCapture.ts
|
|
2442
|
+
function isStreamingContentType(res) {
|
|
2443
|
+
const ct = res.getHeader("content-type");
|
|
2444
|
+
const value = Array.isArray(ct) ? ct.join(";") : String(_nullishCoalesce(ct, () => ( "")));
|
|
2445
|
+
return value.toLowerCase().includes("text/event-stream");
|
|
2446
|
+
}
|
|
2447
|
+
function resolveClientIp(req, trustForwardedFor) {
|
|
2448
|
+
if (trustForwardedFor) {
|
|
2449
|
+
const xff = req.headers["x-forwarded-for"];
|
|
2450
|
+
const raw = Array.isArray(xff) ? xff[0] : xff;
|
|
2451
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
2452
|
+
const first = _optionalChain([raw, 'access', _105 => _105.split, 'call', _106 => _106(","), 'access', _107 => _107[0], 'optionalAccess', _108 => _108.trim, 'call', _109 => _109()]);
|
|
2453
|
+
if (first) return first;
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
return _nullishCoalesce(_optionalChain([req, 'access', _110 => _110.socket, 'optionalAccess', _111 => _111.remoteAddress]), () => ( void 0));
|
|
2457
|
+
}
|
|
2458
|
+
function truncateToBytes(text, maxBytes) {
|
|
2459
|
+
const buf = Buffer.from(text, "utf8");
|
|
2460
|
+
if (buf.length <= maxBytes) return text;
|
|
2461
|
+
return buf.subarray(0, maxBytes).toString("utf8");
|
|
2462
|
+
}
|
|
2463
|
+
function beginAuditCapture(req, res, now) {
|
|
2464
|
+
const config = _chunkAEOZIDEBcjs.getAuditCaptureConfig.call(void 0, );
|
|
2465
|
+
if (!config) return null;
|
|
2466
|
+
let rawRequestBody;
|
|
2467
|
+
const responseChunks = [];
|
|
2468
|
+
let responseBytes = 0;
|
|
2469
|
+
let streamDetected = false;
|
|
2470
|
+
let finished = false;
|
|
2471
|
+
const ctx = {
|
|
2472
|
+
setRequestBody(raw) {
|
|
2473
|
+
if (config.captureBodies) rawRequestBody = raw;
|
|
2474
|
+
}
|
|
2475
|
+
};
|
|
2476
|
+
if (config.captureBodies) {
|
|
2477
|
+
installResponseCapture(res, config.maxBodyBytes, {
|
|
2478
|
+
push(chunk) {
|
|
2479
|
+
responseBytes += chunk.length;
|
|
2480
|
+
responseChunks.push(chunk);
|
|
2481
|
+
},
|
|
2482
|
+
get bytes() {
|
|
2483
|
+
return responseBytes;
|
|
2484
|
+
},
|
|
2485
|
+
markStream() {
|
|
2486
|
+
streamDetected = true;
|
|
2487
|
+
},
|
|
2488
|
+
get isStream() {
|
|
2489
|
+
return streamDetected;
|
|
2490
|
+
}
|
|
2491
|
+
});
|
|
2492
|
+
}
|
|
2493
|
+
const finalize = () => {
|
|
2494
|
+
if (finished) return;
|
|
2495
|
+
finished = true;
|
|
2496
|
+
try {
|
|
2497
|
+
const record = {
|
|
2498
|
+
id: _crypto.randomUUID.call(void 0, ),
|
|
2499
|
+
ts: now,
|
|
2500
|
+
method: _nullishCoalesce(req.method, () => ( "")),
|
|
2501
|
+
path: _nullishCoalesce((_nullishCoalesce(req.url, () => ( ""))).split("?")[0], () => ( "")),
|
|
2502
|
+
status: res.statusCode,
|
|
2503
|
+
latencyMs: Math.max(0, Date.now() - now)
|
|
2504
|
+
};
|
|
2505
|
+
if (ctx.keyId != null) record.keyId = ctx.keyId;
|
|
2506
|
+
const ip = resolveClientIp(req, config.trustForwardedFor);
|
|
2507
|
+
if (ip) record.ip = ip;
|
|
2508
|
+
const ua = req.headers["user-agent"];
|
|
2509
|
+
if (typeof ua === "string" && ua) record.ua = ua;
|
|
2510
|
+
if (ctx.model) record.model = ctx.model;
|
|
2511
|
+
if (ctx.provider) record.provider = ctx.provider;
|
|
2512
|
+
const usage = _chunk3QOSDDNIcjs.readAuditUsage.call(void 0, res);
|
|
2513
|
+
if (usage) {
|
|
2514
|
+
if (typeof usage.inputTokens === "number") record.inputTokens = usage.inputTokens;
|
|
2515
|
+
if (typeof usage.outputTokens === "number") record.outputTokens = usage.outputTokens;
|
|
2516
|
+
if (typeof usage.costUsd === "number") record.costUsd = usage.costUsd;
|
|
2517
|
+
if (!record.model && usage.model) record.model = usage.model;
|
|
2518
|
+
if (!record.provider && usage.provider) record.provider = usage.provider;
|
|
2519
|
+
}
|
|
2520
|
+
if (ctx.error) record.error = redactAuditText(ctx.error);
|
|
2521
|
+
if (config.captureBodies) {
|
|
2522
|
+
if (rawRequestBody != null && rawRequestBody.length > 0) {
|
|
2523
|
+
record.requestBody = redactAuditText(truncateToBytes(rawRequestBody, config.maxBodyBytes));
|
|
2524
|
+
}
|
|
2525
|
+
if (!streamDetected && responseChunks.length > 0) {
|
|
2526
|
+
const body = Buffer.concat(responseChunks).toString("utf8");
|
|
2527
|
+
record.responseBody = redactAuditText(body);
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
_chunkAEOZIDEBcjs.recordAudit.call(void 0, record);
|
|
2531
|
+
} catch (e18) {
|
|
2532
|
+
}
|
|
2533
|
+
};
|
|
2534
|
+
res.once("close", finalize);
|
|
2535
|
+
return ctx;
|
|
2536
|
+
}
|
|
2537
|
+
function installResponseCapture(res, maxBodyBytes, sink2) {
|
|
2538
|
+
let contentTypeChecked = false;
|
|
2539
|
+
const capture = (chunk) => {
|
|
2540
|
+
if (sink2.isStream) return;
|
|
2541
|
+
if (!contentTypeChecked) {
|
|
2542
|
+
contentTypeChecked = true;
|
|
2543
|
+
if (isStreamingContentType(res)) {
|
|
2544
|
+
sink2.markStream();
|
|
2545
|
+
return;
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
if (sink2.bytes >= maxBodyBytes) return;
|
|
2549
|
+
let buf = null;
|
|
2550
|
+
if (Buffer.isBuffer(chunk)) buf = chunk;
|
|
2551
|
+
else if (typeof chunk === "string") buf = Buffer.from(chunk, "utf8");
|
|
2552
|
+
if (!buf || buf.length === 0) return;
|
|
2553
|
+
const remaining = maxBodyBytes - sink2.bytes;
|
|
2554
|
+
sink2.push(buf.length > remaining ? buf.subarray(0, remaining) : buf);
|
|
2555
|
+
};
|
|
2556
|
+
const originalWrite = res.write.bind(res);
|
|
2557
|
+
const originalEnd = res.end.bind(res);
|
|
2558
|
+
res.write = ((chunk, ...args) => {
|
|
2559
|
+
if (chunk != null) capture(chunk);
|
|
2560
|
+
return originalWrite(chunk, ...args);
|
|
2561
|
+
});
|
|
2562
|
+
res.end = ((chunk, ...args) => {
|
|
2563
|
+
if (chunk != null && typeof chunk !== "function") capture(chunk);
|
|
2564
|
+
return originalEnd(chunk, ...args);
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
// src/outbound-api/billingCapture.ts
|
|
2569
|
+
|
|
2570
|
+
var defaultDefer = (fn) => {
|
|
2571
|
+
setTimeout(fn, 0);
|
|
2572
|
+
};
|
|
2573
|
+
function beginBillingCapture(_req, res, now, defer = defaultDefer) {
|
|
2574
|
+
if (!_chunkL74VDN4Acjs.getBillingCaptureConfig.call(void 0, )) return null;
|
|
2575
|
+
const ctx = {};
|
|
2576
|
+
let finished = false;
|
|
2577
|
+
const finalize = () => {
|
|
2578
|
+
if (finished) return;
|
|
2579
|
+
finished = true;
|
|
2580
|
+
const status = res.statusCode;
|
|
2581
|
+
defer(() => {
|
|
2582
|
+
try {
|
|
2583
|
+
const usage = _chunk3QOSDDNIcjs.readAuditUsage.call(void 0, res);
|
|
2584
|
+
if (!usage) return;
|
|
2585
|
+
const event = {
|
|
2586
|
+
id: _crypto.randomUUID.call(void 0, ),
|
|
2587
|
+
ts: now,
|
|
2588
|
+
model: _nullishCoalesce(_nullishCoalesce(ctx.model, () => ( usage.model)), () => ( "")),
|
|
2589
|
+
authMode: _nullishCoalesce(ctx.authMode, () => ( "byo")),
|
|
2590
|
+
inputTokens: _nullishCoalesce(usage.inputTokens, () => ( 0)),
|
|
2591
|
+
outputTokens: _nullishCoalesce(usage.outputTokens, () => ( 0)),
|
|
2592
|
+
costUsd: _nullishCoalesce(usage.costUsd, () => ( 0)),
|
|
2593
|
+
status
|
|
2594
|
+
};
|
|
2595
|
+
if (ctx.keyId != null) event.keyId = ctx.keyId;
|
|
2596
|
+
const provider = _nullishCoalesce(ctx.provider, () => ( usage.provider));
|
|
2597
|
+
if (provider) event.provider = provider;
|
|
2598
|
+
_chunkL74VDN4Acjs.publishBillingEvent.call(void 0, event);
|
|
2599
|
+
} catch (e19) {
|
|
2600
|
+
}
|
|
2601
|
+
});
|
|
2602
|
+
};
|
|
2603
|
+
res.once("close", finalize);
|
|
2604
|
+
return ctx;
|
|
2605
|
+
}
|
|
2606
|
+
|
|
1703
2607
|
// src/outbound-api/outboundApiKeyAuth.ts
|
|
1704
2608
|
|
|
2609
|
+
|
|
2610
|
+
// src/outbound-api/keyPolicy.ts
|
|
2611
|
+
var DAY_MS = 864e5;
|
|
2612
|
+
function computeKeyExpiry(row, now) {
|
|
2613
|
+
const mode = _nullishCoalesce(row.activationMode, () => ( "fixed"));
|
|
2614
|
+
if (mode === "activation") {
|
|
2615
|
+
if (row.activatedAt == null) return { expired: false, needsActivation: true };
|
|
2616
|
+
if (row.activationDays == null) return { expired: false };
|
|
2617
|
+
const effectiveExpiresAt = row.activatedAt + row.activationDays * DAY_MS;
|
|
2618
|
+
return { expired: now >= effectiveExpiresAt, effectiveExpiresAt };
|
|
2619
|
+
}
|
|
2620
|
+
if (row.expiresAt == null) return { expired: false };
|
|
2621
|
+
return { expired: now >= row.expiresAt, effectiveExpiresAt: row.expiresAt };
|
|
2622
|
+
}
|
|
2623
|
+
function checkKeyQuota(limits, spend) {
|
|
2624
|
+
if (!limits) return { allowed: true };
|
|
2625
|
+
if (limits.dailyUsd != null && spend.dailyUsd >= limits.dailyUsd) {
|
|
2626
|
+
return { allowed: false, scope: "daily", limitUsd: limits.dailyUsd, spentUsd: spend.dailyUsd };
|
|
2627
|
+
}
|
|
2628
|
+
if (limits.weeklyUsd != null && spend.weeklyUsd >= limits.weeklyUsd) {
|
|
2629
|
+
return { allowed: false, scope: "weekly", limitUsd: limits.weeklyUsd, spentUsd: spend.weeklyUsd };
|
|
2630
|
+
}
|
|
2631
|
+
if (limits.totalUsd != null && spend.totalUsd >= limits.totalUsd) {
|
|
2632
|
+
return { allowed: false, scope: "total", limitUsd: limits.totalUsd, spentUsd: spend.totalUsd };
|
|
2633
|
+
}
|
|
2634
|
+
return { allowed: true };
|
|
2635
|
+
}
|
|
2636
|
+
function canonicalModelId(value) {
|
|
2637
|
+
const idx = value.indexOf(",");
|
|
2638
|
+
const bare = idx >= 0 ? value.slice(idx + 1) : value;
|
|
2639
|
+
return bare.trim().toLowerCase();
|
|
2640
|
+
}
|
|
2641
|
+
function checkModelAllowed(restriction, resolvedModelId) {
|
|
2642
|
+
if (!restriction) return { allowed: true };
|
|
2643
|
+
const target = canonicalModelId(resolvedModelId);
|
|
2644
|
+
const listed = restriction.models.some((m) => canonicalModelId(m) === target);
|
|
2645
|
+
if (restriction.mode === "allowlist") {
|
|
2646
|
+
return listed ? { allowed: true } : { allowed: false, model: resolvedModelId };
|
|
2647
|
+
}
|
|
2648
|
+
return listed ? { allowed: false, model: resolvedModelId } : { allowed: true };
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
// src/outbound-api/outboundApiKeyAuth.ts
|
|
1705
2652
|
var SECRET_BYTES = 32;
|
|
1706
2653
|
var PREFIX_LEN = 14;
|
|
1707
2654
|
var KEY_PREFIX = "sk-omnicross-";
|
|
@@ -1749,143 +2696,730 @@ async function createNamedKey(db, name) {
|
|
|
1749
2696
|
plaintextOnce: secret
|
|
1750
2697
|
};
|
|
1751
2698
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
2699
|
+
function extractCostLimits(row) {
|
|
2700
|
+
const limits = {};
|
|
2701
|
+
if (row.dailyCostLimitUsd != null) limits.dailyUsd = row.dailyCostLimitUsd;
|
|
2702
|
+
if (row.totalCostLimitUsd != null) limits.totalUsd = row.totalCostLimitUsd;
|
|
2703
|
+
if (row.weeklyCostLimitUsd != null) limits.weeklyUsd = row.weeklyCostLimitUsd;
|
|
2704
|
+
return limits.dailyUsd != null || limits.totalUsd != null || limits.weeklyUsd != null ? limits : void 0;
|
|
2705
|
+
}
|
|
2706
|
+
function extractRateLimit(row) {
|
|
2707
|
+
if (row.rateLimitMaxRequests == null && row.rateLimitWindowMs == null) return void 0;
|
|
2708
|
+
const override = {};
|
|
2709
|
+
if (row.rateLimitMaxRequests != null) override.maxRequests = row.rateLimitMaxRequests;
|
|
2710
|
+
if (row.rateLimitWindowMs != null) override.windowMs = row.rateLimitWindowMs;
|
|
2711
|
+
return override;
|
|
2712
|
+
}
|
|
2713
|
+
function extractModelRestriction(row) {
|
|
2714
|
+
if (row.enableModelRestriction !== true) return void 0;
|
|
2715
|
+
return {
|
|
2716
|
+
mode: _nullishCoalesce(row.restrictionMode, () => ( "blacklist")),
|
|
2717
|
+
models: _nullishCoalesce(row.restrictedModels, () => ( []))
|
|
2718
|
+
};
|
|
2719
|
+
}
|
|
2720
|
+
function toVerifiedKey(row) {
|
|
2721
|
+
const key = { id: row.id };
|
|
2722
|
+
if (row.maxConcurrency !== void 0 && row.maxConcurrency !== null) {
|
|
2723
|
+
key.maxConcurrency = row.maxConcurrency;
|
|
2724
|
+
}
|
|
2725
|
+
const costLimits = extractCostLimits(row);
|
|
2726
|
+
if (costLimits) key.costLimits = costLimits;
|
|
2727
|
+
const rateLimit = extractRateLimit(row);
|
|
2728
|
+
if (rateLimit) key.rateLimit = rateLimit;
|
|
2729
|
+
const modelRestriction = extractModelRestriction(row);
|
|
2730
|
+
if (modelRestriction) key.modelRestriction = modelRestriction;
|
|
2731
|
+
return key;
|
|
2732
|
+
}
|
|
2733
|
+
async function verifyKey(db, presentedKey, now = Date.now()) {
|
|
2734
|
+
if (!presentedKey) return { status: "invalid" };
|
|
1754
2735
|
const trimmed = presentedKey.trim();
|
|
1755
|
-
if (!trimmed) return
|
|
2736
|
+
if (!trimmed) return { status: "invalid" };
|
|
1756
2737
|
const row = await db.outboundApiKeysGetByHash(hashKey(trimmed));
|
|
1757
|
-
if (!row) return
|
|
1758
|
-
if (!row.enabled || row.revokedAt !== null) return
|
|
2738
|
+
if (!row) return { status: "invalid" };
|
|
2739
|
+
if (!row.enabled || row.revokedAt !== null) return { status: "invalid" };
|
|
2740
|
+
const expiry = computeKeyExpiry(row, now);
|
|
2741
|
+
if (expiry.expired) return { status: "expired" };
|
|
1759
2742
|
void db.outboundApiKeysTouchLastUsed(row.id).catch(() => {
|
|
1760
2743
|
});
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
// src/outbound-api/roleDetection.ts
|
|
1765
|
-
var _canonicalmodels = require('@omnicross/contracts/canonical-models');
|
|
1766
|
-
var BACKGROUND_TIER_TOKENS = /* @__PURE__ */ new Set([
|
|
1767
|
-
"haiku",
|
|
1768
|
-
"mini",
|
|
1769
|
-
"flash",
|
|
1770
|
-
"small",
|
|
1771
|
-
"lite",
|
|
1772
|
-
"nano",
|
|
1773
|
-
"8b"
|
|
1774
|
-
]);
|
|
1775
|
-
function modelTokens(normalizedId) {
|
|
1776
|
-
return normalizedId.split(/[-._:/\s]+/).filter(Boolean);
|
|
1777
|
-
}
|
|
1778
|
-
function isBackgroundTierModel(rawModelId) {
|
|
1779
|
-
if (!rawModelId) return false;
|
|
1780
|
-
const tokens = modelTokens(_canonicalmodels.normalizeModelId.call(void 0, rawModelId));
|
|
1781
|
-
return tokens.some((tok) => BACKGROUND_TIER_TOKENS.has(tok));
|
|
1782
|
-
}
|
|
1783
|
-
function extractRequestedModel(ingressFormat, body) {
|
|
1784
|
-
const model = body["model"];
|
|
1785
|
-
return typeof model === "string" && model ? model : void 0;
|
|
1786
|
-
}
|
|
1787
|
-
function anthropicHasVision(body) {
|
|
1788
|
-
const messages = body["messages"];
|
|
1789
|
-
if (!Array.isArray(messages)) return false;
|
|
1790
|
-
for (const msg of messages) {
|
|
1791
|
-
const content = _optionalChain([msg, 'optionalAccess', _64 => _64.content]);
|
|
1792
|
-
if (!Array.isArray(content)) continue;
|
|
1793
|
-
for (const part of content) {
|
|
1794
|
-
if (_optionalChain([part, 'optionalAccess', _65 => _65.type]) === "image") return true;
|
|
1795
|
-
}
|
|
2744
|
+
if (expiry.needsActivation) {
|
|
2745
|
+
void db.outboundApiKeysMarkActivated(row.id, now).catch(() => {
|
|
2746
|
+
});
|
|
1796
2747
|
}
|
|
1797
|
-
return
|
|
2748
|
+
return { status: "ok", key: toVerifiedKey(row) };
|
|
2749
|
+
}
|
|
2750
|
+
async function verifyPresentedKey(db, presentedKey) {
|
|
2751
|
+
const result = await verifyKey(db, presentedKey);
|
|
2752
|
+
return result.status === "ok" ? result.key : null;
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
// src/outbound-api/quotaWarn.ts
|
|
2756
|
+
var WEBHOOK_QUOTA_WARN_RATIO = 0.8;
|
|
2757
|
+
function computeQuotaWarnings(limits, spend, ratio = WEBHOOK_QUOTA_WARN_RATIO) {
|
|
2758
|
+
const out = [];
|
|
2759
|
+
const consider = (scope, limit, spent, windowStart) => {
|
|
2760
|
+
if (limit == null || limit <= 0) return;
|
|
2761
|
+
if (spent >= limit) return;
|
|
2762
|
+
if (spent >= ratio * limit) out.push({ scope, limitUsd: limit, spentUsd: spent, windowStart });
|
|
2763
|
+
};
|
|
2764
|
+
consider("daily", limits.dailyUsd, spend.dailyUsd, spend.dailyWindowStart);
|
|
2765
|
+
consider("weekly", limits.weeklyUsd, spend.weeklyUsd, spend.weeklyWindowStart);
|
|
2766
|
+
consider("total", limits.totalUsd, spend.totalUsd, 0);
|
|
2767
|
+
return out;
|
|
2768
|
+
}
|
|
2769
|
+
var WARN_GUARD_MAX = 1e4;
|
|
2770
|
+
var warned = /* @__PURE__ */ new Set();
|
|
2771
|
+
function markQuotaWarnedOnce(keyId, scope, windowStart) {
|
|
2772
|
+
const key = `${keyId}\0${scope}\0${windowStart}`;
|
|
2773
|
+
if (warned.has(key)) return false;
|
|
2774
|
+
if (warned.size >= WARN_GUARD_MAX) warned.clear();
|
|
2775
|
+
warned.add(key);
|
|
2776
|
+
return true;
|
|
1798
2777
|
}
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
2778
|
+
|
|
2779
|
+
// src/outbound-api/outboundConcurrencyGate.ts
|
|
2780
|
+
var ConcurrencyQueueFullError = (_class4 = class extends Error {
|
|
2781
|
+
constructor(apiKeyId, maxQueueSize) {
|
|
2782
|
+
super(`concurrency queue full (max ${maxQueueSize}) for key '${apiKeyId}'`);_class4.prototype.__init5.call(this);;
|
|
2783
|
+
this.apiKeyId = apiKeyId;
|
|
2784
|
+
this.maxQueueSize = maxQueueSize;
|
|
2785
|
+
this.name = "ConcurrencyQueueFullError";
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
|
|
2789
|
+
__init5() {this.code = "concurrency_queue_full"}
|
|
2790
|
+
}, _class4);
|
|
2791
|
+
var ConcurrencyWaitTimeoutError = (_class5 = class extends Error {
|
|
2792
|
+
constructor(apiKeyId, waitTimeoutMs) {
|
|
2793
|
+
super(`concurrency wait timed out after ${waitTimeoutMs}ms for key '${apiKeyId}'`);_class5.prototype.__init6.call(this);;
|
|
2794
|
+
this.apiKeyId = apiKeyId;
|
|
2795
|
+
this.waitTimeoutMs = waitTimeoutMs;
|
|
2796
|
+
this.name = "ConcurrencyWaitTimeoutError";
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
|
|
2800
|
+
__init6() {this.code = "concurrency_wait_timeout"}
|
|
2801
|
+
}, _class5);
|
|
2802
|
+
var ConcurrencyWaitCancelledError = (_class6 = class extends Error {
|
|
2803
|
+
constructor(apiKeyId) {
|
|
2804
|
+
super(`concurrency wait cancelled for key '${apiKeyId}'`);_class6.prototype.__init7.call(this);;
|
|
2805
|
+
this.apiKeyId = apiKeyId;
|
|
2806
|
+
this.name = "ConcurrencyWaitCancelledError";
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
__init7() {this.code = "concurrency_wait_cancelled"}
|
|
2810
|
+
}, _class6);
|
|
2811
|
+
function isConcurrencyRejection(err) {
|
|
2812
|
+
return err instanceof ConcurrencyQueueFullError || err instanceof ConcurrencyWaitTimeoutError || err instanceof ConcurrencyWaitCancelledError;
|
|
2813
|
+
}
|
|
2814
|
+
var OutboundConcurrencyGate = (_class7 = class {constructor() { _class7.prototype.__init8.call(this); }
|
|
2815
|
+
__init8() {this.states = /* @__PURE__ */ new Map()}
|
|
2816
|
+
ensure(apiKeyId) {
|
|
2817
|
+
let state = this.states.get(apiKeyId);
|
|
2818
|
+
if (!state) {
|
|
2819
|
+
state = { active: 0, waiters: [] };
|
|
2820
|
+
this.states.set(apiKeyId, state);
|
|
1807
2821
|
}
|
|
2822
|
+
return state;
|
|
1808
2823
|
}
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
2824
|
+
/** Delete a key's state once it is fully idle (no active, no waiters). */
|
|
2825
|
+
gc(apiKeyId) {
|
|
2826
|
+
const state = this.states.get(apiKeyId);
|
|
2827
|
+
if (state && state.active === 0 && state.waiters.length === 0) {
|
|
2828
|
+
this.states.delete(apiKeyId);
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
/**
|
|
2832
|
+
* Acquire a concurrency slot for `apiKeyId` under `limit`. Grants immediately
|
|
2833
|
+
* when `active < limit`; else enqueues FIFO while under the per-key queue cap;
|
|
2834
|
+
* else rejects queue-full. `limit <= 0` = unlimited (always granted).
|
|
2835
|
+
*/
|
|
2836
|
+
acquire(apiKeyId, limit, options) {
|
|
2837
|
+
const state = this.ensure(apiKeyId);
|
|
2838
|
+
const cap = limit > 0 ? limit : Infinity;
|
|
2839
|
+
if (state.active < cap) {
|
|
2840
|
+
state.active += 1;
|
|
2841
|
+
const slot = this.makeSlot(apiKeyId);
|
|
2842
|
+
return { granted: Promise.resolve(slot), cancel: slot.cancel };
|
|
2843
|
+
}
|
|
2844
|
+
const maxQueueSize = Math.max(
|
|
2845
|
+
Number.isFinite(cap) ? cap * options.maxQueueSizeFactor : Infinity,
|
|
2846
|
+
options.minQueueSize
|
|
2847
|
+
);
|
|
2848
|
+
if (state.waiters.length >= maxQueueSize) {
|
|
2849
|
+
const err = new ConcurrencyQueueFullError(apiKeyId, maxQueueSize);
|
|
2850
|
+
return { granted: Promise.reject(err), cancel: () => {
|
|
2851
|
+
} };
|
|
2852
|
+
}
|
|
2853
|
+
let settled = false;
|
|
2854
|
+
let granted = false;
|
|
2855
|
+
let resolveGrant;
|
|
2856
|
+
let rejectGrant;
|
|
2857
|
+
const grantedPromise = new Promise((resolve, reject) => {
|
|
2858
|
+
resolveGrant = resolve;
|
|
2859
|
+
rejectGrant = reject;
|
|
2860
|
+
});
|
|
2861
|
+
const waiter = {
|
|
2862
|
+
cap,
|
|
2863
|
+
settled: false,
|
|
2864
|
+
timeoutHandle: null,
|
|
2865
|
+
grant: () => {
|
|
2866
|
+
if (settled) return;
|
|
2867
|
+
settled = true;
|
|
2868
|
+
granted = true;
|
|
2869
|
+
waiter.settled = true;
|
|
2870
|
+
if (waiter.timeoutHandle) clearTimeout(waiter.timeoutHandle);
|
|
2871
|
+
state.active += 1;
|
|
2872
|
+
resolveGrant(this.makeSlot(apiKeyId));
|
|
2873
|
+
},
|
|
2874
|
+
fail: (err) => {
|
|
2875
|
+
if (settled) return;
|
|
2876
|
+
settled = true;
|
|
2877
|
+
waiter.settled = true;
|
|
2878
|
+
if (waiter.timeoutHandle) clearTimeout(waiter.timeoutHandle);
|
|
2879
|
+
const idx = state.waiters.indexOf(waiter);
|
|
2880
|
+
if (idx >= 0) state.waiters.splice(idx, 1);
|
|
2881
|
+
rejectGrant(err);
|
|
2882
|
+
this.gc(apiKeyId);
|
|
2883
|
+
}
|
|
2884
|
+
};
|
|
2885
|
+
waiter.timeoutHandle = setTimeout(() => {
|
|
2886
|
+
waiter.fail(new ConcurrencyWaitTimeoutError(apiKeyId, options.waitTimeoutMs));
|
|
2887
|
+
}, options.waitTimeoutMs);
|
|
2888
|
+
state.waiters.push(waiter);
|
|
2889
|
+
const cancel = () => {
|
|
2890
|
+
if (settled || granted) return;
|
|
2891
|
+
waiter.fail(new ConcurrencyWaitCancelledError(apiKeyId));
|
|
2892
|
+
};
|
|
2893
|
+
return { granted: grantedPromise, cancel };
|
|
2894
|
+
}
|
|
2895
|
+
/** Build an idempotent granted slot (`release` decrements; `cancel` is a no-op). */
|
|
2896
|
+
makeSlot(apiKeyId) {
|
|
2897
|
+
let released = false;
|
|
2898
|
+
const release2 = () => {
|
|
2899
|
+
if (released) return;
|
|
2900
|
+
released = true;
|
|
2901
|
+
const state = this.states.get(apiKeyId);
|
|
2902
|
+
if (!state) return;
|
|
2903
|
+
state.active = Math.max(0, state.active - 1);
|
|
2904
|
+
this.dispatchNext(apiKeyId);
|
|
2905
|
+
this.gc(apiKeyId);
|
|
2906
|
+
};
|
|
2907
|
+
return { release: release2, cancel: () => {
|
|
2908
|
+
} };
|
|
2909
|
+
}
|
|
2910
|
+
/** After a release freed a slot, grant the FIFO head if it fits under its cap. */
|
|
2911
|
+
dispatchNext(apiKeyId) {
|
|
2912
|
+
const state = this.states.get(apiKeyId);
|
|
2913
|
+
if (!state || state.waiters.length === 0) return;
|
|
2914
|
+
const head = state.waiters[0];
|
|
2915
|
+
if (state.active < head.cap) {
|
|
2916
|
+
state.waiters.shift();
|
|
2917
|
+
head.grant();
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2920
|
+
/** Snapshot for observability — only keys with active slots or waiters. */
|
|
2921
|
+
getStatus() {
|
|
2922
|
+
const out = [];
|
|
2923
|
+
for (const [apiKeyId, state] of this.states) {
|
|
2924
|
+
if (state.active > 0 || state.waiters.length > 0) {
|
|
2925
|
+
out.push({ apiKeyId, active: state.active, waiting: state.waiters.length });
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
return out;
|
|
2929
|
+
}
|
|
2930
|
+
/** Drop all state + pending timers (tests / teardown). */
|
|
2931
|
+
reset() {
|
|
2932
|
+
for (const state of this.states.values()) {
|
|
2933
|
+
for (const w of state.waiters) {
|
|
2934
|
+
if (w.timeoutHandle) clearTimeout(w.timeoutHandle);
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
this.states.clear();
|
|
2938
|
+
}
|
|
2939
|
+
}, _class7);
|
|
2940
|
+
|
|
2941
|
+
// src/outbound-api/outboundRateLimiter.ts
|
|
2942
|
+
var DEFAULT_WINDOW_MS = 6e4;
|
|
2943
|
+
var DEFAULT_MAX_REQUESTS = 60;
|
|
2944
|
+
var OutboundRateLimiter = (_class8 = class {
|
|
2945
|
+
|
|
2946
|
+
|
|
2947
|
+
/** apiKeyId → ascending request timestamps within the current window. */
|
|
2948
|
+
__init9() {this.hits = /* @__PURE__ */ new Map()}
|
|
2949
|
+
constructor(options = {}) {;_class8.prototype.__init9.call(this);
|
|
2950
|
+
this.windowMs = _nullishCoalesce(options.windowMs, () => ( DEFAULT_WINDOW_MS));
|
|
2951
|
+
this.maxRequests = _nullishCoalesce(options.maxRequests, () => ( DEFAULT_MAX_REQUESTS));
|
|
2952
|
+
}
|
|
2953
|
+
/**
|
|
2954
|
+
* Record a request for `apiKeyId` and decide whether it is allowed. Prunes
|
|
2955
|
+
* timestamps older than the window first; when allowed, the request's
|
|
2956
|
+
* timestamp is appended.
|
|
2957
|
+
*
|
|
2958
|
+
* `override` (outbound-key-policy) supplies a PER-KEY window/max for this
|
|
2959
|
+
* bucket, superseding the instance defaults for THAT key. Absent ⇒ the
|
|
2960
|
+
* instance default 60/60s (byte-identical to before this change). An effective
|
|
2961
|
+
* `maxRequests` of `0` means UNLIMITED — the request is allowed and NOT
|
|
2962
|
+
* recorded (no bucket growth).
|
|
2963
|
+
*/
|
|
2964
|
+
check(apiKeyId, now = Date.now(), override) {
|
|
2965
|
+
const maxRequests = _nullishCoalesce(_optionalChain([override, 'optionalAccess', _112 => _112.maxRequests]), () => ( this.maxRequests));
|
|
2966
|
+
const windowMs = _nullishCoalesce(_optionalChain([override, 'optionalAccess', _113 => _113.windowMs]), () => ( this.windowMs));
|
|
2967
|
+
if (maxRequests === 0) return { allowed: true, retryAfterSeconds: 0 };
|
|
2968
|
+
const cutoff = now - windowMs;
|
|
2969
|
+
const timestamps = (_nullishCoalesce(this.hits.get(apiKeyId), () => ( []))).filter((ts) => ts > cutoff);
|
|
2970
|
+
if (timestamps.length >= maxRequests) {
|
|
2971
|
+
if (timestamps.length === 0) {
|
|
2972
|
+
this.hits.delete(apiKeyId);
|
|
2973
|
+
return { allowed: false, retryAfterSeconds: Math.max(1, Math.ceil(windowMs / 1e3)) };
|
|
1819
2974
|
}
|
|
2975
|
+
const oldest = timestamps[0];
|
|
2976
|
+
const retryAfterMs = Math.max(0, oldest + windowMs - now);
|
|
2977
|
+
this.hits.set(apiKeyId, timestamps);
|
|
2978
|
+
return {
|
|
2979
|
+
allowed: false,
|
|
2980
|
+
retryAfterSeconds: Math.max(1, Math.ceil(retryAfterMs / 1e3))
|
|
2981
|
+
};
|
|
1820
2982
|
}
|
|
1821
|
-
|
|
2983
|
+
timestamps.push(now);
|
|
2984
|
+
this.hits.set(apiKeyId, timestamps);
|
|
2985
|
+
return { allowed: true, retryAfterSeconds: 0 };
|
|
2986
|
+
}
|
|
2987
|
+
/** Drop all recorded state (tests / teardown). */
|
|
2988
|
+
reset() {
|
|
2989
|
+
this.hits.clear();
|
|
2990
|
+
}
|
|
2991
|
+
}, _class8);
|
|
2992
|
+
|
|
2993
|
+
// src/outbound-api/userMessageDetection.ts
|
|
2994
|
+
function asArray(v) {
|
|
2995
|
+
return Array.isArray(v) ? v : [];
|
|
2996
|
+
}
|
|
2997
|
+
function asObject(v) {
|
|
2998
|
+
return v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
2999
|
+
}
|
|
3000
|
+
function last(arr) {
|
|
3001
|
+
return arr.length > 0 ? arr[arr.length - 1] : void 0;
|
|
3002
|
+
}
|
|
3003
|
+
function isUserMessageAnthropic(body) {
|
|
3004
|
+
const messages = asArray(body["messages"]);
|
|
3005
|
+
const lastMsg = asObject(last(messages));
|
|
3006
|
+
if (!lastMsg || lastMsg["role"] !== "user") return false;
|
|
3007
|
+
const content = lastMsg["content"];
|
|
3008
|
+
if (typeof content === "string") return true;
|
|
3009
|
+
if (Array.isArray(content)) {
|
|
3010
|
+
const hasToolResult = content.some(
|
|
3011
|
+
(block) => _optionalChain([asObject, 'call', _114 => _114(block), 'optionalAccess', _115 => _115["type"]]) === "tool_result"
|
|
3012
|
+
);
|
|
3013
|
+
return !hasToolResult;
|
|
1822
3014
|
}
|
|
1823
3015
|
return false;
|
|
1824
3016
|
}
|
|
1825
|
-
function
|
|
1826
|
-
const
|
|
1827
|
-
if (
|
|
1828
|
-
|
|
1829
|
-
const
|
|
1830
|
-
if (!
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
if (_optionalChain([p, 'optionalAccess', _72 => _72.inline_data]) || _optionalChain([p, 'optionalAccess', _73 => _73.inlineData]) || _optionalChain([p, 'optionalAccess', _74 => _74.file_data]) || _optionalChain([p, 'optionalAccess', _75 => _75.fileData])) return true;
|
|
1834
|
-
}
|
|
3017
|
+
function isUserMessageResponses(body) {
|
|
3018
|
+
const input = body["input"];
|
|
3019
|
+
if (typeof input === "string") return input.length > 0;
|
|
3020
|
+
if (Array.isArray(input)) {
|
|
3021
|
+
const lastItem = asObject(last(input));
|
|
3022
|
+
if (!lastItem) return false;
|
|
3023
|
+
if (lastItem["type"] === "function_call_output") return false;
|
|
3024
|
+
return lastItem["role"] === "user";
|
|
1835
3025
|
}
|
|
1836
3026
|
return false;
|
|
1837
3027
|
}
|
|
1838
|
-
function
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
3028
|
+
function isUserMessageChat(body) {
|
|
3029
|
+
const messages = asArray(body["messages"]);
|
|
3030
|
+
const lastMsg = asObject(last(messages));
|
|
3031
|
+
if (!lastMsg) return false;
|
|
3032
|
+
return lastMsg["role"] === "user";
|
|
3033
|
+
}
|
|
3034
|
+
function isUserMessageGemini(body) {
|
|
3035
|
+
const contents = asArray(body["contents"]);
|
|
3036
|
+
const lastContent = asObject(last(contents));
|
|
3037
|
+
if (!lastContent || lastContent["role"] !== "user") return false;
|
|
3038
|
+
const parts = asArray(lastContent["parts"]);
|
|
3039
|
+
const hasFunctionResponse = parts.some(
|
|
3040
|
+
(part) => _optionalChain([asObject, 'call', _116 => _116(part), 'optionalAccess', _117 => _117["functionResponse"]]) !== void 0
|
|
3041
|
+
);
|
|
3042
|
+
return !hasFunctionResponse;
|
|
3043
|
+
}
|
|
3044
|
+
function isUserMessageRequest(endpoint, parsedBody) {
|
|
3045
|
+
const body = asObject(parsedBody);
|
|
3046
|
+
if (!body) return false;
|
|
3047
|
+
switch (endpoint) {
|
|
3048
|
+
case "messages":
|
|
3049
|
+
return isUserMessageAnthropic(body);
|
|
3050
|
+
case "responses":
|
|
3051
|
+
return isUserMessageResponses(body);
|
|
3052
|
+
case "chat":
|
|
3053
|
+
return isUserMessageChat(body);
|
|
3054
|
+
case "gemini":
|
|
3055
|
+
return isUserMessageGemini(body);
|
|
1848
3056
|
default:
|
|
1849
3057
|
return false;
|
|
1850
3058
|
}
|
|
1851
3059
|
}
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
3060
|
+
|
|
3061
|
+
// src/outbound-api/userMessageSerialQueue.ts
|
|
3062
|
+
var SerialQueueTimeoutError = (_class9 = class extends Error {
|
|
3063
|
+
constructor(providerId, waitTimeoutMs) {
|
|
3064
|
+
super(`user-message serial queue wait timed out after ${waitTimeoutMs}ms for '${providerId}'`);_class9.prototype.__init10.call(this);;
|
|
3065
|
+
this.providerId = providerId;
|
|
3066
|
+
this.waitTimeoutMs = waitTimeoutMs;
|
|
3067
|
+
this.name = "SerialQueueTimeoutError";
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
|
|
3071
|
+
__init10() {this.code = "serial_queue_timeout"}
|
|
3072
|
+
}, _class9);
|
|
3073
|
+
function isSerialQueueTimeout(err) {
|
|
3074
|
+
return err instanceof SerialQueueTimeoutError;
|
|
3075
|
+
}
|
|
3076
|
+
var UserMessageSerialQueue = (_class10 = class {constructor() { _class10.prototype.__init11.call(this); }
|
|
3077
|
+
__init11() {this.states = /* @__PURE__ */ new Map()}
|
|
3078
|
+
ensure(providerId) {
|
|
3079
|
+
let state = this.states.get(providerId);
|
|
3080
|
+
if (!state) {
|
|
3081
|
+
state = { holding: false, lastReleaseTs: null, waiters: [], dispatchHandle: null };
|
|
3082
|
+
this.states.set(providerId, state);
|
|
3083
|
+
}
|
|
3084
|
+
return state;
|
|
3085
|
+
}
|
|
3086
|
+
/**
|
|
3087
|
+
* Acquire the serial slot for `providerId`. Resolves immediately when the slot
|
|
3088
|
+
* is free, no one is queued, and the `delayMs` gap since the last release has
|
|
3089
|
+
* elapsed; otherwise enqueues FIFO and resolves in order (or rejects with a
|
|
3090
|
+
* {@link SerialQueueTimeoutError} after `waitTimeoutMs`).
|
|
3091
|
+
*/
|
|
3092
|
+
acquire(providerId, options) {
|
|
3093
|
+
const { waitTimeoutMs, delayMs } = options;
|
|
3094
|
+
const now = _nullishCoalesce(options.now, () => ( Date.now()));
|
|
3095
|
+
const state = this.ensure(providerId);
|
|
3096
|
+
const spacingElapsed = state.lastReleaseTs === null || now - state.lastReleaseTs >= delayMs;
|
|
3097
|
+
if (!state.holding && state.waiters.length === 0 && spacingElapsed) {
|
|
3098
|
+
state.holding = true;
|
|
3099
|
+
return Promise.resolve(this.makeSlot(providerId));
|
|
1865
3100
|
}
|
|
1866
|
-
|
|
3101
|
+
return new Promise((resolve, reject) => {
|
|
3102
|
+
const waiter = {
|
|
3103
|
+
delayMs,
|
|
3104
|
+
resolve,
|
|
3105
|
+
reject,
|
|
3106
|
+
timeoutHandle: null,
|
|
3107
|
+
done: false
|
|
3108
|
+
};
|
|
3109
|
+
waiter.timeoutHandle = setTimeout(() => {
|
|
3110
|
+
if (waiter.done) return;
|
|
3111
|
+
waiter.done = true;
|
|
3112
|
+
const idx = state.waiters.indexOf(waiter);
|
|
3113
|
+
if (idx >= 0) state.waiters.splice(idx, 1);
|
|
3114
|
+
reject(new SerialQueueTimeoutError(providerId, waitTimeoutMs));
|
|
3115
|
+
}, waitTimeoutMs);
|
|
3116
|
+
state.waiters.push(waiter);
|
|
3117
|
+
this.maybeDispatch(providerId);
|
|
3118
|
+
});
|
|
3119
|
+
}
|
|
3120
|
+
/** Build an idempotent release for a granted slot. */
|
|
3121
|
+
makeSlot(providerId) {
|
|
3122
|
+
let released = false;
|
|
3123
|
+
return {
|
|
3124
|
+
release: () => {
|
|
3125
|
+
if (released) return;
|
|
3126
|
+
released = true;
|
|
3127
|
+
const state = this.states.get(providerId);
|
|
3128
|
+
if (!state) return;
|
|
3129
|
+
state.holding = false;
|
|
3130
|
+
state.lastReleaseTs = Date.now();
|
|
3131
|
+
this.maybeDispatch(providerId);
|
|
3132
|
+
}
|
|
3133
|
+
};
|
|
3134
|
+
}
|
|
3135
|
+
/**
|
|
3136
|
+
* Grant the head waiter when the slot is free and the head's `delayMs` gap has
|
|
3137
|
+
* elapsed; otherwise schedule a dispatch for the residual gap. No-op while the
|
|
3138
|
+
* slot is held (release re-invokes this) or the queue is empty.
|
|
3139
|
+
*/
|
|
3140
|
+
maybeDispatch(providerId) {
|
|
3141
|
+
const state = this.states.get(providerId);
|
|
3142
|
+
if (!state || state.holding || state.waiters.length === 0) return;
|
|
3143
|
+
const head = state.waiters[0];
|
|
3144
|
+
const now = Date.now();
|
|
3145
|
+
const elapsed = state.lastReleaseTs === null ? Infinity : now - state.lastReleaseTs;
|
|
3146
|
+
const residual = Math.max(0, head.delayMs - elapsed);
|
|
3147
|
+
if (residual <= 0) {
|
|
3148
|
+
this.grantHead(providerId);
|
|
3149
|
+
return;
|
|
3150
|
+
}
|
|
3151
|
+
if (state.dispatchHandle !== null) return;
|
|
3152
|
+
state.dispatchHandle = setTimeout(() => {
|
|
3153
|
+
state.dispatchHandle = null;
|
|
3154
|
+
this.grantHead(providerId);
|
|
3155
|
+
}, residual);
|
|
3156
|
+
}
|
|
3157
|
+
/** Hand the slot to the FIFO head (if free and someone is waiting). */
|
|
3158
|
+
grantHead(providerId) {
|
|
3159
|
+
const state = this.states.get(providerId);
|
|
3160
|
+
if (!state || state.holding || state.waiters.length === 0) return;
|
|
3161
|
+
if (state.dispatchHandle !== null) {
|
|
3162
|
+
clearTimeout(state.dispatchHandle);
|
|
3163
|
+
state.dispatchHandle = null;
|
|
3164
|
+
}
|
|
3165
|
+
const head = state.waiters.shift();
|
|
3166
|
+
if (!head) return;
|
|
3167
|
+
if (head.timeoutHandle) clearTimeout(head.timeoutHandle);
|
|
3168
|
+
head.done = true;
|
|
3169
|
+
state.holding = true;
|
|
3170
|
+
head.resolve(this.makeSlot(providerId));
|
|
3171
|
+
}
|
|
3172
|
+
/** Snapshot for observability — only keys holding a slot or with waiters. */
|
|
3173
|
+
getStatus() {
|
|
3174
|
+
const out = [];
|
|
3175
|
+
for (const [providerId, state] of this.states) {
|
|
3176
|
+
if (state.holding || state.waiters.length > 0) {
|
|
3177
|
+
out.push({ providerId, holding: state.holding, waiting: state.waiters.length });
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
return out;
|
|
3181
|
+
}
|
|
3182
|
+
/** Drop all state + pending timers (tests / teardown). */
|
|
3183
|
+
reset() {
|
|
3184
|
+
for (const state of this.states.values()) {
|
|
3185
|
+
if (state.dispatchHandle) clearTimeout(state.dispatchHandle);
|
|
3186
|
+
for (const w of state.waiters) {
|
|
3187
|
+
if (w.timeoutHandle) clearTimeout(w.timeoutHandle);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
this.states.clear();
|
|
3191
|
+
}
|
|
3192
|
+
}, _class10);
|
|
3193
|
+
|
|
3194
|
+
// src/outbound-api/voucher.ts
|
|
3195
|
+
|
|
3196
|
+
var CODE_ENTROPY_CHARS = 32;
|
|
3197
|
+
var CODE_PREFIX = "CC_";
|
|
3198
|
+
var DISPLAY_PREFIX_LEN = 8;
|
|
3199
|
+
var DAY_MS2 = 864e5;
|
|
3200
|
+
function generateVoucherCode() {
|
|
3201
|
+
return CODE_PREFIX + randomBase62(CODE_ENTROPY_CHARS);
|
|
3202
|
+
}
|
|
3203
|
+
function hashVoucherCode(code) {
|
|
3204
|
+
return _crypto.createHash.call(void 0, "sha256").update(code, "utf8").digest("hex");
|
|
3205
|
+
}
|
|
3206
|
+
function voucherCodePrefix(code) {
|
|
3207
|
+
return code.slice(0, DISPLAY_PREFIX_LEN);
|
|
3208
|
+
}
|
|
3209
|
+
function newVoucherId() {
|
|
3210
|
+
return `vch_${Date.now()}_${randomBase62(8)}`;
|
|
3211
|
+
}
|
|
3212
|
+
function computeVoucherGrant(voucher, currentKey, now) {
|
|
3213
|
+
if (voucher.type === "credit") {
|
|
3214
|
+
const current = _nullishCoalesce(_optionalChain([currentKey, 'optionalAccess', _118 => _118.totalCostLimitUsd]), () => ( 0));
|
|
3215
|
+
let granted2 = current + (_nullishCoalesce(voucher.creditUsd, () => ( 0)));
|
|
3216
|
+
if (voucher.maxTotalCostLimitUsd != null) {
|
|
3217
|
+
granted2 = Math.min(granted2, voucher.maxTotalCostLimitUsd);
|
|
3218
|
+
}
|
|
3219
|
+
return { totalCostLimitUsd: granted2 };
|
|
3220
|
+
}
|
|
3221
|
+
const currentExpiry = _nullishCoalesce(_optionalChain([currentKey, 'optionalAccess', _119 => _119.expiresAt]), () => ( now));
|
|
3222
|
+
let granted = currentExpiry + (_nullishCoalesce(voucher.renewalDays, () => ( 0))) * DAY_MS2;
|
|
3223
|
+
if (voucher.maxExpiryDays != null) {
|
|
3224
|
+
granted = Math.min(granted, now + voucher.maxExpiryDays * DAY_MS2);
|
|
3225
|
+
}
|
|
3226
|
+
return { expiresAt: granted };
|
|
3227
|
+
}
|
|
3228
|
+
function toVoucherInfo(v) {
|
|
3229
|
+
const info = {
|
|
3230
|
+
id: v.id,
|
|
3231
|
+
codePrefix: v.codePrefix,
|
|
3232
|
+
type: v.type,
|
|
3233
|
+
status: v.status,
|
|
3234
|
+
createdAt: v.createdAt
|
|
3235
|
+
};
|
|
3236
|
+
if (v.creditUsd != null) info.creditUsd = v.creditUsd;
|
|
3237
|
+
if (v.renewalDays != null) info.renewalDays = v.renewalDays;
|
|
3238
|
+
if (v.maxTotalCostLimitUsd != null) info.maxTotalCostLimitUsd = v.maxTotalCostLimitUsd;
|
|
3239
|
+
if (v.maxExpiryDays != null) info.maxExpiryDays = v.maxExpiryDays;
|
|
3240
|
+
if (v.redeemedAt != null) info.redeemedAt = v.redeemedAt;
|
|
3241
|
+
if (v.redeemedByKeyId != null) info.redeemedByKeyId = v.redeemedByKeyId;
|
|
3242
|
+
if (v.grantApplied != null) info.grantApplied = v.grantApplied;
|
|
3243
|
+
if (v.grantedTotalCostLimitUsd != null) info.grantedTotalCostLimitUsd = v.grantedTotalCostLimitUsd;
|
|
3244
|
+
if (v.grantedExpiresAt != null) info.grantedExpiresAt = v.grantedExpiresAt;
|
|
3245
|
+
if (v.revokedAt != null) info.revokedAt = v.revokedAt;
|
|
3246
|
+
return info;
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3249
|
+
// src/outbound-api/voucherRedeem.ts
|
|
3250
|
+
function isRedeemRequest(method, url) {
|
|
3251
|
+
if (method !== "POST" || !url) return false;
|
|
3252
|
+
const path = _nullishCoalesce(_optionalChain([url, 'access', _120 => _120.split, 'call', _121 => _121("?"), 'access', _122 => _122[0], 'optionalAccess', _123 => _123.replace, 'call', _124 => _124(/\/+$/, "")]), () => ( ""));
|
|
3253
|
+
return path.endsWith("/redeem");
|
|
3254
|
+
}
|
|
3255
|
+
function writeErr(res, status, message, headers = {}) {
|
|
3256
|
+
if (res.headersSent) return;
|
|
3257
|
+
res.writeHead(status, { "Content-Type": "application/json", ...headers });
|
|
3258
|
+
res.end(JSON.stringify({ error: { type: "voucher_error", message } }));
|
|
3259
|
+
}
|
|
3260
|
+
function writeResult(res, result) {
|
|
3261
|
+
if (res.headersSent) return;
|
|
3262
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3263
|
+
res.end(JSON.stringify({ ok: true, ...result }));
|
|
3264
|
+
}
|
|
3265
|
+
function readBody2(req) {
|
|
3266
|
+
return new Promise((resolve, reject) => {
|
|
3267
|
+
const chunks = [];
|
|
3268
|
+
req.on("data", (c) => chunks.push(c));
|
|
3269
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
3270
|
+
req.on("error", reject);
|
|
3271
|
+
});
|
|
3272
|
+
}
|
|
3273
|
+
function grantToResult(type, granted) {
|
|
3274
|
+
const result = { type };
|
|
3275
|
+
if (granted.totalCostLimitUsd != null) result.totalCostLimitUsd = granted.totalCostLimitUsd;
|
|
3276
|
+
if (granted.expiresAt != null) result.expiresAt = granted.expiresAt;
|
|
3277
|
+
return result;
|
|
3278
|
+
}
|
|
3279
|
+
function currentResult(type, keyRow) {
|
|
3280
|
+
const result = { type };
|
|
3281
|
+
if (type === "credit") {
|
|
3282
|
+
if (_optionalChain([keyRow, 'optionalAccess', _125 => _125.totalCostLimitUsd]) != null) result.totalCostLimitUsd = keyRow.totalCostLimitUsd;
|
|
3283
|
+
} else if (_optionalChain([keyRow, 'optionalAccess', _126 => _126.expiresAt]) != null) {
|
|
3284
|
+
result.expiresAt = keyRow.expiresAt;
|
|
3285
|
+
}
|
|
3286
|
+
return result;
|
|
3287
|
+
}
|
|
3288
|
+
function recordedAbsolute(v) {
|
|
3289
|
+
const granted = {};
|
|
3290
|
+
if (v.grantedTotalCostLimitUsd != null) granted.totalCostLimitUsd = v.grantedTotalCostLimitUsd;
|
|
3291
|
+
if (v.grantedExpiresAt != null) granted.expiresAt = v.grantedExpiresAt;
|
|
3292
|
+
return granted;
|
|
3293
|
+
}
|
|
3294
|
+
function policyFrom(granted) {
|
|
3295
|
+
const policy = {};
|
|
3296
|
+
if (granted.totalCostLimitUsd != null) policy.totalCostLimitUsd = granted.totalCostLimitUsd;
|
|
3297
|
+
if (granted.expiresAt != null) policy.expiresAt = granted.expiresAt;
|
|
3298
|
+
return policy;
|
|
3299
|
+
}
|
|
3300
|
+
var NOT_REDEEMABLE = "Invalid or non-redeemable code";
|
|
3301
|
+
var fallbackMutex = null;
|
|
3302
|
+
function getFallbackMutex() {
|
|
3303
|
+
fallbackMutex ??= new KeyedMutex();
|
|
3304
|
+
return fallbackMutex;
|
|
3305
|
+
}
|
|
3306
|
+
async function handleVoucherRedeem(req, res, deps, voucherEnabled, redeemLimiter, verifiedKeyId, presentedKey, now, redeemMutex) {
|
|
3307
|
+
if (!voucherEnabled || !deps.voucherDb) {
|
|
3308
|
+
writeErr(res, 403, "Voucher redemption is not enabled");
|
|
3309
|
+
return;
|
|
3310
|
+
}
|
|
3311
|
+
const voucherDb = deps.voucherDb;
|
|
3312
|
+
const rl = redeemLimiter.check(verifiedKeyId, now);
|
|
3313
|
+
if (!rl.allowed) {
|
|
3314
|
+
writeErr(res, 429, "Too many redeem attempts", { "Retry-After": String(rl.retryAfterSeconds) });
|
|
3315
|
+
return;
|
|
3316
|
+
}
|
|
3317
|
+
const raw = await readBody2(req);
|
|
3318
|
+
let code;
|
|
3319
|
+
try {
|
|
3320
|
+
code = raw ? JSON.parse(raw)["code"] : void 0;
|
|
3321
|
+
} catch (e20) {
|
|
3322
|
+
writeErr(res, 400, "Invalid JSON in request body");
|
|
3323
|
+
return;
|
|
1867
3324
|
}
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
switch (endpoint) {
|
|
1872
|
-
case "chat":
|
|
1873
|
-
return "openai-chat";
|
|
1874
|
-
case "responses":
|
|
1875
|
-
return "openai-responses";
|
|
1876
|
-
case "messages":
|
|
1877
|
-
return "anthropic-messages";
|
|
1878
|
-
case "gemini":
|
|
1879
|
-
return "gemini-generatecontent";
|
|
3325
|
+
if (typeof code !== "string" || !code.trim()) {
|
|
3326
|
+
writeErr(res, 400, "Missing redemption code");
|
|
3327
|
+
return;
|
|
1880
3328
|
}
|
|
3329
|
+
const codeHash = hashVoucherCode(code.trim());
|
|
3330
|
+
const keyHash = hashKey(presentedKey.trim());
|
|
3331
|
+
const applyFirstPass = async (voucher, absolute) => {
|
|
3332
|
+
const ok = await deps.db.outboundApiKeysSetPolicy(verifiedKeyId, policyFrom(absolute));
|
|
3333
|
+
if (!ok) {
|
|
3334
|
+
await voucherDb.voucherRevertRedeem(voucher.id, verifiedKeyId);
|
|
3335
|
+
return { ok: false, status: 409, message: "Could not apply the grant \u2014 please retry" };
|
|
3336
|
+
}
|
|
3337
|
+
await voucherDb.voucherMarkGrantApplied(voucher.id);
|
|
3338
|
+
return { ok: true, result: grantToResult(voucher.type, absolute) };
|
|
3339
|
+
};
|
|
3340
|
+
const reconcileOne = async (voucher) => {
|
|
3341
|
+
const ok = await deps.db.outboundApiKeysSetPolicy(verifiedKeyId, policyFrom(recordedAbsolute(voucher)));
|
|
3342
|
+
if (ok) await voucherDb.voucherMarkGrantApplied(voucher.id);
|
|
3343
|
+
};
|
|
3344
|
+
const reconcileStranded = async () => {
|
|
3345
|
+
const all = await voucherDb.voucherList();
|
|
3346
|
+
const stranded = all.filter((v) => v.status === "redeemed" && v.grantApplied !== true && v.redeemedByKeyId === verifiedKeyId).sort((a, b) => (_nullishCoalesce(a.redeemedAt, () => ( 0))) - (_nullishCoalesce(b.redeemedAt, () => ( 0))));
|
|
3347
|
+
for (const v of stranded) await reconcileOne(v);
|
|
3348
|
+
};
|
|
3349
|
+
const runCritical = async () => {
|
|
3350
|
+
await reconcileStranded();
|
|
3351
|
+
const voucher = await voucherDb.voucherGetByHash(codeHash);
|
|
3352
|
+
if (!voucher || voucher.status === "revoked") {
|
|
3353
|
+
return { ok: false, status: 400, message: NOT_REDEEMABLE };
|
|
3354
|
+
}
|
|
3355
|
+
if (voucher.status === "redeemed") {
|
|
3356
|
+
if (voucher.redeemedByKeyId !== verifiedKeyId) {
|
|
3357
|
+
return { ok: false, status: 400, message: NOT_REDEEMABLE };
|
|
3358
|
+
}
|
|
3359
|
+
if (voucher.grantApplied === true) {
|
|
3360
|
+
const keyRow2 = await deps.db.outboundApiKeysGetByHash(keyHash);
|
|
3361
|
+
return { ok: true, result: currentResult(voucher.type, keyRow2) };
|
|
3362
|
+
}
|
|
3363
|
+
return await applyFirstPass(voucher, recordedAbsolute(voucher));
|
|
3364
|
+
}
|
|
3365
|
+
const keyRow = await deps.db.outboundApiKeysGetByHash(keyHash);
|
|
3366
|
+
const absolute = computeVoucherGrant(voucher, keyRow, now);
|
|
3367
|
+
const won = await voucherDb.voucherRedeemCas(voucher.id, verifiedKeyId, absolute, now);
|
|
3368
|
+
if (!won) {
|
|
3369
|
+
const after = await voucherDb.voucherGetByHash(codeHash);
|
|
3370
|
+
if (_optionalChain([after, 'optionalAccess', _127 => _127.status]) === "redeemed" && after.redeemedByKeyId === verifiedKeyId) {
|
|
3371
|
+
return after.grantApplied === true ? { ok: true, result: currentResult(after.type, await deps.db.outboundApiKeysGetByHash(keyHash)) } : await applyFirstPass(after, recordedAbsolute(after));
|
|
3372
|
+
}
|
|
3373
|
+
return { ok: false, status: 409, message: "Code was already redeemed" };
|
|
3374
|
+
}
|
|
3375
|
+
return await applyFirstPass(voucher, absolute);
|
|
3376
|
+
};
|
|
3377
|
+
const mutex = _nullishCoalesce(redeemMutex, () => ( getFallbackMutex()));
|
|
3378
|
+
const outcome = await mutex.runExclusive(verifiedKeyId, runCritical);
|
|
3379
|
+
if (outcome.ok) writeResult(res, outcome.result);
|
|
3380
|
+
else writeErr(res, outcome.status, outcome.message);
|
|
1881
3381
|
}
|
|
1882
3382
|
|
|
1883
3383
|
// src/outbound-api/outboundApiRouter.ts
|
|
3384
|
+
var fallbackRedeemLimiter = null;
|
|
3385
|
+
function getFallbackRedeemLimiter() {
|
|
3386
|
+
fallbackRedeemLimiter ??= new OutboundRateLimiter({ maxRequests: 10, windowMs: 6e4 });
|
|
3387
|
+
return fallbackRedeemLimiter;
|
|
3388
|
+
}
|
|
1884
3389
|
function writeJsonError(res, status, message, headers = {}) {
|
|
1885
3390
|
if (res.headersSent) return;
|
|
1886
3391
|
res.writeHead(status, { "Content-Type": "application/json", ...headers });
|
|
1887
3392
|
res.end(JSON.stringify({ error: { type: "outbound_api_error", message } }));
|
|
1888
3393
|
}
|
|
3394
|
+
function writeCostLimitError(res, scope, limitUsd, spentUsd) {
|
|
3395
|
+
if (res.headersSent) return;
|
|
3396
|
+
res.writeHead(402, { "Content-Type": "application/json" });
|
|
3397
|
+
res.end(
|
|
3398
|
+
JSON.stringify({
|
|
3399
|
+
error: {
|
|
3400
|
+
type: "outbound_api_error",
|
|
3401
|
+
message: `Cost limit reached for this key (${scope}): $${spentUsd.toFixed(4)} of $${limitUsd.toFixed(4)}`
|
|
3402
|
+
},
|
|
3403
|
+
scope,
|
|
3404
|
+
limitUsd,
|
|
3405
|
+
spentUsd
|
|
3406
|
+
})
|
|
3407
|
+
);
|
|
3408
|
+
}
|
|
3409
|
+
function writeModelNotAllowedError(res, model, mode) {
|
|
3410
|
+
if (res.headersSent) return;
|
|
3411
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
3412
|
+
res.end(
|
|
3413
|
+
JSON.stringify({
|
|
3414
|
+
error: {
|
|
3415
|
+
type: "outbound_api_error",
|
|
3416
|
+
message: `Model '${model}' is not permitted for this key (${mode})`
|
|
3417
|
+
},
|
|
3418
|
+
model,
|
|
3419
|
+
mode
|
|
3420
|
+
})
|
|
3421
|
+
);
|
|
3422
|
+
}
|
|
1889
3423
|
function extractPresentedKey(req) {
|
|
1890
3424
|
const auth = req.headers["authorization"];
|
|
1891
3425
|
if (typeof auth === "string" && auth.trim()) {
|
|
@@ -1894,14 +3428,14 @@ function extractPresentedKey(req) {
|
|
|
1894
3428
|
}
|
|
1895
3429
|
const xApiKey = req.headers["x-api-key"];
|
|
1896
3430
|
const xApi = Array.isArray(xApiKey) ? xApiKey[0] : xApiKey;
|
|
1897
|
-
if (_optionalChain([xApi, 'optionalAccess',
|
|
3431
|
+
if (_optionalChain([xApi, 'optionalAccess', _128 => _128.trim, 'call', _129 => _129()])) return xApi.trim();
|
|
1898
3432
|
const goog = req.headers["x-goog-api-key"];
|
|
1899
3433
|
const g = Array.isArray(goog) ? goog[0] : goog;
|
|
1900
|
-
return _optionalChain([g, 'optionalAccess',
|
|
3434
|
+
return _optionalChain([g, 'optionalAccess', _130 => _130.trim, 'call', _131 => _131()]) || void 0;
|
|
1901
3435
|
}
|
|
1902
3436
|
function selectEndpoint(method, url) {
|
|
1903
3437
|
if (method !== "POST" || !url) return null;
|
|
1904
|
-
const path = _nullishCoalesce(_optionalChain([url, 'access',
|
|
3438
|
+
const path = _nullishCoalesce(_optionalChain([url, 'access', _132 => _132.split, 'call', _133 => _133("?"), 'access', _134 => _134[0], 'optionalAccess', _135 => _135.replace, 'call', _136 => _136(/\/+$/, "")]), () => ( ""));
|
|
1905
3439
|
if (path.endsWith("/chat/completions")) return "chat";
|
|
1906
3440
|
if (path.endsWith("/responses")) return "responses";
|
|
1907
3441
|
if (path.includes("/v1/messages")) return "messages";
|
|
@@ -1917,7 +3451,18 @@ function extractGeminiModelFromUrl(url) {
|
|
|
1917
3451
|
const m = /\/models\/([^/:]+):(?:stream)?[gG]enerateContent$/.exec(path.replace(/\/+$/, ""));
|
|
1918
3452
|
return m ? decodeURIComponent(m[1]) : void 0;
|
|
1919
3453
|
}
|
|
1920
|
-
function
|
|
3454
|
+
function isModelsListRequest(url) {
|
|
3455
|
+
if (!url) return false;
|
|
3456
|
+
const path = _nullishCoalesce(_optionalChain([url, 'access', _137 => _137.split, 'call', _138 => _138("?"), 'access', _139 => _139[0], 'optionalAccess', _140 => _140.replace, 'call', _141 => _141(/\/+$/, "")]), () => ( ""));
|
|
3457
|
+
return path.endsWith("/models");
|
|
3458
|
+
}
|
|
3459
|
+
function writeChatModelsList(res, config) {
|
|
3460
|
+
const chat = config.endpoints.find((e) => e.endpoint === "chat");
|
|
3461
|
+
const data = (_nullishCoalesce(_optionalChain([chat, 'optionalAccess', _142 => _142.models]), () => ( []))).map((ref) => _chunkSVXC36MUcjs.parseModelRef.call(void 0, ref)).filter((p) => p !== null).map((p) => ({ id: p.modelId, object: "model", owned_by: "omnicross" }));
|
|
3462
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
3463
|
+
res.end(JSON.stringify({ object: "list", data }));
|
|
3464
|
+
}
|
|
3465
|
+
function readBody3(req) {
|
|
1921
3466
|
return new Promise((resolve, reject) => {
|
|
1922
3467
|
const chunks = [];
|
|
1923
3468
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
@@ -1930,20 +3475,48 @@ function shimAuthHeader(req, routeToken) {
|
|
|
1930
3475
|
delete req.headers["x-goog-api-key"];
|
|
1931
3476
|
delete req.headers["x-api-key"];
|
|
1932
3477
|
}
|
|
1933
|
-
async function handleOutboundRequest(req, res, deps, config, rateLimiter) {
|
|
3478
|
+
async function handleOutboundRequest(req, res, deps, config, rateLimiter, serialQueue, concurrencyGate, redeemLimiter, redeemMutex) {
|
|
3479
|
+
const now = Date.now();
|
|
3480
|
+
const audit = beginAuditCapture(req, res, now);
|
|
3481
|
+
const billing = beginBillingCapture(req, res, now);
|
|
1934
3482
|
const presented = extractPresentedKey(req);
|
|
1935
|
-
const
|
|
1936
|
-
if (
|
|
1937
|
-
writeJsonError(
|
|
3483
|
+
const verification = await verifyKey(deps.db, presented, now);
|
|
3484
|
+
if (verification.status !== "ok") {
|
|
3485
|
+
writeJsonError(
|
|
3486
|
+
res,
|
|
3487
|
+
401,
|
|
3488
|
+
verification.status === "expired" ? "API key has expired" : "Invalid or missing API key"
|
|
3489
|
+
);
|
|
3490
|
+
return;
|
|
3491
|
+
}
|
|
3492
|
+
const verified = verification.key;
|
|
3493
|
+
if (audit) audit.keyId = verified.id;
|
|
3494
|
+
if (billing) billing.keyId = verified.id;
|
|
3495
|
+
if (isRedeemRequest(req.method, req.url)) {
|
|
3496
|
+
await handleVoucherRedeem(
|
|
3497
|
+
req,
|
|
3498
|
+
res,
|
|
3499
|
+
deps,
|
|
3500
|
+
_optionalChain([config, 'access', _143 => _143.voucher, 'optionalAccess', _144 => _144.enabled]) === true,
|
|
3501
|
+
_nullishCoalesce(redeemLimiter, () => ( getFallbackRedeemLimiter())),
|
|
3502
|
+
verified.id,
|
|
3503
|
+
_nullishCoalesce(presented, () => ( "")),
|
|
3504
|
+
now,
|
|
3505
|
+
redeemMutex
|
|
3506
|
+
);
|
|
1938
3507
|
return;
|
|
1939
3508
|
}
|
|
1940
|
-
const decision = rateLimiter.check(verified.id);
|
|
3509
|
+
const decision = rateLimiter.check(verified.id, now, verified.rateLimit);
|
|
1941
3510
|
if (!decision.allowed) {
|
|
1942
3511
|
writeJsonError(res, 429, "Rate limit exceeded", {
|
|
1943
3512
|
"Retry-After": String(decision.retryAfterSeconds)
|
|
1944
3513
|
});
|
|
1945
3514
|
return;
|
|
1946
3515
|
}
|
|
3516
|
+
if (req.method === "GET" && isModelsListRequest(req.url)) {
|
|
3517
|
+
writeChatModelsList(res, config);
|
|
3518
|
+
return;
|
|
3519
|
+
}
|
|
1947
3520
|
const endpoint = selectEndpoint(req.method, req.url);
|
|
1948
3521
|
if (!endpoint) {
|
|
1949
3522
|
writeJsonError(res, 404, `Unsupported: ${req.method} ${req.url}`);
|
|
@@ -1954,53 +3527,165 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter) {
|
|
|
1954
3527
|
writeJsonError(res, 503, `endpoint '${endpoint}' is not configured`);
|
|
1955
3528
|
return;
|
|
1956
3529
|
}
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
3530
|
+
if (verified.costLimits && deps.keySpendTracker) {
|
|
3531
|
+
const spend = await deps.keySpendTracker.getSpend(verified.id, now);
|
|
3532
|
+
const quota = checkKeyQuota(verified.costLimits, spend);
|
|
3533
|
+
if (!quota.allowed) {
|
|
3534
|
+
emitWebhookEvent({
|
|
3535
|
+
kind: "key.quotaExceeded",
|
|
3536
|
+
at: now,
|
|
3537
|
+
keyId: verified.id,
|
|
3538
|
+
scope: quota.scope,
|
|
3539
|
+
limitUsd: quota.limitUsd,
|
|
3540
|
+
spentUsd: quota.spentUsd
|
|
3541
|
+
});
|
|
3542
|
+
writeCostLimitError(res, quota.scope, quota.limitUsd, quota.spentUsd);
|
|
3543
|
+
return;
|
|
3544
|
+
}
|
|
3545
|
+
for (const warn of computeQuotaWarnings(verified.costLimits, spend)) {
|
|
3546
|
+
if (markQuotaWarnedOnce(verified.id, warn.scope, warn.windowStart)) {
|
|
3547
|
+
emitWebhookEvent({
|
|
3548
|
+
kind: "key.quotaWarning",
|
|
3549
|
+
at: now,
|
|
3550
|
+
keyId: verified.id,
|
|
3551
|
+
scope: warn.scope,
|
|
3552
|
+
limitUsd: warn.limitUsd,
|
|
3553
|
+
spentUsd: warn.spentUsd
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
1969
3557
|
}
|
|
1970
|
-
const
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
3558
|
+
const concurrencyLimit = verified.maxConcurrency;
|
|
3559
|
+
let releaseConcurrency = null;
|
|
3560
|
+
if (typeof concurrencyLimit === "number" && concurrencyLimit > 0) {
|
|
3561
|
+
const cq = _nullishCoalesce(config.concurrencyQueue, () => ( DEFAULT_CONCURRENCY_QUEUE));
|
|
3562
|
+
const acquisition = concurrencyGate.acquire(verified.id, concurrencyLimit, {
|
|
3563
|
+
maxQueueSizeFactor: cq.maxQueueSizeFactor,
|
|
3564
|
+
minQueueSize: cq.minQueueSize,
|
|
3565
|
+
waitTimeoutMs: cq.waitTimeoutMs
|
|
3566
|
+
});
|
|
3567
|
+
const cancelOnClose = () => acquisition.cancel();
|
|
3568
|
+
res.once("close", cancelOnClose);
|
|
3569
|
+
let slot;
|
|
3570
|
+
try {
|
|
3571
|
+
slot = await acquisition.granted;
|
|
3572
|
+
} catch (err) {
|
|
3573
|
+
res.removeListener("close", cancelOnClose);
|
|
3574
|
+
if (isConcurrencyRejection(err)) {
|
|
3575
|
+
writeJsonError(res, 429, "Concurrency limit exceeded", { "Retry-After": "5" });
|
|
3576
|
+
return;
|
|
3577
|
+
}
|
|
3578
|
+
throw err;
|
|
3579
|
+
}
|
|
3580
|
+
res.removeListener("close", cancelOnClose);
|
|
3581
|
+
const release2 = () => slot.release();
|
|
3582
|
+
releaseConcurrency = release2;
|
|
3583
|
+
res.once("close", release2);
|
|
1991
3584
|
}
|
|
1992
|
-
const routeMap = deps.providerProxy.getRouteMap();
|
|
1993
|
-
const token = routeMap.addRoute(resolved.route);
|
|
1994
3585
|
try {
|
|
1995
|
-
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
3586
|
+
const ingressFormat = _chunkSVXC36MUcjs.endpointToIngressFormat.call(void 0, endpoint);
|
|
3587
|
+
const rawBody = await readBody3(req);
|
|
3588
|
+
let parsedBody = {};
|
|
3589
|
+
try {
|
|
3590
|
+
parsedBody = rawBody ? JSON.parse(rawBody) : {};
|
|
3591
|
+
} catch (e21) {
|
|
3592
|
+
writeJsonError(res, 400, "Invalid JSON in request body");
|
|
3593
|
+
return;
|
|
3594
|
+
}
|
|
3595
|
+
if (audit) audit.setRequestBody(rawBody);
|
|
3596
|
+
if (endpoint === "gemini" && typeof parsedBody["model"] !== "string") {
|
|
3597
|
+
const urlModel = extractGeminiModelFromUrl(req.url);
|
|
3598
|
+
if (urlModel) parsedBody["model"] = urlModel;
|
|
3599
|
+
}
|
|
3600
|
+
const sessionId = deps.proxyDeps.apiKeyPool ? `outbound:${verified.id}` : null;
|
|
3601
|
+
const resolved = _chunkSVXC36MUcjs.isKindMappedEndpoint.call(void 0, endpoint) || endpoint === "chat" ? await _chunkSVXC36MUcjs.resolveRoute.call(void 0, {
|
|
3602
|
+
config: endpointConfig,
|
|
3603
|
+
ingressFormat,
|
|
3604
|
+
llmConfig: deps.llmConfig,
|
|
3605
|
+
sessionId,
|
|
3606
|
+
// Capture the ORIGINAL requested id BEFORE any downstream swap; for
|
|
3607
|
+
// kind-mapped endpoints it selects the kind AND is stamped onto
|
|
3608
|
+
// `route.requestedModel`; for chat it is matched against the list.
|
|
3609
|
+
requestedModel: _chunkSVXC36MUcjs.extractRequestedModel.call(void 0, ingressFormat, parsedBody),
|
|
3610
|
+
// Attribution: stamp the verified named-key id onto the route.
|
|
3611
|
+
apiKeyId: verified.id
|
|
3612
|
+
}) : await _chunkSVXC36MUcjs.resolveRoute.call(void 0, {
|
|
3613
|
+
config: endpointConfig,
|
|
3614
|
+
role: _chunkSVXC36MUcjs.detectRequestRole.call(void 0, ingressFormat, parsedBody, {
|
|
3615
|
+
backgroundModelIds: endpointConfig.backgroundModelIds
|
|
3616
|
+
}),
|
|
3617
|
+
ingressFormat,
|
|
3618
|
+
llmConfig: deps.llmConfig,
|
|
3619
|
+
sessionId,
|
|
3620
|
+
// Attribution: stamp the verified named-key id onto the route.
|
|
3621
|
+
apiKeyId: verified.id
|
|
3622
|
+
});
|
|
3623
|
+
if (!resolved.ok) {
|
|
3624
|
+
writeJsonError(res, resolved.error.status, resolved.error.message);
|
|
3625
|
+
return;
|
|
3626
|
+
}
|
|
3627
|
+
if (audit) {
|
|
3628
|
+
audit.model = resolved.route.model;
|
|
3629
|
+
audit.provider = resolved.route.providerId;
|
|
3630
|
+
}
|
|
3631
|
+
if (billing) {
|
|
3632
|
+
billing.model = resolved.route.model;
|
|
3633
|
+
billing.provider = resolved.route.providerId;
|
|
3634
|
+
billing.authMode = resolved.route.authMode;
|
|
3635
|
+
}
|
|
3636
|
+
if (verified.modelRestriction) {
|
|
3637
|
+
const modelDecision = checkModelAllowed(verified.modelRestriction, resolved.route.model);
|
|
3638
|
+
if (!modelDecision.allowed) {
|
|
3639
|
+
writeModelNotAllowedError(res, modelDecision.model, verified.modelRestriction.mode);
|
|
3640
|
+
return;
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
let releaseSerial = null;
|
|
3644
|
+
const providerKey = resolved.route.providerId;
|
|
3645
|
+
if (_optionalChain([config, 'access', _145 => _145.userMessageQueue, 'optionalAccess', _146 => _146.enabled]) && providerKey && isUserMessageRequest(endpoint, parsedBody)) {
|
|
3646
|
+
const umq = config.userMessageQueue;
|
|
3647
|
+
try {
|
|
3648
|
+
const serialSlot = await serialQueue.acquire(providerKey, {
|
|
3649
|
+
waitTimeoutMs: umq.waitTimeoutMs,
|
|
3650
|
+
delayMs: umq.delayMs
|
|
3651
|
+
});
|
|
3652
|
+
releaseSerial = () => serialSlot.release();
|
|
3653
|
+
} catch (err) {
|
|
3654
|
+
if (isSerialQueueTimeout(err)) {
|
|
3655
|
+
writeJsonError(res, 503, "User-message serial queue wait timed out");
|
|
3656
|
+
return;
|
|
3657
|
+
}
|
|
3658
|
+
throw err;
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
if (releaseSerial) {
|
|
3662
|
+
const original = res.writeHead.bind(res);
|
|
3663
|
+
const releaseOnce = releaseSerial;
|
|
3664
|
+
res.writeHead = ((...args) => {
|
|
3665
|
+
res.writeHead = original;
|
|
3666
|
+
releaseOnce();
|
|
3667
|
+
return original(...args);
|
|
3668
|
+
});
|
|
3669
|
+
}
|
|
3670
|
+
const routeMap = deps.providerProxy.getRouteMap();
|
|
3671
|
+
const token = routeMap.addRoute(resolved.route);
|
|
3672
|
+
try {
|
|
3673
|
+
shimAuthHeader(req, token);
|
|
3674
|
+
const replay = makeReplayRequest(req, rawBody);
|
|
3675
|
+
await routeRequest(replay, res, routeMap, deps.proxyDeps);
|
|
3676
|
+
} catch (err) {
|
|
3677
|
+
const message = _chunkSVNDB62Dcjs.serializeError.call(void 0, err);
|
|
3678
|
+
if (deps.logger) deps.logger.error("[OutboundApi] dispatch error:", message);
|
|
3679
|
+
else console.error("[OutboundApi] dispatch error:", message);
|
|
3680
|
+
emitWebhookEvent({ kind: "server.error", at: Date.now(), message });
|
|
3681
|
+
if (audit) audit.error = message;
|
|
3682
|
+
writeJsonError(res, 502, message);
|
|
3683
|
+
} finally {
|
|
3684
|
+
routeMap.removeRoute(token);
|
|
3685
|
+
if (releaseSerial) releaseSerial();
|
|
3686
|
+
}
|
|
2002
3687
|
} finally {
|
|
2003
|
-
|
|
3688
|
+
if (releaseConcurrency) releaseConcurrency();
|
|
2004
3689
|
}
|
|
2005
3690
|
}
|
|
2006
3691
|
function makeReplayRequest(req, rawBody) {
|
|
@@ -2014,65 +3699,50 @@ function makeReplayRequest(req, rawBody) {
|
|
|
2014
3699
|
return readable;
|
|
2015
3700
|
}
|
|
2016
3701
|
|
|
2017
|
-
// src/outbound-api/outboundRateLimiter.ts
|
|
2018
|
-
var DEFAULT_WINDOW_MS = 6e4;
|
|
2019
|
-
var DEFAULT_MAX_REQUESTS = 60;
|
|
2020
|
-
var OutboundRateLimiter = (_class3 = class {
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
/** apiKeyId → ascending request timestamps within the current window. */
|
|
2024
|
-
__init4() {this.hits = /* @__PURE__ */ new Map()}
|
|
2025
|
-
constructor(options = {}) {;_class3.prototype.__init4.call(this);
|
|
2026
|
-
this.windowMs = _nullishCoalesce(options.windowMs, () => ( DEFAULT_WINDOW_MS));
|
|
2027
|
-
this.maxRequests = _nullishCoalesce(options.maxRequests, () => ( DEFAULT_MAX_REQUESTS));
|
|
2028
|
-
}
|
|
2029
|
-
/**
|
|
2030
|
-
* Record a request for `apiKeyId` and decide whether it is allowed. Prunes
|
|
2031
|
-
* timestamps older than the window first; when allowed, the request's
|
|
2032
|
-
* timestamp is appended.
|
|
2033
|
-
*/
|
|
2034
|
-
check(apiKeyId, now = Date.now()) {
|
|
2035
|
-
const cutoff = now - this.windowMs;
|
|
2036
|
-
const timestamps = (_nullishCoalesce(this.hits.get(apiKeyId), () => ( []))).filter((ts) => ts > cutoff);
|
|
2037
|
-
if (timestamps.length >= this.maxRequests) {
|
|
2038
|
-
if (timestamps.length === 0) {
|
|
2039
|
-
this.hits.delete(apiKeyId);
|
|
2040
|
-
return { allowed: false, retryAfterSeconds: Math.max(1, Math.ceil(this.windowMs / 1e3)) };
|
|
2041
|
-
}
|
|
2042
|
-
const oldest = timestamps[0];
|
|
2043
|
-
const retryAfterMs = Math.max(0, oldest + this.windowMs - now);
|
|
2044
|
-
this.hits.set(apiKeyId, timestamps);
|
|
2045
|
-
return {
|
|
2046
|
-
allowed: false,
|
|
2047
|
-
retryAfterSeconds: Math.max(1, Math.ceil(retryAfterMs / 1e3))
|
|
2048
|
-
};
|
|
2049
|
-
}
|
|
2050
|
-
timestamps.push(now);
|
|
2051
|
-
this.hits.set(apiKeyId, timestamps);
|
|
2052
|
-
return { allowed: true, retryAfterSeconds: 0 };
|
|
2053
|
-
}
|
|
2054
|
-
/** Drop all recorded state (tests / teardown). */
|
|
2055
|
-
reset() {
|
|
2056
|
-
this.hits.clear();
|
|
2057
|
-
}
|
|
2058
|
-
}, _class3);
|
|
2059
|
-
|
|
2060
3702
|
// src/outbound-api/OutboundApiServer.ts
|
|
2061
3703
|
var DEFAULT_OUTBOUND_PORT = 8765;
|
|
2062
3704
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
2063
3705
|
var LAN_ADDR = "0.0.0.0";
|
|
2064
|
-
var
|
|
2065
|
-
|
|
3706
|
+
var OutboundApiConfigError = class extends Error {
|
|
3707
|
+
|
|
3708
|
+
constructor(missing) {
|
|
3709
|
+
super(
|
|
3710
|
+
"Outbound API server cannot start: incomplete model-kind configuration \u2014 " + missing.map((m) => `${m.endpoint} missing [${m.missingKinds.join(", ")}]`).join("; ")
|
|
3711
|
+
);
|
|
3712
|
+
this.name = "OutboundApiConfigError";
|
|
3713
|
+
this.missing = missing;
|
|
3714
|
+
}
|
|
3715
|
+
};
|
|
3716
|
+
var OutboundApiServer = (_class11 = class {
|
|
3717
|
+
constructor(deps, onPortChange) {;_class11.prototype.__init12.call(this);_class11.prototype.__init13.call(this);_class11.prototype.__init14.call(this);_class11.prototype.__init15.call(this);_class11.prototype.__init16.call(this);_class11.prototype.__init17.call(this);_class11.prototype.__init18.call(this);_class11.prototype.__init19.call(this);_class11.prototype.__init20.call(this);
|
|
2066
3718
|
this.deps = deps;
|
|
2067
3719
|
this.onPortChange = onPortChange;
|
|
2068
3720
|
}
|
|
2069
3721
|
|
|
2070
3722
|
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
3723
|
+
__init12() {this.server = null}
|
|
3724
|
+
__init13() {this.boundPort = 0}
|
|
3725
|
+
__init14() {this.boundAddr = LOOPBACK_ADDR}
|
|
3726
|
+
__init15() {this.endpoints = []}
|
|
3727
|
+
|
|
3728
|
+
|
|
3729
|
+
|
|
3730
|
+
__init16() {this.rateLimiter = new OutboundRateLimiter()}
|
|
3731
|
+
/**
|
|
3732
|
+
* Redeem-attempt limiter (voucher-redemption #9, design D6) — a SEPARATE bucket
|
|
3733
|
+
* from the traffic `rateLimiter`, keyed by the authenticating key id, so
|
|
3734
|
+
* brute-forcing `CC_` codes is throttled (a handful/min) without touching the
|
|
3735
|
+
* per-key request rate. Conservative fixed defaults (10 / 60s).
|
|
3736
|
+
*/
|
|
3737
|
+
__init17() {this.redeemLimiter = new OutboundRateLimiter({ maxRequests: 10, windowMs: 6e4 })}
|
|
3738
|
+
/**
|
|
3739
|
+
* Per-key redeem mutex (voucher-redemption #9, MJ1 fix). One instance for the
|
|
3740
|
+
* server's lifetime so concurrent redeem REQUESTS for the same key serialize
|
|
3741
|
+
* (relative grant increments accumulate instead of clobbering a snapshot).
|
|
3742
|
+
*/
|
|
3743
|
+
__init18() {this.redeemMutex = new KeyedMutex()}
|
|
3744
|
+
__init19() {this.serialQueue = new UserMessageSerialQueue()}
|
|
3745
|
+
__init20() {this.concurrencyGate = new OutboundConcurrencyGate()}
|
|
2076
3746
|
/**
|
|
2077
3747
|
* Apply a config. Restarts the listener ONLY when the bind address or port
|
|
2078
3748
|
* changes (or when toggling enabled); per-endpoint routing config is updated
|
|
@@ -2080,12 +3750,25 @@ var OutboundApiServer = (_class4 = class {
|
|
|
2080
3750
|
*/
|
|
2081
3751
|
async applyConfig(input) {
|
|
2082
3752
|
this.endpoints = input.endpoints;
|
|
3753
|
+
this.userMessageQueue = input.userMessageQueue;
|
|
3754
|
+
this.concurrencyQueue = input.concurrencyQueue;
|
|
3755
|
+
this.voucherConfig = input.voucher;
|
|
2083
3756
|
const wantAddr = input.networkBinding ? LAN_ADDR : LOOPBACK_ADDR;
|
|
2084
3757
|
const wantPort = _nullishCoalesce(input.port, () => ( DEFAULT_OUTBOUND_PORT));
|
|
2085
3758
|
if (!input.enabled) {
|
|
2086
3759
|
await this.stop();
|
|
2087
3760
|
return;
|
|
2088
3761
|
}
|
|
3762
|
+
const missing = _chunkSVXC36MUcjs.validateServerModelConfig.call(void 0, {
|
|
3763
|
+
enabled: true,
|
|
3764
|
+
networkBinding: input.networkBinding,
|
|
3765
|
+
endpoints: input.endpoints,
|
|
3766
|
+
port: input.port
|
|
3767
|
+
});
|
|
3768
|
+
if (missing.length > 0) {
|
|
3769
|
+
await this.stop();
|
|
3770
|
+
throw new OutboundApiConfigError(missing);
|
|
3771
|
+
}
|
|
2089
3772
|
const running = this.server !== null;
|
|
2090
3773
|
const bindChanged = running && (this.boundAddr !== wantAddr || this.boundPort !== wantPort);
|
|
2091
3774
|
if (running && !bindChanged) {
|
|
@@ -2100,8 +3783,8 @@ var OutboundApiServer = (_class4 = class {
|
|
|
2100
3783
|
const actualPort = await this.listen(bindAddr, port);
|
|
2101
3784
|
this.boundAddr = bindAddr;
|
|
2102
3785
|
this.boundPort = actualPort;
|
|
2103
|
-
if (actualPort !== port) _optionalChain([this, 'access',
|
|
2104
|
-
|
|
3786
|
+
if (actualPort !== port) _optionalChain([this, 'access', _147 => _147.onPortChange, 'optionalCall', _148 => _148(actualPort)]);
|
|
3787
|
+
this.logInfo(`[OutboundApiServer] Listening on ${bindAddr}:${actualPort}`);
|
|
2105
3788
|
return actualPort;
|
|
2106
3789
|
}
|
|
2107
3790
|
/** Bind once; on EADDRINUSE retry with an ephemeral port (port 0). */
|
|
@@ -2123,7 +3806,7 @@ var OutboundApiServer = (_class4 = class {
|
|
|
2123
3806
|
const addr = server.address();
|
|
2124
3807
|
if (addr && typeof addr === "object") {
|
|
2125
3808
|
server.removeListener("error", onError);
|
|
2126
|
-
server.on("error", (e) =>
|
|
3809
|
+
server.on("error", (e) => this.logError("[OutboundApiServer] server error", _chunkSVNDB62Dcjs.serializeError.call(void 0, e)));
|
|
2127
3810
|
this.server = server;
|
|
2128
3811
|
resolve(addr.port);
|
|
2129
3812
|
} else {
|
|
@@ -2134,21 +3817,58 @@ var OutboundApiServer = (_class4 = class {
|
|
|
2134
3817
|
}
|
|
2135
3818
|
/** Per-request handler. Auth is enforced on EVERY request (incl. loopback). */
|
|
2136
3819
|
onRequest(req, res) {
|
|
3820
|
+
if (this.deps.healthReportProvider && this.tryServeHealth(req, res)) return;
|
|
2137
3821
|
handleOutboundRequest(
|
|
2138
3822
|
req,
|
|
2139
3823
|
res,
|
|
2140
3824
|
this.deps,
|
|
2141
|
-
{
|
|
2142
|
-
|
|
3825
|
+
{
|
|
3826
|
+
endpoints: this.endpoints,
|
|
3827
|
+
userMessageQueue: this.userMessageQueue,
|
|
3828
|
+
concurrencyQueue: this.concurrencyQueue,
|
|
3829
|
+
voucher: this.voucherConfig
|
|
3830
|
+
},
|
|
3831
|
+
this.rateLimiter,
|
|
3832
|
+
this.serialQueue,
|
|
3833
|
+
this.concurrencyGate,
|
|
3834
|
+
this.redeemLimiter,
|
|
3835
|
+
this.redeemMutex
|
|
2143
3836
|
).catch((err) => {
|
|
2144
3837
|
const message = _chunkSVNDB62Dcjs.serializeError.call(void 0, err);
|
|
2145
|
-
|
|
3838
|
+
this.logError("[OutboundApiServer] unhandled error:", message);
|
|
2146
3839
|
if (!res.headersSent) {
|
|
2147
3840
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
2148
3841
|
res.end(JSON.stringify({ error: { type: "outbound_api_error", message } }));
|
|
2149
3842
|
}
|
|
2150
3843
|
});
|
|
2151
3844
|
}
|
|
3845
|
+
/**
|
|
3846
|
+
* Serve `GET|HEAD /health` (+ `/healthz`) from the injected provider, returning
|
|
3847
|
+
* true when it handled the request. 200 when `ok`, else 503; secret-free body.
|
|
3848
|
+
*/
|
|
3849
|
+
tryServeHealth(req, res) {
|
|
3850
|
+
const provider = this.deps.healthReportProvider;
|
|
3851
|
+
if (!provider) return false;
|
|
3852
|
+
if (req.method !== "GET" && req.method !== "HEAD") return false;
|
|
3853
|
+
const path = _optionalChain([(_nullishCoalesce(req.url, () => ( "/"))), 'access', _149 => _149.split, 'call', _150 => _150("?"), 'access', _151 => _151[0], 'optionalAccess', _152 => _152.replace, 'call', _153 => _153(/\/+$/, "")]) || "/";
|
|
3854
|
+
if (path !== "/health" && path !== "/healthz") return false;
|
|
3855
|
+
const report = provider();
|
|
3856
|
+
res.writeHead(_healthloggingtypes.healthHttpStatus.call(void 0, report.status), { "Content-Type": "application/json" });
|
|
3857
|
+
res.end(req.method === "HEAD" ? void 0 : JSON.stringify(report));
|
|
3858
|
+
return true;
|
|
3859
|
+
}
|
|
3860
|
+
/** Route an info lifecycle line through the injected logger, else `console.log`
|
|
3861
|
+
* (byte-identical legacy fallback when no logger is wired). */
|
|
3862
|
+
logInfo(message) {
|
|
3863
|
+
if (this.deps.logger) this.deps.logger.info(message);
|
|
3864
|
+
else console.log(message);
|
|
3865
|
+
}
|
|
3866
|
+
/** Route an error lifecycle line through the injected logger, else `console.error`. */
|
|
3867
|
+
logError(message, detail) {
|
|
3868
|
+
if (this.deps.logger) this.deps.logger.error(message, detail);
|
|
3869
|
+
else if (detail === void 0) console.error(message);
|
|
3870
|
+
else console.error(message, detail);
|
|
3871
|
+
}
|
|
2152
3872
|
/** Stop the listener and release the port. */
|
|
2153
3873
|
async stop() {
|
|
2154
3874
|
const server = this.server;
|
|
@@ -2157,7 +3877,7 @@ var OutboundApiServer = (_class4 = class {
|
|
|
2157
3877
|
this.boundPort = 0;
|
|
2158
3878
|
return new Promise((resolve) => {
|
|
2159
3879
|
server.close(() => {
|
|
2160
|
-
|
|
3880
|
+
this.logInfo("[OutboundApiServer] Stopped");
|
|
2161
3881
|
resolve();
|
|
2162
3882
|
});
|
|
2163
3883
|
});
|
|
@@ -2189,7 +3909,18 @@ var OutboundApiServer = (_class4 = class {
|
|
|
2189
3909
|
lanFormats: lanBase ? formatUrls(lanBase) : null
|
|
2190
3910
|
};
|
|
2191
3911
|
}
|
|
2192
|
-
|
|
3912
|
+
/**
|
|
3913
|
+
* Live queue-occupancy snapshot (only active entries). This getter's name +
|
|
3914
|
+
* shape are FROZEN — `omnicross-uqc-daemon` spreads it into its `/status`
|
|
3915
|
+
* response; the existing {@link getStatus} shape is deliberately NOT changed.
|
|
3916
|
+
*/
|
|
3917
|
+
getQueueStatus() {
|
|
3918
|
+
return {
|
|
3919
|
+
serial: this.serialQueue.getStatus(),
|
|
3920
|
+
concurrency: this.concurrencyGate.getStatus()
|
|
3921
|
+
};
|
|
3922
|
+
}
|
|
3923
|
+
}, _class11);
|
|
2193
3924
|
function formatUrls(base) {
|
|
2194
3925
|
return {
|
|
2195
3926
|
chat: `${base}/v1/chat/completions`,
|
|
@@ -2209,66 +3940,88 @@ function firstLanIPv4() {
|
|
|
2209
3940
|
return null;
|
|
2210
3941
|
}
|
|
2211
3942
|
|
|
2212
|
-
// src/outbound-api/
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
return {
|
|
2217
|
-
endpoint,
|
|
2218
|
-
defaultModel: "",
|
|
2219
|
-
backgroundModel: "",
|
|
2220
|
-
useSubscription: false
|
|
2221
|
-
};
|
|
3943
|
+
// src/outbound-api/keySpendTracker.ts
|
|
3944
|
+
function startOfLocalDay(ts) {
|
|
3945
|
+
const d = new Date(ts);
|
|
3946
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
2222
3947
|
}
|
|
2223
|
-
function
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
port: DEFAULT_OUTBOUND_PORT
|
|
2229
|
-
};
|
|
3948
|
+
function startOfLocalWeek(ts) {
|
|
3949
|
+
const d = new Date(ts);
|
|
3950
|
+
const day = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
3951
|
+
day.setDate(day.getDate() - day.getDay());
|
|
3952
|
+
return day.getTime();
|
|
2230
3953
|
}
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
3954
|
+
var KeySpendTracker = (_class12 = class {
|
|
3955
|
+
constructor(seeder) {;_class12.prototype.__init21.call(this);
|
|
3956
|
+
this.seeder = seeder;
|
|
3957
|
+
}
|
|
3958
|
+
|
|
3959
|
+
/** apiKeyId → live accumulated spend (seeded lazily). */
|
|
3960
|
+
__init21() {this.spend = /* @__PURE__ */ new Map()}
|
|
3961
|
+
/**
|
|
3962
|
+
* Read the key's accumulated spend, seeding from the durable store on a cache
|
|
3963
|
+
* miss and rolling the daily/weekly windows on a hit.
|
|
3964
|
+
*/
|
|
3965
|
+
async getSpend(apiKeyId, now) {
|
|
3966
|
+
const existing = this.spend.get(apiKeyId);
|
|
3967
|
+
if (existing) {
|
|
3968
|
+
rollWindows(existing, now);
|
|
3969
|
+
return existing;
|
|
2245
3970
|
}
|
|
3971
|
+
const seeded = await this.seed(apiKeyId, now);
|
|
3972
|
+
const existingAfterSeed = this.spend.get(apiKeyId);
|
|
3973
|
+
if (existingAfterSeed) {
|
|
3974
|
+
rollWindows(existingAfterSeed, now);
|
|
3975
|
+
return existingAfterSeed;
|
|
3976
|
+
}
|
|
3977
|
+
this.spend.set(apiKeyId, seeded);
|
|
3978
|
+
return seeded;
|
|
3979
|
+
}
|
|
3980
|
+
/**
|
|
3981
|
+
* Increment the aggregate for one recorded request. No-op when the key has not
|
|
3982
|
+
* been seeded yet — a later `getSpend` will lazy-seed from the durable store,
|
|
3983
|
+
* which already includes this record, so it is neither lost nor double-counted.
|
|
3984
|
+
*/
|
|
3985
|
+
add(apiKeyId, costUsd, now) {
|
|
3986
|
+
const s = this.spend.get(apiKeyId);
|
|
3987
|
+
if (!s) return;
|
|
3988
|
+
rollWindows(s, now);
|
|
3989
|
+
s.dailyUsd += costUsd;
|
|
3990
|
+
s.weeklyUsd += costUsd;
|
|
3991
|
+
s.totalUsd += costUsd;
|
|
3992
|
+
}
|
|
3993
|
+
/** Drop all cached spend (tests / teardown). */
|
|
3994
|
+
reset() {
|
|
3995
|
+
this.spend.clear();
|
|
3996
|
+
}
|
|
3997
|
+
/**
|
|
3998
|
+
* Derive a fresh spend snapshot for `apiKeyId` from the durable store in a
|
|
3999
|
+
* SINGLE scan (all three windows at once). `endTs` is EXCLUSIVE, so `now + 1`
|
|
4000
|
+
* includes a record stamped at exactly `now`.
|
|
4001
|
+
*/
|
|
4002
|
+
async seed(apiKeyId, now) {
|
|
4003
|
+
const dailyWindowStart = startOfLocalDay(now);
|
|
4004
|
+
const weeklyWindowStart = startOfLocalWeek(now);
|
|
4005
|
+
const { totalUsd, dailyUsd, weeklyUsd } = await this.seeder.getSpendByKey({
|
|
4006
|
+
apiKeyId,
|
|
4007
|
+
dayStartTs: dailyWindowStart,
|
|
4008
|
+
weekStartTs: weeklyWindowStart,
|
|
4009
|
+
endTs: now + 1
|
|
4010
|
+
});
|
|
4011
|
+
return { dailyUsd, dailyWindowStart, weeklyUsd, weeklyWindowStart, totalUsd };
|
|
4012
|
+
}
|
|
4013
|
+
}, _class12);
|
|
4014
|
+
function rollWindows(s, now) {
|
|
4015
|
+
const day = startOfLocalDay(now);
|
|
4016
|
+
if (s.dailyWindowStart !== day) {
|
|
4017
|
+
s.dailyUsd = 0;
|
|
4018
|
+
s.dailyWindowStart = day;
|
|
4019
|
+
}
|
|
4020
|
+
const week = startOfLocalWeek(now);
|
|
4021
|
+
if (s.weeklyWindowStart !== week) {
|
|
4022
|
+
s.weeklyUsd = 0;
|
|
4023
|
+
s.weeklyWindowStart = week;
|
|
2246
4024
|
}
|
|
2247
|
-
return {
|
|
2248
|
-
enabled: raw.enabled === true,
|
|
2249
|
-
networkBinding: raw.networkBinding === true,
|
|
2250
|
-
endpoints: ALL_ENDPOINTS.map(
|
|
2251
|
-
(ep) => _nullishCoalesce(byEndpoint.get(ep), () => ( defaultEndpointConfig(ep)))
|
|
2252
|
-
),
|
|
2253
|
-
port: _nullishCoalesce(raw.port, () => ( base.port))
|
|
2254
|
-
};
|
|
2255
|
-
}
|
|
2256
|
-
async function loadServerConfig(store) {
|
|
2257
|
-
const raw = await store.get(
|
|
2258
|
-
OUTBOUND_API_SERVER_CONFIG_KEY
|
|
2259
|
-
);
|
|
2260
|
-
return normalizeServerConfig(raw);
|
|
2261
|
-
}
|
|
2262
|
-
async function saveServerConfig(store, config) {
|
|
2263
|
-
await store.set(OUTBOUND_API_SERVER_CONFIG_KEY, config);
|
|
2264
|
-
}
|
|
2265
|
-
function mergeServerConfig(current, patch) {
|
|
2266
|
-
return normalizeServerConfig({
|
|
2267
|
-
enabled: _nullishCoalesce(patch.enabled, () => ( current.enabled)),
|
|
2268
|
-
networkBinding: _nullishCoalesce(patch.networkBinding, () => ( current.networkBinding)),
|
|
2269
|
-
endpoints: _nullishCoalesce(patch.endpoints, () => ( current.endpoints)),
|
|
2270
|
-
port: _nullishCoalesce(patch.port, () => ( current.port))
|
|
2271
|
-
});
|
|
2272
4025
|
}
|
|
2273
4026
|
|
|
2274
4027
|
// src/outbound-api/index.ts
|
|
@@ -2304,7 +4057,7 @@ async function callOpenAICompletion(provider, apiKey, options, logger) {
|
|
|
2304
4057
|
}
|
|
2305
4058
|
}
|
|
2306
4059
|
const apiUrl = _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: options.model, stream: false });
|
|
2307
|
-
const headers =
|
|
4060
|
+
const headers = _chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey);
|
|
2308
4061
|
logger.info("Calling OpenAI completion API", { url: apiUrl, model: options.model });
|
|
2309
4062
|
const response = await fetch(apiUrl, {
|
|
2310
4063
|
method: "POST",
|
|
@@ -2350,7 +4103,7 @@ async function callAnthropicCompletion(provider, apiKey, options, logger) {
|
|
|
2350
4103
|
stream: false
|
|
2351
4104
|
};
|
|
2352
4105
|
const apiUrl2 = _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: options.model, stream: false });
|
|
2353
|
-
const headers2 =
|
|
4106
|
+
const headers2 = _chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey);
|
|
2354
4107
|
logger.info("Calling Anthropic completion API with images", { url: apiUrl2, model: options.model });
|
|
2355
4108
|
const response2 = await fetch(apiUrl2, {
|
|
2356
4109
|
method: "POST",
|
|
@@ -2372,7 +4125,7 @@ async function callAnthropicCompletion(provider, apiKey, options, logger) {
|
|
|
2372
4125
|
message: {
|
|
2373
4126
|
id: `assistant-${Date.now()}`,
|
|
2374
4127
|
role: "assistant",
|
|
2375
|
-
content: _optionalChain([choice2, 'access',
|
|
4128
|
+
content: _optionalChain([choice2, 'access', _154 => _154.message, 'optionalAccess', _155 => _155.content]) || "",
|
|
2376
4129
|
timestamp: Date.now()
|
|
2377
4130
|
},
|
|
2378
4131
|
usage: openaiResponse2.usage ? {
|
|
@@ -2398,7 +4151,7 @@ async function callAnthropicCompletion(provider, apiKey, options, logger) {
|
|
|
2398
4151
|
};
|
|
2399
4152
|
const anthropicRequest = _chunkSUEGJ75Dcjs.convertOpenAIToAnthropic.call(void 0, openaiRequest, config);
|
|
2400
4153
|
const apiUrl = _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: options.model, stream: false });
|
|
2401
|
-
const headers =
|
|
4154
|
+
const headers = _chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey);
|
|
2402
4155
|
logger.info("Calling Anthropic completion API", { url: apiUrl, model: options.model });
|
|
2403
4156
|
const response = await fetch(apiUrl, {
|
|
2404
4157
|
method: "POST",
|
|
@@ -2452,7 +4205,7 @@ async function callGeminiCompletion(provider, apiKey, options, logger) {
|
|
|
2452
4205
|
request.systemInstruction = systemInstruction;
|
|
2453
4206
|
}
|
|
2454
4207
|
const apiUrl = _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: options.model, stream: false });
|
|
2455
|
-
const headers =
|
|
4208
|
+
const headers = _chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey);
|
|
2456
4209
|
logger.info("Calling Gemini completion API", { url: apiUrl, model: options.model });
|
|
2457
4210
|
const response = await fetch(apiUrl, {
|
|
2458
4211
|
method: "POST",
|
|
@@ -2477,7 +4230,7 @@ async function callGeminiCompletion(provider, apiKey, options, logger) {
|
|
|
2477
4230
|
const candidate = candidates[0];
|
|
2478
4231
|
const content = candidate.content;
|
|
2479
4232
|
let textContent = "";
|
|
2480
|
-
if (_optionalChain([content, 'optionalAccess',
|
|
4233
|
+
if (_optionalChain([content, 'optionalAccess', _156 => _156.parts])) {
|
|
2481
4234
|
for (const part of content.parts) {
|
|
2482
4235
|
if (part.text) {
|
|
2483
4236
|
textContent += part.text;
|
|
@@ -2523,7 +4276,7 @@ async function callOpenAIResponseCompletion(provider, apiKey, options, logger) {
|
|
|
2523
4276
|
}
|
|
2524
4277
|
}
|
|
2525
4278
|
const apiUrl = _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: options.model, stream: false });
|
|
2526
|
-
const headers =
|
|
4279
|
+
const headers = _chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey);
|
|
2527
4280
|
logger.info("Calling OpenAI Response API", { url: apiUrl, model: options.model });
|
|
2528
4281
|
const response = await fetch(apiUrl, {
|
|
2529
4282
|
method: "POST",
|
|
@@ -2589,9 +4342,9 @@ async function streamOpenAICompletion(provider, apiKey, options, messageId, call
|
|
|
2589
4342
|
}
|
|
2590
4343
|
}
|
|
2591
4344
|
const apiUrl = _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: options.model, stream: true });
|
|
2592
|
-
const headers =
|
|
4345
|
+
const headers = _chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey);
|
|
2593
4346
|
if (options.nativeSearchAugmentation) {
|
|
2594
|
-
|
|
4347
|
+
_chunkJMJBSACDcjs.applyAugmentation.call(void 0, request, options.nativeSearchAugmentation);
|
|
2595
4348
|
}
|
|
2596
4349
|
logger.info("Streaming OpenAI completion request", {
|
|
2597
4350
|
url: apiUrl,
|
|
@@ -2610,7 +4363,7 @@ async function streamOpenAICompletion(provider, apiKey, options, messageId, call
|
|
|
2610
4363
|
});
|
|
2611
4364
|
if (!response.ok) {
|
|
2612
4365
|
const errorText = await response.text();
|
|
2613
|
-
_optionalChain([callbacks, 'access',
|
|
4366
|
+
_optionalChain([callbacks, 'access', _157 => _157.onError, 'optionalCall', _158 => _158(`API error (${response.status}): ${errorText}`)]);
|
|
2614
4367
|
return;
|
|
2615
4368
|
}
|
|
2616
4369
|
const result = await _chunkHQP3K7PUcjs.streamSSEResponse.call(void 0, response, "openai", {
|
|
@@ -2620,7 +4373,7 @@ async function streamOpenAICompletion(provider, apiKey, options, messageId, call
|
|
|
2620
4373
|
onVideo: callbacks.onVideo,
|
|
2621
4374
|
onError: callbacks.onError
|
|
2622
4375
|
});
|
|
2623
|
-
_optionalChain([callbacks, 'access',
|
|
4376
|
+
_optionalChain([callbacks, 'access', _159 => _159.onDone, 'optionalCall', _160 => _160(
|
|
2624
4377
|
{
|
|
2625
4378
|
id: messageId,
|
|
2626
4379
|
role: "assistant",
|
|
@@ -2685,14 +4438,14 @@ async function streamAnthropicCompletion(provider, apiKey, options, messageId, c
|
|
|
2685
4438
|
}
|
|
2686
4439
|
}
|
|
2687
4440
|
const apiUrl = _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: options.model, stream: true });
|
|
2688
|
-
const headers =
|
|
4441
|
+
const headers = _chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey);
|
|
2689
4442
|
if (options.nativeSearchAugmentation) {
|
|
2690
|
-
|
|
4443
|
+
_chunkJMJBSACDcjs.applyAugmentation.call(void 0, anthropicRequest, options.nativeSearchAugmentation);
|
|
2691
4444
|
}
|
|
2692
4445
|
logger.info("Streaming Anthropic completion request", {
|
|
2693
4446
|
url: apiUrl,
|
|
2694
4447
|
model: anthropicRequest.model,
|
|
2695
|
-
messagesCount: _optionalChain([anthropicRequest, 'access',
|
|
4448
|
+
messagesCount: _optionalChain([anthropicRequest, 'access', _161 => _161.messages, 'optionalAccess', _162 => _162.length]),
|
|
2696
4449
|
max_tokens: anthropicRequest.max_tokens,
|
|
2697
4450
|
temperature: anthropicRequest.temperature,
|
|
2698
4451
|
stream: anthropicRequest.stream,
|
|
@@ -2709,7 +4462,7 @@ async function streamAnthropicCompletion(provider, apiKey, options, messageId, c
|
|
|
2709
4462
|
if (!response.ok) {
|
|
2710
4463
|
const errorText = await response.text();
|
|
2711
4464
|
logger.error("Anthropic API error", void 0, { status: response.status, errorText });
|
|
2712
|
-
_optionalChain([callbacks, 'access',
|
|
4465
|
+
_optionalChain([callbacks, 'access', _163 => _163.onError, 'optionalCall', _164 => _164(`API error (${response.status}): ${errorText}`)]);
|
|
2713
4466
|
return;
|
|
2714
4467
|
}
|
|
2715
4468
|
const result = await _chunkHQP3K7PUcjs.streamSSEResponse.call(void 0, response, "anthropic", {
|
|
@@ -2720,10 +4473,10 @@ async function streamAnthropicCompletion(provider, apiKey, options, messageId, c
|
|
|
2720
4473
|
});
|
|
2721
4474
|
logger.info("Anthropic stream complete", {
|
|
2722
4475
|
contentLength: result.content.length,
|
|
2723
|
-
reasoningLength: _optionalChain([result, 'access',
|
|
4476
|
+
reasoningLength: _optionalChain([result, 'access', _165 => _165.reasoning, 'optionalAccess', _166 => _166.length]) || 0,
|
|
2724
4477
|
blocksCount: result.blocks.length
|
|
2725
4478
|
});
|
|
2726
|
-
_optionalChain([callbacks, 'access',
|
|
4479
|
+
_optionalChain([callbacks, 'access', _167 => _167.onDone, 'optionalCall', _168 => _168(
|
|
2727
4480
|
{
|
|
2728
4481
|
id: messageId,
|
|
2729
4482
|
role: "assistant",
|
|
@@ -2757,9 +4510,9 @@ async function streamGeminiCompletion(provider, apiKey, options, messageId, call
|
|
|
2757
4510
|
request.systemInstruction = systemInstruction;
|
|
2758
4511
|
}
|
|
2759
4512
|
const apiUrl = _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: options.model, stream: true });
|
|
2760
|
-
const headers =
|
|
4513
|
+
const headers = _chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey);
|
|
2761
4514
|
if (options.nativeSearchAugmentation) {
|
|
2762
|
-
|
|
4515
|
+
_chunkJMJBSACDcjs.applyAugmentation.call(void 0, request, options.nativeSearchAugmentation);
|
|
2763
4516
|
}
|
|
2764
4517
|
logger.info("Streaming Gemini completion request", {
|
|
2765
4518
|
url: apiUrl,
|
|
@@ -2779,7 +4532,7 @@ async function streamGeminiCompletion(provider, apiKey, options, messageId, call
|
|
|
2779
4532
|
if (!response.ok) {
|
|
2780
4533
|
const errorText = await response.text();
|
|
2781
4534
|
logger.error("Gemini API error", void 0, { status: response.status, errorText });
|
|
2782
|
-
_optionalChain([callbacks, 'access',
|
|
4535
|
+
_optionalChain([callbacks, 'access', _169 => _169.onError, 'optionalCall', _170 => _170(`API error (${response.status}): ${errorText}`)]);
|
|
2783
4536
|
return;
|
|
2784
4537
|
}
|
|
2785
4538
|
const result = await _chunkHQP3K7PUcjs.streamSSEResponse.call(void 0, response, "gemini", {
|
|
@@ -2788,7 +4541,7 @@ async function streamGeminiCompletion(provider, apiKey, options, messageId, call
|
|
|
2788
4541
|
onError: callbacks.onError
|
|
2789
4542
|
});
|
|
2790
4543
|
logger.info("Gemini stream complete", { contentLength: result.content.length });
|
|
2791
|
-
_optionalChain([callbacks, 'access',
|
|
4544
|
+
_optionalChain([callbacks, 'access', _171 => _171.onDone, 'optionalCall', _172 => _172(
|
|
2792
4545
|
{
|
|
2793
4546
|
id: messageId,
|
|
2794
4547
|
role: "assistant",
|
|
@@ -2823,7 +4576,7 @@ async function streamOpenAIResponseCompletion(provider, apiKey, options, message
|
|
|
2823
4576
|
}
|
|
2824
4577
|
}
|
|
2825
4578
|
const apiUrl = _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: options.model, stream: true });
|
|
2826
|
-
const headers =
|
|
4579
|
+
const headers = _chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey);
|
|
2827
4580
|
logger.info("Streaming OpenAI Response API request", {
|
|
2828
4581
|
url: apiUrl,
|
|
2829
4582
|
providerId: options.providerId,
|
|
@@ -2840,7 +4593,7 @@ async function streamOpenAIResponseCompletion(provider, apiKey, options, message
|
|
|
2840
4593
|
});
|
|
2841
4594
|
if (!response.ok) {
|
|
2842
4595
|
const errorText = await response.text();
|
|
2843
|
-
_optionalChain([callbacks, 'access',
|
|
4596
|
+
_optionalChain([callbacks, 'access', _173 => _173.onError, 'optionalCall', _174 => _174(`API error (${response.status}): ${errorText}`)]);
|
|
2844
4597
|
return;
|
|
2845
4598
|
}
|
|
2846
4599
|
const result = await _chunkHQP3K7PUcjs.streamSSEResponse.call(void 0, response, "openai-response", {
|
|
@@ -2849,7 +4602,7 @@ async function streamOpenAIResponseCompletion(provider, apiKey, options, message
|
|
|
2849
4602
|
onError: callbacks.onError
|
|
2850
4603
|
});
|
|
2851
4604
|
logger.info("OpenAI Response API stream complete", { contentLength: result.content.length });
|
|
2852
|
-
_optionalChain([callbacks, 'access',
|
|
4605
|
+
_optionalChain([callbacks, 'access', _175 => _175.onDone, 'optionalCall', _176 => _176(
|
|
2853
4606
|
{
|
|
2854
4607
|
id: messageId,
|
|
2855
4608
|
role: "assistant",
|
|
@@ -2877,7 +4630,7 @@ async function resolveEffectiveMaxTokens(llmConfig, getProvider, logger, provide
|
|
|
2877
4630
|
}
|
|
2878
4631
|
try {
|
|
2879
4632
|
const globalParams = await llmConfig.getGlobalModelParameters();
|
|
2880
|
-
if (_optionalChain([globalParams, 'optionalAccess',
|
|
4633
|
+
if (_optionalChain([globalParams, 'optionalAccess', _177 => _177.maxTokens, 'optionalAccess', _178 => _178.enabled]) && globalParams.maxTokens.value > 0) {
|
|
2881
4634
|
logger.debug("Using global maxTokens", { maxTokens: globalParams.maxTokens.value });
|
|
2882
4635
|
return globalParams.maxTokens.value;
|
|
2883
4636
|
}
|
|
@@ -2887,8 +4640,8 @@ async function resolveEffectiveMaxTokens(llmConfig, getProvider, logger, provide
|
|
|
2887
4640
|
const MAX_TOKENS_CAP = 131072;
|
|
2888
4641
|
const provider = await getProvider(providerId);
|
|
2889
4642
|
if (provider) {
|
|
2890
|
-
const modelConfig = _optionalChain([provider, 'access',
|
|
2891
|
-
if (_optionalChain([modelConfig, 'optionalAccess',
|
|
4643
|
+
const modelConfig = _optionalChain([provider, 'access', _179 => _179.modelConfigs, 'optionalAccess', _180 => _180.find, 'call', _181 => _181((m) => m.id === modelId)]);
|
|
4644
|
+
if (_optionalChain([modelConfig, 'optionalAccess', _182 => _182.maxTokens]) && modelConfig.maxTokens > 0) {
|
|
2892
4645
|
const cappedMaxTokens = Math.min(modelConfig.maxTokens, MAX_TOKENS_CAP);
|
|
2893
4646
|
logger.debug("Using model config maxTokens", {
|
|
2894
4647
|
maxTokens: modelConfig.maxTokens,
|
|
@@ -2898,8 +4651,8 @@ async function resolveEffectiveMaxTokens(llmConfig, getProvider, logger, provide
|
|
|
2898
4651
|
}
|
|
2899
4652
|
if (provider.modelGroups) {
|
|
2900
4653
|
for (const group of provider.modelGroups) {
|
|
2901
|
-
const model = _optionalChain([group, 'access',
|
|
2902
|
-
if (_optionalChain([model, 'optionalAccess',
|
|
4654
|
+
const model = _optionalChain([group, 'access', _183 => _183.models, 'optionalAccess', _184 => _184.find, 'call', _185 => _185((m) => m.id === modelId)]);
|
|
4655
|
+
if (_optionalChain([model, 'optionalAccess', _186 => _186.maxTokens]) && model.maxTokens > 0) {
|
|
2903
4656
|
const cappedMaxTokens = Math.min(model.maxTokens, MAX_TOKENS_CAP);
|
|
2904
4657
|
logger.debug("Using modelGroup model maxTokens", {
|
|
2905
4658
|
maxTokens: model.maxTokens,
|
|
@@ -2944,7 +4697,7 @@ async function resolveThinkingBudget(getProvider, logger, providerId, modelId, m
|
|
|
2944
4697
|
}
|
|
2945
4698
|
const thinkingBudget = _thinkingconfig.calculateThinkingBudget.call(void 0, modelId, thinkLevel, maxTokens);
|
|
2946
4699
|
const provider = await getProvider(providerId);
|
|
2947
|
-
const providerName = _optionalChain([provider, 'optionalAccess',
|
|
4700
|
+
const providerName = _optionalChain([provider, 'optionalAccess', _187 => _187.name, 'optionalAccess', _188 => _188.toLowerCase, 'call', _189 => _189()]) || "";
|
|
2948
4701
|
const apiFormat = provider ? _chunkA2YMUCBTcjs.resolveApiFormat.call(void 0, provider) : "openai";
|
|
2949
4702
|
if (apiFormat === "anthropic" || providerName === "anthropic" || providerName.includes("claude")) {
|
|
2950
4703
|
const thinkingConfig = _thinkingconfig.buildAnthropicThinking.call(void 0, modelId, thinkLevel, maxTokens);
|
|
@@ -2981,7 +4734,7 @@ function logToolFormat(tools, logger) {
|
|
|
2981
4734
|
tools: openaiTools.map((t) => t.function.name),
|
|
2982
4735
|
firstTool: {
|
|
2983
4736
|
name: firstTool.function.name,
|
|
2984
|
-
description: _optionalChain([firstTool, 'access',
|
|
4737
|
+
description: _optionalChain([firstTool, 'access', _190 => _190.function, 'access', _191 => _191.description, 'optionalAccess', _192 => _192.slice, 'call', _193 => _193(0, 100)]),
|
|
2985
4738
|
parameters: firstTool.function.parameters
|
|
2986
4739
|
}
|
|
2987
4740
|
});
|
|
@@ -2992,7 +4745,7 @@ function logToolFormat(tools, logger) {
|
|
|
2992
4745
|
tools: anthropicTools.map((t) => t.name),
|
|
2993
4746
|
firstTool: {
|
|
2994
4747
|
name: firstTool.name,
|
|
2995
|
-
description: _optionalChain([firstTool, 'access',
|
|
4748
|
+
description: _optionalChain([firstTool, 'access', _194 => _194.description, 'optionalAccess', _195 => _195.slice, 'call', _196 => _196(0, 100)]),
|
|
2996
4749
|
input_schema: firstTool.input_schema
|
|
2997
4750
|
}
|
|
2998
4751
|
});
|
|
@@ -3003,9 +4756,9 @@ function logToolFormat(tools, logger) {
|
|
|
3003
4756
|
count: allDeclarations.length,
|
|
3004
4757
|
tools: allDeclarations.map((t) => t.name),
|
|
3005
4758
|
firstTool: {
|
|
3006
|
-
name: _optionalChain([allDeclarations, 'access',
|
|
3007
|
-
description: _optionalChain([allDeclarations, 'access',
|
|
3008
|
-
parameters: _optionalChain([allDeclarations, 'access',
|
|
4759
|
+
name: _optionalChain([allDeclarations, 'access', _197 => _197[0], 'optionalAccess', _198 => _198.name]),
|
|
4760
|
+
description: _optionalChain([allDeclarations, 'access', _199 => _199[0], 'optionalAccess', _200 => _200.description, 'optionalAccess', _201 => _201.slice, 'call', _202 => _202(0, 100)]),
|
|
4761
|
+
parameters: _optionalChain([allDeclarations, 'access', _203 => _203[0], 'optionalAccess', _204 => _204.parameters])
|
|
3009
4762
|
}
|
|
3010
4763
|
});
|
|
3011
4764
|
}
|
|
@@ -3064,7 +4817,7 @@ function buildToolRequest(apiFormat, conversationMessages, actualModel, options,
|
|
|
3064
4817
|
function extractDeltaContent(rawJson, apiFormat) {
|
|
3065
4818
|
const json = rawJson;
|
|
3066
4819
|
if (apiFormat === "google") {
|
|
3067
|
-
const parts = _optionalChain([json, 'access',
|
|
4820
|
+
const parts = _optionalChain([json, 'access', _205 => _205.candidates, 'optionalAccess', _206 => _206[0], 'optionalAccess', _207 => _207.content, 'optionalAccess', _208 => _208.parts]) || [];
|
|
3068
4821
|
let text = "";
|
|
3069
4822
|
for (const part of parts) {
|
|
3070
4823
|
if (part.thought === true) continue;
|
|
@@ -3072,15 +4825,15 @@ function extractDeltaContent(rawJson, apiFormat) {
|
|
|
3072
4825
|
}
|
|
3073
4826
|
return text;
|
|
3074
4827
|
} else if (apiFormat === "anthropic") {
|
|
3075
|
-
return _optionalChain([json, 'access',
|
|
4828
|
+
return _optionalChain([json, 'access', _209 => _209.delta, 'optionalAccess', _210 => _210.text]) || "";
|
|
3076
4829
|
} else {
|
|
3077
|
-
return _optionalChain([json, 'access',
|
|
4830
|
+
return _optionalChain([json, 'access', _211 => _211.choices, 'optionalAccess', _212 => _212[0], 'optionalAccess', _213 => _213.delta, 'optionalAccess', _214 => _214.content]) || "";
|
|
3078
4831
|
}
|
|
3079
4832
|
}
|
|
3080
4833
|
function extractDeltaReasoning(rawJson, apiFormat) {
|
|
3081
4834
|
const json = rawJson;
|
|
3082
4835
|
if (apiFormat === "google") {
|
|
3083
|
-
const parts = _optionalChain([json, 'access',
|
|
4836
|
+
const parts = _optionalChain([json, 'access', _215 => _215.candidates, 'optionalAccess', _216 => _216[0], 'optionalAccess', _217 => _217.content, 'optionalAccess', _218 => _218.parts]) || [];
|
|
3084
4837
|
let reasoning = "";
|
|
3085
4838
|
for (const part of parts) {
|
|
3086
4839
|
if (part.thought === true && part.text) reasoning += part.text;
|
|
@@ -3089,14 +4842,14 @@ function extractDeltaReasoning(rawJson, apiFormat) {
|
|
|
3089
4842
|
} else if (apiFormat === "anthropic") {
|
|
3090
4843
|
return "";
|
|
3091
4844
|
} else {
|
|
3092
|
-
const delta = _optionalChain([json, 'access',
|
|
3093
|
-
return (_optionalChain([delta, 'optionalAccess',
|
|
4845
|
+
const delta = _optionalChain([json, 'access', _219 => _219.choices, 'optionalAccess', _220 => _220[0], 'optionalAccess', _221 => _221.delta]);
|
|
4846
|
+
return (_optionalChain([delta, 'optionalAccess', _222 => _222.thinking, 'optionalAccess', _223 => _223.content]) || "") + (_optionalChain([delta, 'optionalAccess', _224 => _224.reasoning_content]) || "");
|
|
3094
4847
|
}
|
|
3095
4848
|
}
|
|
3096
4849
|
function extractToolCalls(rawJson, apiFormat, toolCalls, callbacks, logger, pendingOpenAIToolCalls) {
|
|
3097
4850
|
const json = rawJson;
|
|
3098
4851
|
if (apiFormat === "google") {
|
|
3099
|
-
const parts = _optionalChain([json, 'access',
|
|
4852
|
+
const parts = _optionalChain([json, 'access', _225 => _225.candidates, 'optionalAccess', _226 => _226[0], 'optionalAccess', _227 => _227.content, 'optionalAccess', _228 => _228.parts]) || [];
|
|
3100
4853
|
for (const part of parts) {
|
|
3101
4854
|
if (part.functionCall) {
|
|
3102
4855
|
logger.info("Function call detected", { functionCall: part.functionCall });
|
|
@@ -3106,13 +4859,13 @@ function extractToolCalls(rawJson, apiFormat, toolCalls, callbacks, logger, pend
|
|
|
3106
4859
|
args: part.functionCall.args || {}
|
|
3107
4860
|
};
|
|
3108
4861
|
toolCalls.push(toolCall);
|
|
3109
|
-
_optionalChain([callbacks, 'access',
|
|
4862
|
+
_optionalChain([callbacks, 'access', _229 => _229.onToolCall, 'optionalCall', _230 => _230(toolCall)]);
|
|
3110
4863
|
logger.info("Tool called", { toolName: toolCall.name });
|
|
3111
4864
|
}
|
|
3112
4865
|
}
|
|
3113
4866
|
} else if (apiFormat !== "anthropic") {
|
|
3114
|
-
const delta = _optionalChain([json, 'access',
|
|
3115
|
-
if (_optionalChain([delta, 'optionalAccess',
|
|
4867
|
+
const delta = _optionalChain([json, 'access', _231 => _231.choices, 'optionalAccess', _232 => _232[0], 'optionalAccess', _233 => _233.delta]);
|
|
4868
|
+
if (_optionalChain([delta, 'optionalAccess', _234 => _234.tool_calls]) && pendingOpenAIToolCalls) {
|
|
3116
4869
|
for (const tc of delta.tool_calls) {
|
|
3117
4870
|
const index = _nullishCoalesce(tc.index, () => ( 0));
|
|
3118
4871
|
let pending = pendingOpenAIToolCalls.get(index);
|
|
@@ -3121,8 +4874,8 @@ function extractToolCalls(rawJson, apiFormat, toolCalls, callbacks, logger, pend
|
|
|
3121
4874
|
pendingOpenAIToolCalls.set(index, pending);
|
|
3122
4875
|
}
|
|
3123
4876
|
if (tc.id) pending.id = tc.id;
|
|
3124
|
-
if (_optionalChain([tc, 'access',
|
|
3125
|
-
if (_optionalChain([tc, 'access',
|
|
4877
|
+
if (_optionalChain([tc, 'access', _235 => _235.function, 'optionalAccess', _236 => _236.name])) pending.name += tc.function.name;
|
|
4878
|
+
if (_optionalChain([tc, 'access', _237 => _237.function, 'optionalAccess', _238 => _238.arguments])) pending.arguments += tc.function.arguments;
|
|
3126
4879
|
}
|
|
3127
4880
|
}
|
|
3128
4881
|
}
|
|
@@ -3137,7 +4890,7 @@ function finalizeOpenAIToolCalls(pendingOpenAIToolCalls, toolCalls, callbacks, l
|
|
|
3137
4890
|
args: JSON.parse(pending.arguments || "{}")
|
|
3138
4891
|
};
|
|
3139
4892
|
toolCalls.push(toolCall);
|
|
3140
|
-
_optionalChain([callbacks, 'access',
|
|
4893
|
+
_optionalChain([callbacks, 'access', _239 => _239.onToolCall, 'optionalCall', _240 => _240(toolCall)]);
|
|
3141
4894
|
logger.info("Tool called (OpenAI)", { toolName: toolCall.name, args: toolCall.args });
|
|
3142
4895
|
} catch (e) {
|
|
3143
4896
|
logger.error("Failed to parse OpenAI tool call arguments", e instanceof Error ? e : void 0, {
|
|
@@ -3151,38 +4904,38 @@ function finalizeOpenAIToolCalls(pendingOpenAIToolCalls, toolCalls, callbacks, l
|
|
|
3151
4904
|
function parseStreamChunk(rawJson, apiFormat, callbacks, logger) {
|
|
3152
4905
|
const json = rawJson;
|
|
3153
4906
|
if (apiFormat === "google") {
|
|
3154
|
-
const candidate = _optionalChain([json, 'access',
|
|
4907
|
+
const candidate = _optionalChain([json, 'access', _241 => _241.candidates, 'optionalAccess', _242 => _242[0]]);
|
|
3155
4908
|
if (!candidate) {
|
|
3156
4909
|
logger.warn("No candidate found in Gemini response");
|
|
3157
4910
|
return;
|
|
3158
4911
|
}
|
|
3159
|
-
const parts = _optionalChain([candidate, 'access',
|
|
4912
|
+
const parts = _optionalChain([candidate, 'access', _243 => _243.content, 'optionalAccess', _244 => _244.parts]) || [];
|
|
3160
4913
|
for (const part of parts) {
|
|
3161
4914
|
if (part.thought === true && part.text) {
|
|
3162
|
-
_optionalChain([callbacks, 'access',
|
|
4915
|
+
_optionalChain([callbacks, 'access', _245 => _245.onReasoning, 'optionalCall', _246 => _246(part.text)]);
|
|
3163
4916
|
continue;
|
|
3164
4917
|
} else if (part.thoughtSignature && !part.thought) {
|
|
3165
4918
|
logger.debug("Gemini returned thoughtSignature without thought content");
|
|
3166
4919
|
}
|
|
3167
4920
|
if (part.text) {
|
|
3168
|
-
_optionalChain([callbacks, 'access',
|
|
4921
|
+
_optionalChain([callbacks, 'access', _247 => _247.onDelta, 'optionalCall', _248 => _248(part.text)]);
|
|
3169
4922
|
}
|
|
3170
4923
|
}
|
|
3171
4924
|
} else if (apiFormat === "anthropic") {
|
|
3172
4925
|
const delta = json.delta;
|
|
3173
|
-
if (_optionalChain([delta, 'optionalAccess',
|
|
3174
|
-
_optionalChain([callbacks, 'access',
|
|
4926
|
+
if (_optionalChain([delta, 'optionalAccess', _249 => _249.text])) {
|
|
4927
|
+
_optionalChain([callbacks, 'access', _250 => _250.onDelta, 'optionalCall', _251 => _251(delta.text)]);
|
|
3175
4928
|
}
|
|
3176
4929
|
} else {
|
|
3177
|
-
const delta = _optionalChain([json, 'access',
|
|
3178
|
-
if (_optionalChain([delta, 'optionalAccess',
|
|
3179
|
-
_optionalChain([callbacks, 'access',
|
|
4930
|
+
const delta = _optionalChain([json, 'access', _252 => _252.choices, 'optionalAccess', _253 => _253[0], 'optionalAccess', _254 => _254.delta]);
|
|
4931
|
+
if (_optionalChain([delta, 'optionalAccess', _255 => _255.content])) {
|
|
4932
|
+
_optionalChain([callbacks, 'access', _256 => _256.onDelta, 'optionalCall', _257 => _257(delta.content)]);
|
|
3180
4933
|
}
|
|
3181
|
-
if (_optionalChain([delta, 'optionalAccess',
|
|
3182
|
-
_optionalChain([callbacks, 'access',
|
|
4934
|
+
if (_optionalChain([delta, 'optionalAccess', _258 => _258.thinking, 'optionalAccess', _259 => _259.content])) {
|
|
4935
|
+
_optionalChain([callbacks, 'access', _260 => _260.onReasoning, 'optionalCall', _261 => _261(delta.thinking.content)]);
|
|
3183
4936
|
}
|
|
3184
|
-
if (_optionalChain([delta, 'optionalAccess',
|
|
3185
|
-
_optionalChain([callbacks, 'access',
|
|
4937
|
+
if (_optionalChain([delta, 'optionalAccess', _262 => _262.reasoning_content])) {
|
|
4938
|
+
_optionalChain([callbacks, 'access', _263 => _263.onReasoning, 'optionalCall', _264 => _264(delta.reasoning_content)]);
|
|
3186
4939
|
}
|
|
3187
4940
|
}
|
|
3188
4941
|
}
|
|
@@ -3195,7 +4948,7 @@ function buildIterationBlocks(iteration, content, reasoning, blocks, callbacks)
|
|
|
3195
4948
|
content: reasoning
|
|
3196
4949
|
};
|
|
3197
4950
|
blocks.push(thinkingBlock);
|
|
3198
|
-
_optionalChain([callbacks, 'access',
|
|
4951
|
+
_optionalChain([callbacks, 'access', _265 => _265.onBlock, 'optionalCall', _266 => _266(thinkingBlock)]);
|
|
3199
4952
|
}
|
|
3200
4953
|
if (content && content.trim()) {
|
|
3201
4954
|
const textBlock = {
|
|
@@ -3204,7 +4957,7 @@ function buildIterationBlocks(iteration, content, reasoning, blocks, callbacks)
|
|
|
3204
4957
|
content
|
|
3205
4958
|
};
|
|
3206
4959
|
blocks.push(textBlock);
|
|
3207
|
-
_optionalChain([callbacks, 'access',
|
|
4960
|
+
_optionalChain([callbacks, 'access', _267 => _267.onBlock, 'optionalCall', _268 => _268(textBlock)]);
|
|
3208
4961
|
}
|
|
3209
4962
|
}
|
|
3210
4963
|
async function executeToolCalls(toolCalls, mcpTools, mcpService, blocks, callbacks, logger, builtinExecutor) {
|
|
@@ -3219,7 +4972,7 @@ async function executeToolCalls(toolCalls, mcpTools, mcpService, blocks, callbac
|
|
|
3219
4972
|
status: "running"
|
|
3220
4973
|
};
|
|
3221
4974
|
blocks.push(toolUseBlock);
|
|
3222
|
-
_optionalChain([callbacks, 'access',
|
|
4975
|
+
_optionalChain([callbacks, 'access', _269 => _269.onBlock, 'optionalCall', _270 => _270(toolUseBlock)]);
|
|
3223
4976
|
try {
|
|
3224
4977
|
logger.info("Executing tool", { toolName: toolCall.name });
|
|
3225
4978
|
const mcpTool = mcpTools.find((t) => t.id === toolCall.name);
|
|
@@ -3242,8 +4995,8 @@ async function executeToolCalls(toolCalls, mcpTools, mcpService, blocks, callbac
|
|
|
3242
4995
|
result = { isError: true, content: [{ type: "text", text: "MCP service not available" }] };
|
|
3243
4996
|
}
|
|
3244
4997
|
logger.info("Tool result received", { result });
|
|
3245
|
-
_optionalChain([callbacks, 'access',
|
|
3246
|
-
const resultText = _optionalChain([result, 'access',
|
|
4998
|
+
_optionalChain([callbacks, 'access', _271 => _271.onToolResult, 'optionalCall', _272 => _272(toolCall.id, result)]);
|
|
4999
|
+
const resultText = _optionalChain([result, 'access', _273 => _273.content, 'optionalAccess', _274 => _274[0], 'optionalAccess', _275 => _275.text]) || JSON.stringify(result);
|
|
3247
5000
|
const toolResultBlock = {
|
|
3248
5001
|
id: `${toolCall.id}_result`,
|
|
3249
5002
|
type: "tool_result",
|
|
@@ -3253,11 +5006,11 @@ async function executeToolCalls(toolCalls, mcpTools, mcpService, blocks, callbac
|
|
|
3253
5006
|
isError: result.isError
|
|
3254
5007
|
};
|
|
3255
5008
|
blocks.push(toolResultBlock);
|
|
3256
|
-
_optionalChain([callbacks, 'access',
|
|
5009
|
+
_optionalChain([callbacks, 'access', _276 => _276.onBlock, 'optionalCall', _277 => _277(toolResultBlock)]);
|
|
3257
5010
|
const toolUseBlockRef = blocks.find((b) => b.id === `${toolCall.id}_use`);
|
|
3258
5011
|
if (toolUseBlockRef && toolUseBlockRef.type === "tool_use") {
|
|
3259
5012
|
toolUseBlockRef.status = result.isError ? "error" : "completed";
|
|
3260
|
-
_optionalChain([callbacks, 'access',
|
|
5013
|
+
_optionalChain([callbacks, 'access', _278 => _278.onBlock, 'optionalCall', _279 => _279({ ...toolUseBlockRef })]);
|
|
3261
5014
|
}
|
|
3262
5015
|
toolResults.push({ toolCall, result });
|
|
3263
5016
|
} catch (error) {
|
|
@@ -3267,7 +5020,7 @@ async function executeToolCalls(toolCalls, mcpTools, mcpService, blocks, callbac
|
|
|
3267
5020
|
isError: true,
|
|
3268
5021
|
content: [{ type: "text", text: `Error: ${errorMessage}` }]
|
|
3269
5022
|
};
|
|
3270
|
-
_optionalChain([callbacks, 'access',
|
|
5023
|
+
_optionalChain([callbacks, 'access', _280 => _280.onToolResult, 'optionalCall', _281 => _281(toolCall.id, errorResult)]);
|
|
3271
5024
|
const errorToolResultBlock = {
|
|
3272
5025
|
id: `${toolCall.id}_result`,
|
|
3273
5026
|
type: "tool_result",
|
|
@@ -3277,11 +5030,11 @@ async function executeToolCalls(toolCalls, mcpTools, mcpService, blocks, callbac
|
|
|
3277
5030
|
isError: true
|
|
3278
5031
|
};
|
|
3279
5032
|
blocks.push(errorToolResultBlock);
|
|
3280
|
-
_optionalChain([callbacks, 'access',
|
|
5033
|
+
_optionalChain([callbacks, 'access', _282 => _282.onBlock, 'optionalCall', _283 => _283(errorToolResultBlock)]);
|
|
3281
5034
|
const errorToolUseBlockRef = blocks.find((b) => b.id === `${toolCall.id}_use`);
|
|
3282
5035
|
if (errorToolUseBlockRef && errorToolUseBlockRef.type === "tool_use") {
|
|
3283
5036
|
errorToolUseBlockRef.status = "error";
|
|
3284
|
-
_optionalChain([callbacks, 'access',
|
|
5037
|
+
_optionalChain([callbacks, 'access', _284 => _284.onBlock, 'optionalCall', _285 => _285({ ...errorToolUseBlockRef })]);
|
|
3285
5038
|
}
|
|
3286
5039
|
toolResults.push({ toolCall, result: errorResult });
|
|
3287
5040
|
}
|
|
@@ -3291,7 +5044,7 @@ async function executeToolCalls(toolCalls, mcpTools, mcpService, blocks, callbac
|
|
|
3291
5044
|
function mapToolArguments(args, mcpTool, logger) {
|
|
3292
5045
|
const mappedArgs = { ...args };
|
|
3293
5046
|
const toolSchema = mcpTool.inputSchema;
|
|
3294
|
-
if (_optionalChain([toolSchema, 'optionalAccess',
|
|
5047
|
+
if (_optionalChain([toolSchema, 'optionalAccess', _286 => _286.properties])) {
|
|
3295
5048
|
const expectedParams = Object.keys(toolSchema.properties);
|
|
3296
5049
|
const paramMappings = {
|
|
3297
5050
|
"search_query": ["query", "q", "searchQuery", "search"],
|
|
@@ -3325,25 +5078,25 @@ async function streamWithTools(options, callbacks, mcpService, llmConfig, getPro
|
|
|
3325
5078
|
options.providerId,
|
|
3326
5079
|
options.model
|
|
3327
5080
|
);
|
|
3328
|
-
const actualProviderId = _optionalChain([routedInfo, 'optionalAccess',
|
|
3329
|
-
const actualModel = _optionalChain([routedInfo, 'optionalAccess',
|
|
5081
|
+
const actualProviderId = _optionalChain([routedInfo, 'optionalAccess', _287 => _287.actualProviderId]) || options.providerId;
|
|
5082
|
+
const actualModel = _optionalChain([routedInfo, 'optionalAccess', _288 => _288.actualModelId]) || options.model;
|
|
3330
5083
|
const provider = await getProvider(actualProviderId);
|
|
3331
5084
|
if (!provider) {
|
|
3332
|
-
_optionalChain([callbacks, 'access',
|
|
5085
|
+
_optionalChain([callbacks, 'access', _289 => _289.onError, 'optionalCall', _290 => _290(`Provider not found: ${actualProviderId}`)]);
|
|
3333
5086
|
return;
|
|
3334
5087
|
}
|
|
3335
5088
|
if (!provider.enabled) {
|
|
3336
|
-
_optionalChain([callbacks, 'access',
|
|
5089
|
+
_optionalChain([callbacks, 'access', _291 => _291.onError, 'optionalCall', _292 => _292(`Provider is disabled: ${provider.name}`)]);
|
|
3337
5090
|
return;
|
|
3338
5091
|
}
|
|
3339
5092
|
const { apiKey: effectiveKey } = _chunkA2YMUCBTcjs.resolveProviderEndpoint.call(void 0, provider);
|
|
3340
5093
|
const apiKey = resolveApiKey2(effectiveKey);
|
|
3341
5094
|
if (!apiKey) {
|
|
3342
|
-
_optionalChain([callbacks, 'access',
|
|
5095
|
+
_optionalChain([callbacks, 'access', _293 => _293.onError, 'optionalCall', _294 => _294("API key not configured")]);
|
|
3343
5096
|
return;
|
|
3344
5097
|
}
|
|
3345
5098
|
const messageId = `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
3346
|
-
_optionalChain([callbacks, 'access',
|
|
5099
|
+
_optionalChain([callbacks, 'access', _295 => _295.onStart, 'optionalCall', _296 => _296(messageId)]);
|
|
3347
5100
|
const globalParams = await llmConfig.getGlobalModelParameters();
|
|
3348
5101
|
const MAX_ITERATIONS = _nullishCoalesce(globalParams.toolMaxTurns, () => ( 100));
|
|
3349
5102
|
let iteration = 0;
|
|
@@ -3375,9 +5128,9 @@ async function streamWithTools(options, callbacks, mcpService, llmConfig, getPro
|
|
|
3375
5128
|
provider
|
|
3376
5129
|
);
|
|
3377
5130
|
if (options.nativeSearchAugmentation) {
|
|
3378
|
-
|
|
5131
|
+
_chunkJMJBSACDcjs.applyAugmentation.call(void 0, requestBody, options.nativeSearchAugmentation);
|
|
3379
5132
|
}
|
|
3380
|
-
const headers =
|
|
5133
|
+
const headers = _chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey);
|
|
3381
5134
|
logger.info("Sending tool call request", {
|
|
3382
5135
|
url,
|
|
3383
5136
|
headers,
|
|
@@ -3390,12 +5143,12 @@ async function streamWithTools(options, callbacks, mcpService, llmConfig, getPro
|
|
|
3390
5143
|
});
|
|
3391
5144
|
if (!response.ok) {
|
|
3392
5145
|
const errorText = await response.text();
|
|
3393
|
-
_optionalChain([callbacks, 'access',
|
|
5146
|
+
_optionalChain([callbacks, 'access', _297 => _297.onError, 'optionalCall', _298 => _298(`API error (${response.status}): ${errorText}`)]);
|
|
3394
5147
|
return;
|
|
3395
5148
|
}
|
|
3396
|
-
const reader = _optionalChain([response, 'access',
|
|
5149
|
+
const reader = _optionalChain([response, 'access', _299 => _299.body, 'optionalAccess', _300 => _300.getReader, 'call', _301 => _301()]);
|
|
3397
5150
|
if (!reader) {
|
|
3398
|
-
_optionalChain([callbacks, 'access',
|
|
5151
|
+
_optionalChain([callbacks, 'access', _302 => _302.onError, 'optionalCall', _303 => _303("No response body")]);
|
|
3399
5152
|
return;
|
|
3400
5153
|
}
|
|
3401
5154
|
const decoder = new TextDecoder();
|
|
@@ -3428,7 +5181,7 @@ async function streamWithTools(options, callbacks, mcpService, llmConfig, getPro
|
|
|
3428
5181
|
logger.debug("Parsed JSON", { json });
|
|
3429
5182
|
if (json.error) {
|
|
3430
5183
|
logger.error("API Error", void 0, { error: json.error });
|
|
3431
|
-
_optionalChain([callbacks, 'access',
|
|
5184
|
+
_optionalChain([callbacks, 'access', _304 => _304.onError, 'optionalCall', _305 => _305(`API Error: ${json.error.message || JSON.stringify(json.error)}`)]);
|
|
3432
5185
|
return;
|
|
3433
5186
|
}
|
|
3434
5187
|
parseStreamChunk(json, apiFormat, callbacks, logger);
|
|
@@ -3461,7 +5214,7 @@ async function streamWithTools(options, callbacks, mcpService, llmConfig, getPro
|
|
|
3461
5214
|
toolCallsLength: toolCalls.length,
|
|
3462
5215
|
executableToolCallsLength: executableToolCalls.length,
|
|
3463
5216
|
hasMcpTools: !!options.mcpTools,
|
|
3464
|
-
mcpToolsLength: _optionalChain([options, 'access',
|
|
5217
|
+
mcpToolsLength: _optionalChain([options, 'access', _306 => _306.mcpTools, 'optionalAccess', _307 => _307.length]),
|
|
3465
5218
|
hasMcpService: !!mcpService
|
|
3466
5219
|
});
|
|
3467
5220
|
if (executableToolCalls.length > 0 && options.mcpTools && (mcpService || builtinExecutor)) {
|
|
@@ -3482,7 +5235,7 @@ async function streamWithTools(options, callbacks, mcpService, llmConfig, getPro
|
|
|
3482
5235
|
builtinExecutor
|
|
3483
5236
|
);
|
|
3484
5237
|
const toolResultsText = toolResults.map(({ toolCall, result }) => {
|
|
3485
|
-
const resultText = typeof result === "string" ? result : _optionalChain([result, 'access',
|
|
5238
|
+
const resultText = typeof result === "string" ? result : _optionalChain([result, 'access', _308 => _308.content, 'optionalAccess', _309 => _309[0], 'optionalAccess', _310 => _310.text]) || JSON.stringify(result);
|
|
3486
5239
|
return `Tool ${toolCall.name} result:
|
|
3487
5240
|
${resultText}`;
|
|
3488
5241
|
}).join("\n\n");
|
|
@@ -3500,12 +5253,12 @@ ${resultText}`;
|
|
|
3500
5253
|
}
|
|
3501
5254
|
} catch (streamError) {
|
|
3502
5255
|
logger.error("Stream processing error", streamError instanceof Error ? streamError : void 0);
|
|
3503
|
-
_optionalChain([callbacks, 'access',
|
|
5256
|
+
_optionalChain([callbacks, 'access', _311 => _311.onError, 'optionalCall', _312 => _312(`Stream error: ${streamError instanceof Error ? streamError.message : "Unknown error"}`)]);
|
|
3504
5257
|
return;
|
|
3505
5258
|
}
|
|
3506
5259
|
}
|
|
3507
5260
|
logger.info("Sending final callback", { blocksCount: blocks.length });
|
|
3508
|
-
_optionalChain([callbacks, 'access',
|
|
5261
|
+
_optionalChain([callbacks, 'access', _313 => _313.onDone, 'optionalCall', _314 => _314({
|
|
3509
5262
|
id: messageId,
|
|
3510
5263
|
role: "assistant",
|
|
3511
5264
|
content: finalContent,
|
|
@@ -3516,7 +5269,7 @@ ${resultText}`;
|
|
|
3516
5269
|
} catch (error) {
|
|
3517
5270
|
logger.error("Error in streamWithTools", error instanceof Error ? error : void 0);
|
|
3518
5271
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3519
|
-
_optionalChain([callbacks, 'access',
|
|
5272
|
+
_optionalChain([callbacks, 'access', _315 => _315.onError, 'optionalCall', _316 => _316(message)]);
|
|
3520
5273
|
}
|
|
3521
5274
|
}
|
|
3522
5275
|
|
|
@@ -3555,8 +5308,8 @@ async function completeWithTransformers(options, llmConfig, getProvider, resolve
|
|
|
3555
5308
|
options.providerId,
|
|
3556
5309
|
options.model
|
|
3557
5310
|
);
|
|
3558
|
-
const actualProviderId = _optionalChain([routedInfo, 'optionalAccess',
|
|
3559
|
-
const actualModel = _optionalChain([routedInfo, 'optionalAccess',
|
|
5311
|
+
const actualProviderId = _optionalChain([routedInfo, 'optionalAccess', _317 => _317.actualProviderId]) || options.providerId;
|
|
5312
|
+
const actualModel = _optionalChain([routedInfo, 'optionalAccess', _318 => _318.actualModelId]) || options.model;
|
|
3560
5313
|
const provider = await getProvider(actualProviderId);
|
|
3561
5314
|
if (!provider) {
|
|
3562
5315
|
return { success: false, error: `Provider not found: ${actualProviderId}` };
|
|
@@ -3603,9 +5356,9 @@ async function completeWithTransformers(options, llmConfig, getProvider, resolve
|
|
|
3603
5356
|
extendedContext: options.useExtendedContext ? { enabled: true, model: actualModel } : void 0,
|
|
3604
5357
|
resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: actualModel, stream: false }),
|
|
3605
5358
|
buildHeaders: (config) => ({
|
|
3606
|
-
...
|
|
5359
|
+
..._chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey),
|
|
3607
5360
|
...config.headers,
|
|
3608
|
-
...
|
|
5361
|
+
..._chunkUNEFIWXIcjs.isOpenRouterProvider.call(void 0, provider) ? _chunkUNEFIWXIcjs.OPENROUTER_APP_HEADERS : {}
|
|
3609
5362
|
}),
|
|
3610
5363
|
// Add OpenRouter provider routing config if applicable
|
|
3611
5364
|
prepareBody: (requestBody) => addOpenRouterProviderToRequest(
|
|
@@ -3637,18 +5390,18 @@ async function completeWithTransformers(options, llmConfig, getProvider, resolve
|
|
|
3637
5390
|
{ endpointTransformer: void 0 }
|
|
3638
5391
|
);
|
|
3639
5392
|
const data = await transformedResponse.json();
|
|
3640
|
-
const choice = _optionalChain([data, 'access',
|
|
5393
|
+
const choice = _optionalChain([data, 'access', _319 => _319.choices, 'optionalAccess', _320 => _320[0]]);
|
|
3641
5394
|
if (!choice) {
|
|
3642
5395
|
return { success: false, error: "No choices in response" };
|
|
3643
5396
|
}
|
|
3644
|
-
const rawToolCalls = _optionalChain([choice, 'access',
|
|
3645
|
-
const toolCalls = _optionalChain([rawToolCalls, 'optionalAccess',
|
|
5397
|
+
const rawToolCalls = _optionalChain([choice, 'access', _321 => _321.message, 'optionalAccess', _322 => _322.tool_calls]);
|
|
5398
|
+
const toolCalls = _optionalChain([rawToolCalls, 'optionalAccess', _323 => _323.map, 'call', _324 => _324((tc) => ({
|
|
3646
5399
|
id: tc.id,
|
|
3647
5400
|
name: tc.function.name,
|
|
3648
5401
|
args: typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments
|
|
3649
5402
|
}))]);
|
|
3650
5403
|
const tapped = readUsageFromOpenAIResponse(data.usage);
|
|
3651
|
-
if (_optionalChain([recording, 'optionalAccess',
|
|
5404
|
+
if (_optionalChain([recording, 'optionalAccess', _325 => _325.recorder]) && tapped) {
|
|
3652
5405
|
recording.recorder.record({
|
|
3653
5406
|
messageId: _nullishCoalesce(options.messageId, () => ( null)),
|
|
3654
5407
|
parentMessageId: _nullishCoalesce(options.parentMessageId, () => ( null)),
|
|
@@ -3666,9 +5419,9 @@ async function completeWithTransformers(options, llmConfig, getProvider, resolve
|
|
|
3666
5419
|
message: {
|
|
3667
5420
|
id: `msg_${Date.now()}`,
|
|
3668
5421
|
role: "assistant",
|
|
3669
|
-
content: _optionalChain([choice, 'access',
|
|
5422
|
+
content: _optionalChain([choice, 'access', _326 => _326.message, 'optionalAccess', _327 => _327.content]) || "",
|
|
3670
5423
|
timestamp: Date.now(),
|
|
3671
|
-
thinking: data.reasoning_content || _optionalChain([choice, 'access',
|
|
5424
|
+
thinking: data.reasoning_content || _optionalChain([choice, 'access', _328 => _328.message, 'optionalAccess', _329 => _329.thinking, 'optionalAccess', _330 => _330.content]) ? { content: data.reasoning_content || _optionalChain([choice, 'access', _331 => _331.message, 'optionalAccess', _332 => _332.thinking, 'optionalAccess', _333 => _333.content]) || "", signature: _optionalChain([choice, 'access', _334 => _334.message, 'optionalAccess', _335 => _335.thinking, 'optionalAccess', _336 => _336.signature]) } : void 0,
|
|
3672
5425
|
toolCalls
|
|
3673
5426
|
},
|
|
3674
5427
|
usage: data.usage ? {
|
|
@@ -3689,21 +5442,21 @@ async function completeStreamWithTransformers(options, callbacks, llmConfig, get
|
|
|
3689
5442
|
options.providerId,
|
|
3690
5443
|
options.model
|
|
3691
5444
|
);
|
|
3692
|
-
const actualProviderId = _optionalChain([routedInfo, 'optionalAccess',
|
|
3693
|
-
const actualModel = _optionalChain([routedInfo, 'optionalAccess',
|
|
5445
|
+
const actualProviderId = _optionalChain([routedInfo, 'optionalAccess', _337 => _337.actualProviderId]) || options.providerId;
|
|
5446
|
+
const actualModel = _optionalChain([routedInfo, 'optionalAccess', _338 => _338.actualModelId]) || options.model;
|
|
3694
5447
|
const provider = await getProvider(actualProviderId);
|
|
3695
5448
|
if (!provider) {
|
|
3696
|
-
_optionalChain([callbacks, 'access',
|
|
5449
|
+
_optionalChain([callbacks, 'access', _339 => _339.onError, 'optionalCall', _340 => _340(`Provider not found: ${actualProviderId}`)]);
|
|
3697
5450
|
return;
|
|
3698
5451
|
}
|
|
3699
5452
|
if (!provider.enabled) {
|
|
3700
|
-
_optionalChain([callbacks, 'access',
|
|
5453
|
+
_optionalChain([callbacks, 'access', _341 => _341.onError, 'optionalCall', _342 => _342(`Provider is disabled: ${provider.name}`)]);
|
|
3701
5454
|
return;
|
|
3702
5455
|
}
|
|
3703
5456
|
const { apiKey: effectiveKey } = _chunkA2YMUCBTcjs.resolveProviderEndpoint.call(void 0, provider);
|
|
3704
5457
|
const apiKey = resolveApiKey2(effectiveKey);
|
|
3705
5458
|
if (!apiKey) {
|
|
3706
|
-
_optionalChain([callbacks, 'access',
|
|
5459
|
+
_optionalChain([callbacks, 'access', _343 => _343.onError, 'optionalCall', _344 => _344("API key not configured")]);
|
|
3707
5460
|
return;
|
|
3708
5461
|
}
|
|
3709
5462
|
const { chain, hasTransformers } = await resolveChainWithMain(
|
|
@@ -3715,7 +5468,7 @@ async function completeStreamWithTransformers(options, callbacks, llmConfig, get
|
|
|
3715
5468
|
return completeStreamFallback(options, callbacks);
|
|
3716
5469
|
}
|
|
3717
5470
|
const messageId = `msg_${Date.now()}`;
|
|
3718
|
-
_optionalChain([callbacks, 'access',
|
|
5471
|
+
_optionalChain([callbacks, 'access', _345 => _345.onStart, 'optionalCall', _346 => _346(messageId)]);
|
|
3719
5472
|
const unifiedRequest = {
|
|
3720
5473
|
model: actualModel,
|
|
3721
5474
|
messages: options.messages.map((m) => ({
|
|
@@ -3742,9 +5495,9 @@ async function completeStreamWithTransformers(options, callbacks, llmConfig, get
|
|
|
3742
5495
|
extendedContext: options.useExtendedContext ? { enabled: true, model: actualModel } : void 0,
|
|
3743
5496
|
resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : _chunkA2YMUCBTcjs.buildProviderApiUrl.call(void 0, provider, { model: actualModel, stream: true }),
|
|
3744
5497
|
buildHeaders: (config) => ({
|
|
3745
|
-
...
|
|
5498
|
+
..._chunkQ27JY5RGcjs.getProviderHeaders.call(void 0, provider, apiKey),
|
|
3746
5499
|
...config.headers,
|
|
3747
|
-
...
|
|
5500
|
+
..._chunkUNEFIWXIcjs.isOpenRouterProvider.call(void 0, provider) ? _chunkUNEFIWXIcjs.OPENROUTER_APP_HEADERS : {}
|
|
3748
5501
|
}),
|
|
3749
5502
|
// Add OpenRouter provider routing config if applicable
|
|
3750
5503
|
prepareBody: (requestBody) => addOpenRouterProviderToRequest(
|
|
@@ -3763,7 +5516,7 @@ async function completeStreamWithTransformers(options, callbacks, llmConfig, get
|
|
|
3763
5516
|
});
|
|
3764
5517
|
if (!response.ok) {
|
|
3765
5518
|
const errorText = await response.text();
|
|
3766
|
-
_optionalChain([callbacks, 'access',
|
|
5519
|
+
_optionalChain([callbacks, 'access', _347 => _347.onError, 'optionalCall', _348 => _348(`API error (${response.status}): ${errorText}`)]);
|
|
3767
5520
|
return;
|
|
3768
5521
|
}
|
|
3769
5522
|
const transformedResponse = await executor.executeResponseChain(
|
|
@@ -3773,9 +5526,9 @@ async function completeStreamWithTransformers(options, callbacks, llmConfig, get
|
|
|
3773
5526
|
chain,
|
|
3774
5527
|
{ endpointTransformer: void 0 }
|
|
3775
5528
|
);
|
|
3776
|
-
const reader = _optionalChain([transformedResponse, 'access',
|
|
5529
|
+
const reader = _optionalChain([transformedResponse, 'access', _349 => _349.body, 'optionalAccess', _350 => _350.getReader, 'call', _351 => _351()]);
|
|
3777
5530
|
if (!reader) {
|
|
3778
|
-
_optionalChain([callbacks, 'access',
|
|
5531
|
+
_optionalChain([callbacks, 'access', _352 => _352.onError, 'optionalCall', _353 => _353("No response body")]);
|
|
3779
5532
|
return;
|
|
3780
5533
|
}
|
|
3781
5534
|
const decoder = new TextDecoder();
|
|
@@ -3793,22 +5546,22 @@ async function completeStreamWithTransformers(options, callbacks, llmConfig, get
|
|
|
3793
5546
|
if (data === "[DONE]") continue;
|
|
3794
5547
|
try {
|
|
3795
5548
|
const json = JSON.parse(data);
|
|
3796
|
-
const delta = _optionalChain([json, 'access',
|
|
3797
|
-
if (_optionalChain([delta, 'optionalAccess',
|
|
5549
|
+
const delta = _optionalChain([json, 'access', _354 => _354.choices, 'optionalAccess', _355 => _355[0], 'optionalAccess', _356 => _356.delta]);
|
|
5550
|
+
if (_optionalChain([delta, 'optionalAccess', _357 => _357.content])) {
|
|
3798
5551
|
content += delta.content;
|
|
3799
|
-
_optionalChain([callbacks, 'access',
|
|
5552
|
+
_optionalChain([callbacks, 'access', _358 => _358.onDelta, 'optionalCall', _359 => _359(delta.content)]);
|
|
3800
5553
|
}
|
|
3801
|
-
if (_optionalChain([delta, 'optionalAccess',
|
|
5554
|
+
if (_optionalChain([delta, 'optionalAccess', _360 => _360.thinking, 'optionalAccess', _361 => _361.content])) {
|
|
3802
5555
|
reasoning += delta.thinking.content;
|
|
3803
|
-
_optionalChain([callbacks, 'access',
|
|
5556
|
+
_optionalChain([callbacks, 'access', _362 => _362.onReasoning, 'optionalCall', _363 => _363(delta.thinking.content)]);
|
|
3804
5557
|
}
|
|
3805
|
-
if (_optionalChain([delta, 'optionalAccess',
|
|
5558
|
+
if (_optionalChain([delta, 'optionalAccess', _364 => _364.reasoning_content])) {
|
|
3806
5559
|
reasoning += delta.reasoning_content;
|
|
3807
|
-
_optionalChain([callbacks, 'access',
|
|
5560
|
+
_optionalChain([callbacks, 'access', _365 => _365.onReasoning, 'optionalCall', _366 => _366(delta.reasoning_content)]);
|
|
3808
5561
|
}
|
|
3809
5562
|
if (json.usage) {
|
|
3810
5563
|
const tapped = readUsageFromOpenAIResponse(json.usage);
|
|
3811
|
-
if (_optionalChain([recording, 'optionalAccess',
|
|
5564
|
+
if (_optionalChain([recording, 'optionalAccess', _367 => _367.recorder]) && tapped) {
|
|
3812
5565
|
recording.recorder.record({
|
|
3813
5566
|
messageId: _nullishCoalesce(options.messageId, () => ( null)),
|
|
3814
5567
|
parentMessageId: _nullishCoalesce(options.parentMessageId, () => ( null)),
|
|
@@ -3821,7 +5574,7 @@ async function completeStreamWithTransformers(options, callbacks, llmConfig, get
|
|
|
3821
5574
|
rawUsage: json.usage
|
|
3822
5575
|
});
|
|
3823
5576
|
}
|
|
3824
|
-
_optionalChain([callbacks, 'access',
|
|
5577
|
+
_optionalChain([callbacks, 'access', _368 => _368.onDone, 'optionalCall', _369 => _369(
|
|
3825
5578
|
{
|
|
3826
5579
|
id: messageId,
|
|
3827
5580
|
role: "assistant",
|
|
@@ -3842,7 +5595,7 @@ async function completeStreamWithTransformers(options, callbacks, llmConfig, get
|
|
|
3842
5595
|
}
|
|
3843
5596
|
}
|
|
3844
5597
|
}
|
|
3845
|
-
_optionalChain([callbacks, 'access',
|
|
5598
|
+
_optionalChain([callbacks, 'access', _370 => _370.onDone, 'optionalCall', _371 => _371({
|
|
3846
5599
|
id: messageId,
|
|
3847
5600
|
role: "assistant",
|
|
3848
5601
|
content,
|
|
@@ -3854,13 +5607,13 @@ async function completeStreamWithTransformers(options, callbacks, llmConfig, get
|
|
|
3854
5607
|
}
|
|
3855
5608
|
} catch (error) {
|
|
3856
5609
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3857
|
-
_optionalChain([callbacks, 'access',
|
|
5610
|
+
_optionalChain([callbacks, 'access', _372 => _372.onError, 'optionalCall', _373 => _373(message)]);
|
|
3858
5611
|
}
|
|
3859
5612
|
}
|
|
3860
5613
|
|
|
3861
5614
|
// src/completion/CompletionService.ts
|
|
3862
|
-
var CompletionService = (
|
|
3863
|
-
constructor(paths, llmConfig, logger) {;
|
|
5615
|
+
var CompletionService = (_class13 = class {
|
|
5616
|
+
constructor(paths, llmConfig, logger) {;_class13.prototype.__init22.call(this);_class13.prototype.__init23.call(this);_class13.prototype.__init24.call(this);_class13.prototype.__init25.call(this);
|
|
3864
5617
|
this.paths = paths;
|
|
3865
5618
|
this.llmConfig = llmConfig;
|
|
3866
5619
|
this.logger = logger;
|
|
@@ -3868,10 +5621,10 @@ var CompletionService = (_class5 = class {
|
|
|
3868
5621
|
|
|
3869
5622
|
|
|
3870
5623
|
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
5624
|
+
__init22() {this.apiKeyPool = null}
|
|
5625
|
+
__init23() {this.usageRecorder = null}
|
|
5626
|
+
__init24() {this.usageEventSink = null}
|
|
5627
|
+
__init25() {this.visionFallbackProvider = null}
|
|
3875
5628
|
/**
|
|
3876
5629
|
* Set the API key pool service for multi-key load balancing.
|
|
3877
5630
|
* When set, keys are resolved via the pool instead of directly from the provider.
|
|
@@ -3893,8 +5646,8 @@ var CompletionService = (_class5 = class {
|
|
|
3893
5646
|
* emission is unconditional in production. Optional — when unset (unit-test
|
|
3894
5647
|
* constructors), the emit calls no-op, identical to `usageRecorder`.
|
|
3895
5648
|
*/
|
|
3896
|
-
setUsageEventSink(
|
|
3897
|
-
this.usageEventSink =
|
|
5649
|
+
setUsageEventSink(sink2) {
|
|
5650
|
+
this.usageEventSink = sink2;
|
|
3898
5651
|
}
|
|
3899
5652
|
/**
|
|
3900
5653
|
* Set the vision-fallback provider used by `applyVisionFallback` to describe
|
|
@@ -3921,8 +5674,8 @@ var CompletionService = (_class5 = class {
|
|
|
3921
5674
|
options.providerId,
|
|
3922
5675
|
options.model
|
|
3923
5676
|
);
|
|
3924
|
-
const actualProviderId = _optionalChain([routedInfo, 'optionalAccess',
|
|
3925
|
-
const actualModel = _optionalChain([routedInfo, 'optionalAccess',
|
|
5677
|
+
const actualProviderId = _optionalChain([routedInfo, 'optionalAccess', _374 => _374.actualProviderId]) || options.providerId;
|
|
5678
|
+
const actualModel = _optionalChain([routedInfo, 'optionalAccess', _375 => _375.actualModelId]) || options.model;
|
|
3926
5679
|
const provider = await this.getProvider(actualProviderId);
|
|
3927
5680
|
if (!provider) {
|
|
3928
5681
|
return { success: false, error: `Provider not found: ${actualProviderId}` };
|
|
@@ -3955,7 +5708,7 @@ var CompletionService = (_class5 = class {
|
|
|
3955
5708
|
this.apiKeyPool.reportSuccess(options.sessionId);
|
|
3956
5709
|
}
|
|
3957
5710
|
if (result.success && result.usage && options.sessionId) {
|
|
3958
|
-
_optionalChain([this, 'access',
|
|
5711
|
+
_optionalChain([this, 'access', _376 => _376.usageEventSink, 'optionalAccess', _377 => _377.emit, 'call', _378 => _378({
|
|
3959
5712
|
sessionId: options.sessionId,
|
|
3960
5713
|
modelId: actualModel,
|
|
3961
5714
|
usage: result.usage,
|
|
@@ -3984,8 +5737,8 @@ var CompletionService = (_class5 = class {
|
|
|
3984
5737
|
options.model
|
|
3985
5738
|
);
|
|
3986
5739
|
this.logger.debug("Resolved routed model", { routedInfo });
|
|
3987
|
-
const actualProviderId = _optionalChain([routedInfo, 'optionalAccess',
|
|
3988
|
-
const actualModel = _optionalChain([routedInfo, 'optionalAccess',
|
|
5740
|
+
const actualProviderId = _optionalChain([routedInfo, 'optionalAccess', _379 => _379.actualProviderId]) || options.providerId;
|
|
5741
|
+
const actualModel = _optionalChain([routedInfo, 'optionalAccess', _380 => _380.actualModelId]) || options.model;
|
|
3989
5742
|
this.logger.debug("Resolved provider and model", { actualProviderId, actualModel });
|
|
3990
5743
|
const provider = await this.getProvider(actualProviderId);
|
|
3991
5744
|
this.logger.debug("Retrieved provider", provider ? {
|
|
@@ -3997,22 +5750,22 @@ var CompletionService = (_class5 = class {
|
|
|
3997
5750
|
enabled: provider.enabled
|
|
3998
5751
|
} : { error: "Provider not found" });
|
|
3999
5752
|
if (!provider) {
|
|
4000
|
-
_optionalChain([callbacks, 'access',
|
|
5753
|
+
_optionalChain([callbacks, 'access', _381 => _381.onError, 'optionalCall', _382 => _382(`Provider not found: ${actualProviderId}`)]);
|
|
4001
5754
|
return;
|
|
4002
5755
|
}
|
|
4003
5756
|
if (!provider.enabled) {
|
|
4004
|
-
_optionalChain([callbacks, 'access',
|
|
5757
|
+
_optionalChain([callbacks, 'access', _383 => _383.onError, 'optionalCall', _384 => _384(`Provider is disabled: ${provider.name}`)]);
|
|
4005
5758
|
return;
|
|
4006
5759
|
}
|
|
4007
5760
|
const apiKey = await this.resolveApiKeyForRequest(provider, actualProviderId, options.sessionId);
|
|
4008
5761
|
if (!apiKey) {
|
|
4009
|
-
_optionalChain([callbacks, 'access',
|
|
5762
|
+
_optionalChain([callbacks, 'access', _385 => _385.onError, 'optionalCall', _386 => _386("API key not configured")]);
|
|
4010
5763
|
return;
|
|
4011
5764
|
}
|
|
4012
5765
|
const apiFormat = _chunkA2YMUCBTcjs.resolveApiFormat.call(void 0, provider);
|
|
4013
5766
|
this.logger.info("Using API format for stream", { apiFormat });
|
|
4014
5767
|
const messageId = `msg_${Date.now()}`;
|
|
4015
|
-
_optionalChain([callbacks, 'access',
|
|
5768
|
+
_optionalChain([callbacks, 'access', _387 => _387.onStart, 'optionalCall', _388 => _388(messageId)]);
|
|
4016
5769
|
const resolvedOptions = { ...options, model: actualModel };
|
|
4017
5770
|
if (options.sessionId) {
|
|
4018
5771
|
const sid = options.sessionId;
|
|
@@ -4021,14 +5774,14 @@ var CompletionService = (_class5 = class {
|
|
|
4021
5774
|
...callbacks,
|
|
4022
5775
|
onDone: (message, usage, metrics) => {
|
|
4023
5776
|
if (usage) {
|
|
4024
|
-
_optionalChain([this, 'access',
|
|
5777
|
+
_optionalChain([this, 'access', _389 => _389.usageEventSink, 'optionalAccess', _390 => _390.emit, 'call', _391 => _391({
|
|
4025
5778
|
sessionId: sid,
|
|
4026
5779
|
modelId: actualModel,
|
|
4027
5780
|
usage,
|
|
4028
5781
|
engineOrigin: "completion"
|
|
4029
5782
|
})]);
|
|
4030
5783
|
}
|
|
4031
|
-
_optionalChain([userOnDone, 'optionalCall',
|
|
5784
|
+
_optionalChain([userOnDone, 'optionalCall', _392 => _392(message, usage, metrics)]);
|
|
4032
5785
|
}
|
|
4033
5786
|
};
|
|
4034
5787
|
}
|
|
@@ -4044,11 +5797,11 @@ var CompletionService = (_class5 = class {
|
|
|
4044
5797
|
retryState.error = { status, message: error };
|
|
4045
5798
|
return;
|
|
4046
5799
|
}
|
|
4047
|
-
_optionalChain([callbacks, 'access',
|
|
5800
|
+
_optionalChain([callbacks, 'access', _393 => _393.onError, 'optionalCall', _394 => _394(error)]);
|
|
4048
5801
|
},
|
|
4049
5802
|
onDone: (message, usage, metrics) => {
|
|
4050
5803
|
this.apiKeyPool.reportSuccess(options.sessionId);
|
|
4051
|
-
_optionalChain([callbacks, 'access',
|
|
5804
|
+
_optionalChain([callbacks, 'access', _395 => _395.onDone, 'optionalCall', _396 => _396(message, usage, metrics)]);
|
|
4052
5805
|
}
|
|
4053
5806
|
};
|
|
4054
5807
|
await this.callStreamHandler(apiFormat, provider, apiKey, resolvedOptions, messageId, interceptCallbacks);
|
|
@@ -4069,7 +5822,7 @@ var CompletionService = (_class5 = class {
|
|
|
4069
5822
|
// don't fire onStart again
|
|
4070
5823
|
});
|
|
4071
5824
|
} else {
|
|
4072
|
-
_optionalChain([callbacks, 'access',
|
|
5825
|
+
_optionalChain([callbacks, 'access', _397 => _397.onError, 'optionalCall', _398 => _398(retryState.error.message)]);
|
|
4073
5826
|
}
|
|
4074
5827
|
}
|
|
4075
5828
|
} else {
|
|
@@ -4082,7 +5835,7 @@ var CompletionService = (_class5 = class {
|
|
|
4082
5835
|
} catch (error) {
|
|
4083
5836
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
4084
5837
|
this.logger.error("Stream completion error", error instanceof Error ? error : void 0, { message });
|
|
4085
|
-
_optionalChain([callbacks, 'access',
|
|
5838
|
+
_optionalChain([callbacks, 'access', _399 => _399.onError, 'optionalCall', _400 => _400(message)]);
|
|
4086
5839
|
}
|
|
4087
5840
|
}
|
|
4088
5841
|
/**
|
|
@@ -4122,7 +5875,7 @@ var CompletionService = (_class5 = class {
|
|
|
4122
5875
|
return {
|
|
4123
5876
|
success: true,
|
|
4124
5877
|
message: "Connection successful",
|
|
4125
|
-
response: _optionalChain([result, 'access',
|
|
5878
|
+
response: _optionalChain([result, 'access', _401 => _401.message, 'optionalAccess', _402 => _402.content]) || "",
|
|
4126
5879
|
model: modelId,
|
|
4127
5880
|
durationMs
|
|
4128
5881
|
};
|
|
@@ -4187,7 +5940,7 @@ ${description}`;
|
|
|
4187
5940
|
* 3. Legacy single key from provider config
|
|
4188
5941
|
*/
|
|
4189
5942
|
async resolveApiKeyForRequest(provider, providerId, sessionId) {
|
|
4190
|
-
if (_optionalChain([provider, 'access',
|
|
5943
|
+
if (_optionalChain([provider, 'access', _403 => _403.codingPlan, 'optionalAccess', _404 => _404.enabled]) && provider.codingPlan.apiKey) {
|
|
4191
5944
|
return this.resolveApiKey(provider.codingPlan.apiKey);
|
|
4192
5945
|
}
|
|
4193
5946
|
if (this.apiKeyPool) {
|
|
@@ -4352,7 +6105,50 @@ ${description}`;
|
|
|
4352
6105
|
builtinExecutor
|
|
4353
6106
|
);
|
|
4354
6107
|
}
|
|
4355
|
-
},
|
|
6108
|
+
}, _class13);
|
|
6109
|
+
|
|
6110
|
+
|
|
6111
|
+
|
|
6112
|
+
|
|
6113
|
+
|
|
6114
|
+
|
|
6115
|
+
|
|
6116
|
+
|
|
6117
|
+
|
|
6118
|
+
|
|
6119
|
+
|
|
6120
|
+
|
|
6121
|
+
|
|
6122
|
+
|
|
6123
|
+
|
|
6124
|
+
|
|
6125
|
+
|
|
6126
|
+
|
|
6127
|
+
|
|
6128
|
+
|
|
6129
|
+
|
|
6130
|
+
|
|
6131
|
+
|
|
6132
|
+
|
|
6133
|
+
|
|
6134
|
+
|
|
6135
|
+
|
|
6136
|
+
|
|
6137
|
+
|
|
6138
|
+
|
|
6139
|
+
|
|
6140
|
+
|
|
6141
|
+
|
|
6142
|
+
|
|
6143
|
+
|
|
6144
|
+
|
|
6145
|
+
|
|
6146
|
+
|
|
6147
|
+
|
|
6148
|
+
|
|
6149
|
+
|
|
6150
|
+
|
|
6151
|
+
|
|
4356
6152
|
|
|
4357
6153
|
|
|
4358
6154
|
|
|
@@ -4397,4 +6193,4 @@ ${description}`;
|
|
|
4397
6193
|
|
|
4398
6194
|
|
|
4399
6195
|
|
|
4400
|
-
exports.CompletionService = CompletionService; exports.register = register; exports.emit = emit; exports.attach = attach; exports.release = release; exports.convertMessageToOpenAI = convertMessageToOpenAI; exports.convertMessageToAnthropic = convertMessageToAnthropic; exports.convertMessageToGemini = convertMessageToGemini; exports.getOpenRouterProviderConfig = getOpenRouterProviderConfig; exports.addOpenRouterProviderToRequest = addOpenRouterProviderToRequest; exports.DEFAULT_ROUTE_IDLE_MS = DEFAULT_ROUTE_IDLE_MS; exports.ProviderProxyRouteMap = ProviderProxyRouteMap; exports.getSharedExecutor = getSharedExecutor; exports.getResponsesEndpointTransformer = getResponsesEndpointTransformer; exports.getAnthropicEndpointTransformer = getAnthropicEndpointTransformer; exports.getGeminiEndpointTransformer = getGeminiEndpointTransformer; exports.readBody = readBody; exports.relayResponse = relayResponse; exports.writeError = writeError; exports.resolvePoolBoundKey = resolvePoolBoundKey; exports.extractRouteToken = extractRouteToken; exports.isLoopbackAddress = isLoopbackAddress; exports.ProviderProxy = ProviderProxy; exports.getProviderProxy = getProviderProxy; exports.__resetProviderProxyForTests = __resetProviderProxyForTests; exports.
|
|
6196
|
+
exports.CompletionService = CompletionService; exports.register = register; exports.emit = emit; exports.attach = attach; exports.release = release; exports.convertMessageToOpenAI = convertMessageToOpenAI; exports.convertMessageToAnthropic = convertMessageToAnthropic; exports.convertMessageToGemini = convertMessageToGemini; exports.getOpenRouterProviderConfig = getOpenRouterProviderConfig; exports.addOpenRouterProviderToRequest = addOpenRouterProviderToRequest; exports.DEFAULT_ROUTE_IDLE_MS = DEFAULT_ROUTE_IDLE_MS; exports.ProviderProxyRouteMap = ProviderProxyRouteMap; exports.getSharedExecutor = getSharedExecutor; exports.getResponsesEndpointTransformer = getResponsesEndpointTransformer; exports.getAnthropicEndpointTransformer = getAnthropicEndpointTransformer; exports.getGeminiEndpointTransformer = getGeminiEndpointTransformer; exports.readBody = readBody; exports.relayResponse = relayResponse; exports.writeError = writeError; exports.resolvePoolBoundKey = resolvePoolBoundKey; exports.extractRouteToken = extractRouteToken; exports.isLoopbackAddress = isLoopbackAddress; exports.ProviderProxy = ProviderProxy; exports.getProviderProxy = getProviderProxy; exports.__resetProviderProxyForTests = __resetProviderProxyForTests; exports.KeyedMutex = KeyedMutex; exports.OUTBOUND_API_SERVER_CONFIG_KEY = OUTBOUND_API_SERVER_CONFIG_KEY; exports.DEFAULT_USER_MESSAGE_QUEUE = DEFAULT_USER_MESSAGE_QUEUE; exports.DEFAULT_CONCURRENCY_QUEUE = DEFAULT_CONCURRENCY_QUEUE; exports.DEFAULT_ACCOUNT_PROBE = DEFAULT_ACCOUNT_PROBE; exports.normalizeAccountProbe = normalizeAccountProbe; exports.normalizeAudit = normalizeAudit; exports.normalizeBilling = normalizeBilling; exports.DEFAULT_FINGERPRINT = DEFAULT_FINGERPRINT; exports.normalizeFingerprint = normalizeFingerprint; exports.normalizeVoucher = normalizeVoucher; exports.normalizeProxyConfig = normalizeProxyConfig; exports.normalizeProxySegment = normalizeProxySegment; exports.normalizeWebhookDestination = normalizeWebhookDestination; exports.normalizeWebhookSegment = normalizeWebhookSegment; exports.normalizeQueueSegments = normalizeQueueSegments; exports.normalizePrefixTargets = normalizePrefixTargets; exports.defaultServerConfig = defaultServerConfig; exports.normalizeServerConfig = normalizeServerConfig; exports.loadServerConfig = loadServerConfig; exports.saveServerConfig = saveServerConfig; exports.mergeServerConfig = mergeServerConfig; exports.AUDIT_REDACTED = AUDIT_REDACTED; exports.redactAuditText = redactAuditText; exports.beginAuditCapture = beginAuditCapture; exports.beginBillingCapture = beginBillingCapture; exports.computeKeyExpiry = computeKeyExpiry; exports.checkKeyQuota = checkKeyQuota; exports.randomBase62 = randomBase62; exports.hashKey = hashKey; exports.createNamedKey = createNamedKey; exports.verifyKey = verifyKey; exports.verifyPresentedKey = verifyPresentedKey; exports.ConcurrencyQueueFullError = ConcurrencyQueueFullError; exports.ConcurrencyWaitTimeoutError = ConcurrencyWaitTimeoutError; exports.ConcurrencyWaitCancelledError = ConcurrencyWaitCancelledError; exports.isConcurrencyRejection = isConcurrencyRejection; exports.OutboundConcurrencyGate = OutboundConcurrencyGate; exports.OutboundRateLimiter = OutboundRateLimiter; exports.isUserMessageRequest = isUserMessageRequest; exports.SerialQueueTimeoutError = SerialQueueTimeoutError; exports.isSerialQueueTimeout = isSerialQueueTimeout; exports.UserMessageSerialQueue = UserMessageSerialQueue; exports.generateVoucherCode = generateVoucherCode; exports.hashVoucherCode = hashVoucherCode; exports.voucherCodePrefix = voucherCodePrefix; exports.newVoucherId = newVoucherId; exports.computeVoucherGrant = computeVoucherGrant; exports.toVoucherInfo = toVoucherInfo; exports.isRedeemRequest = isRedeemRequest; exports.handleVoucherRedeem = handleVoucherRedeem; exports.DEFAULT_OUTBOUND_PORT = DEFAULT_OUTBOUND_PORT; exports.OutboundApiConfigError = OutboundApiConfigError; exports.OutboundApiServer = OutboundApiServer; exports.formatUrls = formatUrls; exports.startOfLocalDay = startOfLocalDay; exports.startOfLocalWeek = startOfLocalWeek; exports.KeySpendTracker = KeySpendTracker; exports.getOutboundApiServer = getOutboundApiServer; exports.__resetOutboundApiServerForTests = __resetOutboundApiServerForTests;
|