@omnicross/core 0.1.5 → 0.1.7
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/ApiConverter.cjs +2 -2
- package/dist/ApiConverter.js +1 -1
- package/dist/{CompletionService-DdWaAxq0.d.ts → CompletionService-C3BX0Ovy.d.ts} +4 -3
- package/dist/{CompletionService-D3a47vdU.d.cts → CompletionService-kNA17cfI.d.cts} +4 -3
- package/dist/auth/GeminiCodeAssistProjectResolver.cjs +8 -6
- package/dist/auth/GeminiCodeAssistProjectResolver.js +6 -4
- package/dist/{chunk-3Z5BQZFM.cjs → chunk-235MXAGX.cjs} +3 -1
- package/dist/{chunk-HRRYG2AX.js → chunk-23S5OPIO.js} +10 -1
- package/dist/chunk-25GXJCEZ.cjs +40 -0
- package/dist/chunk-2E42SAW3.cjs +323 -0
- package/dist/chunk-2JYRIADS.js +40 -0
- package/dist/chunk-2Z4XM3H5.cjs +44 -0
- package/dist/{chunk-BODMAL6G.js → chunk-32O35CIF.js} +271 -91
- package/dist/chunk-3SCOLIPT.cjs +63 -0
- package/dist/{chunk-SYQF7OIR.js → chunk-3WD4HTR2.js} +2 -0
- package/dist/{chunk-CGKLE6IW.cjs → chunk-4BB343BI.cjs} +1489 -520
- package/dist/{chunk-VACWOIRE.cjs → chunk-4VZX5F4T.cjs} +39 -15
- package/dist/{chunk-5ZQBEOMD.js → chunk-5NJLQ6T6.js} +46 -12
- package/dist/chunk-5RSZYQJH.cjs +62 -0
- package/dist/chunk-6QQG7YG3.cjs +153 -0
- package/dist/{chunk-IFXJ2BG7.js → chunk-7C7DET6S.js} +51 -6
- package/dist/{chunk-IK3KE3AU.js → chunk-DE2SRBFZ.js} +1 -1
- package/dist/{chunk-FH2P4OCD.js → chunk-E3COOKUP.js} +1240 -271
- package/dist/{chunk-UHGVBFU3.js → chunk-EBLFCNPY.js} +38 -14
- package/dist/chunk-FU7EAR73.js +62 -0
- package/dist/chunk-FXR6N3SR.js +323 -0
- package/dist/{chunk-SUEGJ75D.cjs → chunk-GDIXXKF2.cjs} +46 -12
- package/dist/chunk-GFIZ6LQI.js +170 -0
- package/dist/chunk-GL6XM527.js +61 -0
- package/dist/chunk-HVBBDBS3.cjs +350 -0
- package/dist/{chunk-46ZIFWYG.cjs → chunk-IIRPUVPH.cjs} +7 -1
- package/dist/chunk-J3QQ6JS5.cjs +170 -0
- package/dist/{chunk-JWVTXXPX.cjs → chunk-KBM2W6UY.cjs} +8 -4
- package/dist/chunk-LB6EBS6Y.js +69 -0
- package/dist/{chunk-IWQAX3CS.js → chunk-LVUCAWMK.js} +1 -1
- package/dist/chunk-MKF46GNZ.js +350 -0
- package/dist/{chunk-HYN75H6G.cjs → chunk-MUHWHQVM.cjs} +1 -1
- package/dist/chunk-MYYR3HZL.js +44 -0
- package/dist/{chunk-MFZ5437A.js → chunk-N5T4M4GU.js} +1 -1
- package/dist/{chunk-5ENKBSWO.js → chunk-N7OOJI5V.js} +88 -8
- package/dist/chunk-NKVZ6Z3Y.cjs +61 -0
- package/dist/chunk-OF3BECU2.cjs +69 -0
- package/dist/{chunk-SNYBEXLB.cjs → chunk-OJGQKHN5.cjs} +3 -1
- package/dist/{chunk-DDMRUL76.cjs → chunk-OKMJLEXX.cjs} +5 -5
- package/dist/chunk-OPRS4IQZ.js +153 -0
- package/dist/chunk-OTZC4TNH.cjs +79 -0
- package/dist/{chunk-3Q6Z6JTO.cjs → chunk-OVQBNVSV.cjs} +287 -107
- package/dist/{chunk-4XMYPW46.cjs → chunk-OXHNSZ53.cjs} +84 -44
- package/dist/{chunk-C6OV7GCC.js → chunk-PEGTIPB3.js} +52 -12
- package/dist/chunk-PO3XB56Z.js +79 -0
- package/dist/{chunk-D3TDKWRP.js → chunk-RJ5ZGNI3.js} +54 -5
- package/dist/{chunk-PG7PS2NN.cjs → chunk-RJUONLUQ.cjs} +59 -10
- package/dist/{chunk-BPKCU575.js → chunk-SOO562NM.js} +3 -1
- package/dist/{chunk-6QZGDLL4.js → chunk-TOVPMMHM.js} +5 -1
- package/dist/{chunk-K52THW2C.cjs → chunk-V4TXMEJS.cjs} +5 -5
- package/dist/{chunk-G2UTNSGU.js → chunk-WNKWAEUR.js} +7 -1
- package/dist/{chunk-UNWKG6EI.cjs → chunk-XPIOJQS2.cjs} +51 -6
- package/dist/chunk-XXFYLL7R.js +63 -0
- package/dist/{chunk-KFI44N2R.cjs → chunk-YNJNTCSQ.cjs} +10 -1
- package/dist/{chunk-VQY5W4YW.cjs → chunk-YWL7YZWS.cjs} +90 -10
- package/dist/completion/ApiKeyPoolService.cjs +12 -0
- package/dist/completion/ApiKeyPoolService.d.cts +6 -0
- package/dist/completion/ApiKeyPoolService.d.ts +6 -0
- package/dist/completion/ApiKeyPoolService.js +12 -0
- package/dist/completion/CompletionService.cjs +30 -24
- package/dist/completion/CompletionService.d.cts +3 -1
- package/dist/completion/CompletionService.d.ts +3 -1
- package/dist/completion/CompletionService.js +29 -23
- package/dist/completion.cjs +32 -26
- package/dist/completion.d.cts +3 -1
- package/dist/completion.d.ts +3 -1
- package/dist/completion.js +31 -25
- package/dist/index.cjs +56 -29
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +61 -34
- package/dist/{keyPolicy-Cp0FntZl.d.cts → keyPolicy-zdWTu3CQ.d.cts} +1 -1
- package/dist/{keyPolicy-Cp0FntZl.d.ts → keyPolicy-zdWTu3CQ.d.ts} +1 -1
- package/dist/outbound-api/auditCapture.cjs +2 -2
- package/dist/outbound-api/auditCapture.d.cts +3 -4
- package/dist/outbound-api/auditCapture.d.ts +3 -4
- package/dist/outbound-api/auditCapture.js +1 -1
- package/dist/outbound-api/quotaWarn.d.cts +1 -1
- package/dist/outbound-api/quotaWarn.d.ts +1 -1
- package/dist/outbound-api/routeResolver.cjs +2 -2
- package/dist/outbound-api/routeResolver.d.cts +5 -3
- package/dist/outbound-api/routeResolver.d.ts +5 -3
- package/dist/outbound-api/routeResolver.js +1 -1
- package/dist/outbound-api/subscriptionRegistryPort.d.cts +2 -0
- package/dist/outbound-api/subscriptionRegistryPort.d.ts +2 -0
- package/dist/outbound-api/types.d.cts +4 -2
- package/dist/outbound-api/types.d.ts +4 -2
- package/dist/outbound-api.cjs +52 -25
- package/dist/outbound-api.d.cts +131 -87
- package/dist/outbound-api.d.ts +131 -87
- package/dist/outbound-api.js +55 -28
- package/dist/pipeline/AccountAllowanceScheduling.cjs +15 -0
- package/dist/pipeline/AccountAllowanceScheduling.d.cts +91 -0
- package/dist/pipeline/AccountAllowanceScheduling.d.ts +91 -0
- package/dist/pipeline/AccountAllowanceScheduling.js +15 -0
- package/dist/pipeline/AccountAllowanceStore.cjs +20 -0
- package/dist/pipeline/AccountAllowanceStore.d.cts +77 -0
- package/dist/pipeline/AccountAllowanceStore.d.ts +77 -0
- package/dist/pipeline/AccountAllowanceStore.js +20 -0
- package/dist/pipeline/AccountRouteActivity.cjs +10 -0
- package/dist/pipeline/AccountRouteActivity.d.cts +61 -0
- package/dist/pipeline/AccountRouteActivity.d.ts +61 -0
- package/dist/pipeline/AccountRouteActivity.js +10 -0
- package/dist/pipeline/AuthSource.d.cts +11 -2
- package/dist/pipeline/AuthSource.d.ts +11 -2
- package/dist/pipeline/BoundAccountSelectionError.cjs +10 -0
- package/dist/pipeline/BoundAccountSelectionError.d.cts +28 -0
- package/dist/pipeline/BoundAccountSelectionError.d.ts +28 -0
- package/dist/pipeline/BoundAccountSelectionError.js +10 -0
- package/dist/pipeline/LlmConfigProviderAuth.d.cts +1 -0
- package/dist/pipeline/LlmConfigProviderAuth.d.ts +1 -0
- package/dist/pipeline/ServerOverloadCounter.cjs +16 -0
- package/dist/pipeline/ServerOverloadCounter.d.cts +77 -0
- package/dist/pipeline/ServerOverloadCounter.d.ts +77 -0
- package/dist/pipeline/ServerOverloadCounter.js +16 -0
- package/dist/pipeline/SubscriptionAccountHealth.cjs +6 -2
- package/dist/pipeline/SubscriptionAccountHealth.d.cts +65 -8
- package/dist/pipeline/SubscriptionAccountHealth.d.ts +65 -8
- package/dist/pipeline/SubscriptionAccountHealth.js +5 -1
- package/dist/pipeline/SubscriptionAuthSource.cjs +5 -2
- package/dist/pipeline/SubscriptionAuthSource.d.cts +1 -0
- package/dist/pipeline/SubscriptionAuthSource.d.ts +1 -0
- package/dist/pipeline/SubscriptionAuthSource.js +4 -1
- package/dist/pipeline/SubscriptionAuthStrategy.d.cts +8 -3
- package/dist/pipeline/SubscriptionAuthStrategy.d.ts +8 -3
- package/dist/pipeline/executeProviderCall.cjs +2 -2
- package/dist/pipeline/executeProviderCall.d.cts +8 -3
- package/dist/pipeline/executeProviderCall.d.ts +8 -3
- package/dist/pipeline/executeProviderCall.js +1 -1
- package/dist/pipeline/resolveProviderChain.d.cts +2 -3
- package/dist/pipeline/resolveProviderChain.d.ts +2 -3
- package/dist/pipeline/resolveSubscriptionChain.d.cts +1 -0
- package/dist/pipeline/resolveSubscriptionChain.d.ts +1 -0
- package/dist/pipeline/upstreamFetch.cjs +5 -3
- package/dist/pipeline/upstreamFetch.d.cts +35 -6
- package/dist/pipeline/upstreamFetch.d.ts +35 -6
- package/dist/pipeline/upstreamFetch.js +4 -2
- package/dist/pipeline/upstreamTrace.cjs +4 -2
- package/dist/pipeline/upstreamTrace.d.cts +22 -8
- package/dist/pipeline/upstreamTrace.d.ts +22 -8
- package/dist/pipeline/upstreamTrace.js +3 -1
- package/dist/ports/pricing-store.d.cts +4 -3
- package/dist/ports/pricing-store.d.ts +4 -3
- package/dist/ports.d.cts +4 -3
- package/dist/ports.d.ts +4 -3
- package/dist/provider-proxy/ProviderProxy.cjs +30 -24
- package/dist/provider-proxy/ProviderProxy.d.cts +2 -0
- package/dist/provider-proxy/ProviderProxy.d.ts +2 -0
- package/dist/provider-proxy/ProviderProxy.js +29 -23
- package/dist/provider-proxy/identity/codexCliHeaders.cjs +10 -0
- package/dist/provider-proxy/identity/codexCliHeaders.d.cts +45 -0
- package/dist/provider-proxy/identity/codexCliHeaders.d.ts +45 -0
- package/dist/provider-proxy/identity/codexCliHeaders.js +10 -0
- package/dist/provider-proxy/ingress/providerProxyShared.cjs +32 -24
- package/dist/provider-proxy/ingress/providerProxyShared.d.cts +44 -5
- package/dist/provider-proxy/ingress/providerProxyShared.d.ts +44 -5
- package/dist/provider-proxy/ingress/providerProxyShared.js +31 -23
- package/dist/provider-proxy/matchText.cjs +4 -2
- package/dist/provider-proxy/matchText.d.cts +39 -6
- package/dist/provider-proxy/matchText.d.ts +39 -6
- package/dist/provider-proxy/matchText.js +3 -1
- package/dist/provider-proxy/types.d.cts +35 -2
- package/dist/provider-proxy/types.d.ts +35 -2
- package/dist/provider-proxy.cjs +30 -24
- package/dist/provider-proxy.d.cts +2 -0
- package/dist/provider-proxy.d.ts +2 -0
- package/dist/provider-proxy.js +29 -23
- package/dist/{routeResolver-C4T7yClx.d.ts → routeResolver-BxOfunbm.d.ts} +1 -1
- package/dist/{routeResolver-CB3tvJKy.d.cts → routeResolver-D5OzD0iM.d.cts} +1 -1
- package/dist/transformer/TransformerChainExecutor.cjs +2 -2
- package/dist/transformer/TransformerChainExecutor.js +1 -1
- package/dist/transformer/transformers/AnthropicTransformer.cjs +3 -2
- package/dist/transformer/transformers/AnthropicTransformer.js +2 -1
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +3 -3
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +2 -2
- package/dist/transformer/transformers/GeminiTransformer.cjs +3 -3
- package/dist/transformer/transformers/GeminiTransformer.js +2 -2
- package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +3 -2
- package/dist/transformer/transformers/OpenAIResponseTransformer.d.cts +1 -1
- package/dist/transformer/transformers/OpenAIResponseTransformer.d.ts +1 -1
- package/dist/transformer/transformers/OpenAIResponseTransformer.js +2 -1
- package/dist/transformer/transformers/OpenAITransformer.cjs +6 -0
- package/dist/transformer/transformers/OpenAITransformer.d.cts +68 -0
- package/dist/transformer/transformers/OpenAITransformer.d.ts +68 -0
- package/dist/transformer/transformers/OpenAITransformer.js +6 -0
- package/dist/transformer/transformers.cjs +9 -14
- package/dist/transformer/transformers.d.cts +12 -48
- package/dist/transformer/transformers.d.ts +12 -48
- package/dist/transformer/transformers.js +11 -16
- package/dist/transformer/types.d.cts +17 -0
- package/dist/transformer/types.d.ts +17 -0
- package/dist/transformer.cjs +12 -12
- package/dist/transformer.d.cts +1 -2
- package/dist/transformer.d.ts +1 -2
- package/dist/transformer.js +12 -12
- package/dist/{types-lgPYQuZS.d.ts → types-CqZD77rS.d.ts} +173 -7
- package/dist/{types-B8Arpxn0.d.cts → types-gjJ5lJ5n.d.cts} +173 -7
- package/dist/usage/pricing-engine.cjs +2 -2
- package/dist/usage/pricing-engine.d.cts +29 -1
- package/dist/usage/pricing-engine.d.ts +29 -1
- package/dist/usage/pricing-engine.js +1 -1
- package/dist/usage/usage-recorder.cjs +2 -2
- package/dist/usage/usage-recorder.d.cts +5 -1
- package/dist/usage/usage-recorder.d.ts +5 -1
- package/dist/usage/usage-recorder.js +1 -1
- package/dist/usage.cjs +3 -3
- package/dist/usage.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-7FEBOPJO.cjs +0 -50
- package/dist/chunk-DWKQZ2SK.cjs +0 -254
- package/dist/chunk-IAXWICZU.js +0 -69
- package/dist/chunk-IB3FDI4O.js +0 -50
- package/dist/chunk-OQWKZCXP.js +0 -242
- package/dist/chunk-QWBWGODS.cjs +0 -69
- package/dist/chunk-YMFAJFQT.cjs +0 -173
- package/dist/chunk-ZCIQ3L3R.cjs +0 -242
- package/dist/chunk-ZJZAOKI7.js +0 -254
- package/dist/chunk-ZLX3KGXX.js +0 -173
- package/dist/transformer/transformers/OpenCodeGoTransformer.cjs +0 -6
- package/dist/transformer/transformers/OpenCodeGoTransformer.d.cts +0 -51
- package/dist/transformer/transformers/OpenCodeGoTransformer.d.ts +0 -51
- package/dist/transformer/transformers/OpenCodeGoTransformer.js +0 -6
- package/dist/transformer/transformers/ReasoningTransformer.cjs +0 -6
- package/dist/transformer/transformers/ReasoningTransformer.d.cts +0 -47
- package/dist/transformer/transformers/ReasoningTransformer.d.ts +0 -47
- package/dist/transformer/transformers/ReasoningTransformer.js +0 -6
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AnthropicTransformer
|
|
3
|
+
} from "./chunk-PEGTIPB3.js";
|
|
4
|
+
import {
|
|
5
|
+
GeminiTransformer
|
|
6
|
+
} from "./chunk-LVUCAWMK.js";
|
|
7
|
+
import {
|
|
8
|
+
OpenAIResponseTransformer
|
|
9
|
+
} from "./chunk-32O35CIF.js";
|
|
10
|
+
import {
|
|
11
|
+
OpenAITransformer
|
|
12
|
+
} from "./chunk-LB6EBS6Y.js";
|
|
13
|
+
import {
|
|
14
|
+
GeminiCodeAssistTransformer
|
|
15
|
+
} from "./chunk-N5T4M4GU.js";
|
|
16
|
+
|
|
17
|
+
// src/transformer/transformers/index.ts
|
|
18
|
+
var BuiltinTransformers = {
|
|
19
|
+
OpenAITransformer,
|
|
20
|
+
GeminiTransformer,
|
|
21
|
+
GeminiCodeAssistTransformer,
|
|
22
|
+
AnthropicTransformer,
|
|
23
|
+
OpenAIResponseTransformer
|
|
24
|
+
};
|
|
25
|
+
function getBuiltinTransformers() {
|
|
26
|
+
return BuiltinTransformers;
|
|
27
|
+
}
|
|
28
|
+
var BUILTIN_TRANSFORMER_NAMES = [
|
|
29
|
+
"openai",
|
|
30
|
+
"gemini",
|
|
31
|
+
"gemini-code-assist",
|
|
32
|
+
"anthropic",
|
|
33
|
+
"openai-response"
|
|
34
|
+
];
|
|
35
|
+
async function registerBuiltinTransformers(service) {
|
|
36
|
+
await service.initialize(getBuiltinTransformers());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
BuiltinTransformers,
|
|
41
|
+
getBuiltinTransformers,
|
|
42
|
+
BUILTIN_TRANSFORMER_NAMES,
|
|
43
|
+
registerBuiltinTransformers
|
|
44
|
+
};
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
transformRequestOut,
|
|
4
4
|
transformResponseIn,
|
|
5
5
|
transformResponseOut
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TOVPMMHM.js";
|
|
7
7
|
|
|
8
8
|
// src/transformer/transformers/GeminiCodeAssistTransformer.ts
|
|
9
9
|
var DEFAULT_CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
|
|
@@ -3,10 +3,12 @@ var OVERLOAD_TTL_MS = 10 * 6e4;
|
|
|
3
3
|
var OVERLOAD_ENABLED_DEFAULT = true;
|
|
4
4
|
var AUTH_ERROR_TTL_MS = 30 * 6e4;
|
|
5
5
|
var SERVER_ERROR_TTL_MS = 5 * 6e4;
|
|
6
|
+
var MAX_QUOTA_COOLDOWN_MS = 7 * 24 * 60 * 6e4;
|
|
6
7
|
var BAN_BODY_MARKERS = [
|
|
7
8
|
"this organization has been disabled",
|
|
8
9
|
"oauth authentication is currently not allowed"
|
|
9
10
|
];
|
|
11
|
+
var ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT = 200;
|
|
10
12
|
function readHeader(headers, name) {
|
|
11
13
|
if (typeof headers.get === "function") {
|
|
12
14
|
return headers.get(name);
|
|
@@ -37,13 +39,14 @@ function isBanBody(bodyText) {
|
|
|
37
39
|
return BAN_BODY_MARKERS.some((marker) => lower.includes(marker));
|
|
38
40
|
}
|
|
39
41
|
function isRecordEmpty(record) {
|
|
40
|
-
return record.rateLimitEndAt === void 0 && record.overloadUntil === void 0 && record.tempUnavailableUntil === void 0 && !record.blocked;
|
|
42
|
+
return record.rateLimitEndAt === void 0 && record.overloadUntil === void 0 && record.tempUnavailableUntil === void 0 && record.quotaExhaustedUntil === void 0 && !record.blocked;
|
|
41
43
|
}
|
|
42
44
|
var KEY_SEP = "\0";
|
|
43
45
|
var SubscriptionAccountHealth = class {
|
|
44
46
|
records = /* @__PURE__ */ new Map();
|
|
45
47
|
listeners = /* @__PURE__ */ new Set();
|
|
46
48
|
anomalyListeners = /* @__PURE__ */ new Set();
|
|
49
|
+
diagnostics = [];
|
|
47
50
|
now;
|
|
48
51
|
overloadEnabled;
|
|
49
52
|
overloadTtlMs;
|
|
@@ -71,8 +74,8 @@ var SubscriptionAccountHealth = class {
|
|
|
71
74
|
}
|
|
72
75
|
/**
|
|
73
76
|
* Whether an account may be scheduled RIGHT NOW. `blocked` → false; any timer
|
|
74
|
-
* still in the future → false. Expired timers are lazily deleted on read
|
|
75
|
-
*
|
|
77
|
+
* still in the future → false. Expired timers are lazily deleted on read so
|
|
78
|
+
* an elapsed cooldown restores the account WITHOUT a
|
|
76
79
|
* timer — but the recovery SIGNAL is the sweeper's job (`sweepRecoveries`), so
|
|
77
80
|
* this read never emits (correctness is independent of the tick).
|
|
78
81
|
*/
|
|
@@ -86,7 +89,7 @@ var SubscriptionAccountHealth = class {
|
|
|
86
89
|
return !active;
|
|
87
90
|
}
|
|
88
91
|
/**
|
|
89
|
-
* The single marking entry point (design D3)
|
|
92
|
+
* The single marking entry point (design D3):
|
|
90
93
|
* - 429 + authoritative reset → `rateLimitEndAt`; a non-claude 429 may use
|
|
91
94
|
* `retryAfterSeconds`; a bare 429 (no resolvable reset) is NOT marked;
|
|
92
95
|
* - 529 → `overloadUntil` (gated by `overloadEnabled`);
|
|
@@ -141,9 +144,54 @@ var SubscriptionAccountHealth = class {
|
|
|
141
144
|
}
|
|
142
145
|
if (isRecordEmpty(record)) this.records.delete(key);
|
|
143
146
|
else this.records.set(key, record);
|
|
144
|
-
if (recovered)
|
|
147
|
+
if (recovered) {
|
|
148
|
+
const recovery = {
|
|
149
|
+
providerId,
|
|
150
|
+
accountId,
|
|
151
|
+
at: now,
|
|
152
|
+
kind: "rateLimitRecovery"
|
|
153
|
+
};
|
|
154
|
+
this.recordDiagnostic({ kind: "health-recovery", providerId, accountId, at: now });
|
|
155
|
+
this.emit(recovery);
|
|
156
|
+
}
|
|
145
157
|
if (anomalyState && !wasUnhealthyBefore) {
|
|
146
|
-
|
|
158
|
+
const anomaly = { providerId, accountId, at: now, state: anomalyState };
|
|
159
|
+
this.recordDiagnostic({ kind: "health-anomaly", ...anomaly });
|
|
160
|
+
this.emitAnomaly(anomaly);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Mark an account quota/usage-exhausted until `untilEpochMs` (the Codex weekly
|
|
165
|
+
* usage-limit wall). Unlike the status-driven timers set by
|
|
166
|
+
* {@link recordUpstreamOutcome}, this mark is NOT cleared by a 2xx: in-flight
|
|
167
|
+
* sessions on the same account keep succeeding after the window is exhausted,
|
|
168
|
+
* so a traffic-driven clear would re-strand every NEW session onto this
|
|
169
|
+
* account. It excludes the account from NEW-session selection until the
|
|
170
|
+
* deadline elapses (existing sessions are unaffected — they already hold their
|
|
171
|
+
* token). Self-heals lazily on read and via {@link sweepRecoveries}.
|
|
172
|
+
* `untilEpochMs` is clamped to {@link MAX_QUOTA_COOLDOWN_MS}; an already-elapsed
|
|
173
|
+
* deadline is a no-op. Emits a `quota_exhausted` anomaly on the healthy→
|
|
174
|
+
* unhealthy edge.
|
|
175
|
+
*/
|
|
176
|
+
markQuotaExhausted(providerId, accountId, untilEpochMs, now = this.now()) {
|
|
177
|
+
if (!Number.isFinite(untilEpochMs)) return;
|
|
178
|
+
const clamped = Math.min(untilEpochMs, now + MAX_QUOTA_COOLDOWN_MS);
|
|
179
|
+
if (clamped <= now) return;
|
|
180
|
+
const key = this.key(providerId, accountId);
|
|
181
|
+
const preExisting = this.records.get(key);
|
|
182
|
+
const record = preExisting ?? {};
|
|
183
|
+
const wasUnhealthyBefore = preExisting ? this.isUnhealthyAt(preExisting, now) : false;
|
|
184
|
+
record.quotaExhaustedUntil = clamped;
|
|
185
|
+
this.records.set(key, record);
|
|
186
|
+
if (!wasUnhealthyBefore) {
|
|
187
|
+
const anomaly = {
|
|
188
|
+
providerId,
|
|
189
|
+
accountId,
|
|
190
|
+
at: now,
|
|
191
|
+
state: "quota_exhausted"
|
|
192
|
+
};
|
|
193
|
+
this.recordDiagnostic({ kind: "health-anomaly", ...anomaly });
|
|
194
|
+
this.emitAnomaly(anomaly);
|
|
147
195
|
}
|
|
148
196
|
}
|
|
149
197
|
/** Whether a record is unhealthy at `now`: blocked, or any timer still future. */
|
|
@@ -152,6 +200,7 @@ var SubscriptionAccountHealth = class {
|
|
|
152
200
|
if (record.rateLimitEndAt !== void 0 && now < record.rateLimitEndAt) return true;
|
|
153
201
|
if (record.overloadUntil !== void 0 && now < record.overloadUntil) return true;
|
|
154
202
|
if (record.tempUnavailableUntil !== void 0 && now < record.tempUnavailableUntil) return true;
|
|
203
|
+
if (record.quotaExhaustedUntil !== void 0 && now < record.quotaExhaustedUntil) return true;
|
|
155
204
|
return false;
|
|
156
205
|
}
|
|
157
206
|
/**
|
|
@@ -188,7 +237,7 @@ var SubscriptionAccountHealth = class {
|
|
|
188
237
|
const recovered = [];
|
|
189
238
|
for (const [key, record] of this.records) {
|
|
190
239
|
if (record.blocked) continue;
|
|
191
|
-
const hadTimer = record.rateLimitEndAt !== void 0 || record.overloadUntil !== void 0 || record.tempUnavailableUntil !== void 0;
|
|
240
|
+
const hadTimer = record.rateLimitEndAt !== void 0 || record.overloadUntil !== void 0 || record.tempUnavailableUntil !== void 0 || record.quotaExhaustedUntil !== void 0;
|
|
192
241
|
const active = this.clearExpired(record, now);
|
|
193
242
|
if (hadTimer && !active && isRecordEmpty(record)) {
|
|
194
243
|
const [pid, aid] = this.parseKey(key);
|
|
@@ -196,7 +245,15 @@ var SubscriptionAccountHealth = class {
|
|
|
196
245
|
this.records.delete(key);
|
|
197
246
|
}
|
|
198
247
|
}
|
|
199
|
-
for (const event of recovered)
|
|
248
|
+
for (const event of recovered) {
|
|
249
|
+
this.recordDiagnostic({
|
|
250
|
+
kind: "health-recovery",
|
|
251
|
+
providerId: event.providerId,
|
|
252
|
+
accountId: event.accountId,
|
|
253
|
+
at: event.at
|
|
254
|
+
});
|
|
255
|
+
this.emit(event);
|
|
256
|
+
}
|
|
200
257
|
return recovered;
|
|
201
258
|
}
|
|
202
259
|
/** Admin-facing coarse status + cooldown-until for one account (secret-free). */
|
|
@@ -204,6 +261,9 @@ var SubscriptionAccountHealth = class {
|
|
|
204
261
|
const record = this.records.get(this.key(providerId, accountId));
|
|
205
262
|
if (!record) return { state: "healthy" };
|
|
206
263
|
if (record.blocked) return { state: "blocked" };
|
|
264
|
+
if (record.quotaExhaustedUntil !== void 0 && now < record.quotaExhaustedUntil) {
|
|
265
|
+
return { state: "quota_exhausted", cooldownUntil: record.quotaExhaustedUntil };
|
|
266
|
+
}
|
|
207
267
|
if (record.rateLimitEndAt !== void 0 && now < record.rateLimitEndAt) {
|
|
208
268
|
return { state: "rate_limited", cooldownUntil: record.rateLimitEndAt };
|
|
209
269
|
}
|
|
@@ -215,6 +275,14 @@ var SubscriptionAccountHealth = class {
|
|
|
215
275
|
}
|
|
216
276
|
return { state: "healthy" };
|
|
217
277
|
}
|
|
278
|
+
/** Return newest-first, bounded, secret-free health edge diagnostics. */
|
|
279
|
+
getDiagnostics(filter = {}) {
|
|
280
|
+
const requested = typeof filter.limit === "number" && Number.isFinite(filter.limit) ? Math.trunc(filter.limit) : ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT;
|
|
281
|
+
const limit = Math.min(ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT, Math.max(1, requested));
|
|
282
|
+
return this.diagnostics.filter(
|
|
283
|
+
(event) => (!filter.providerId || event.providerId === filter.providerId) && (!filter.accountId || event.accountId === filter.accountId)
|
|
284
|
+
).slice(-limit).reverse().map((event) => ({ ...event }));
|
|
285
|
+
}
|
|
218
286
|
/**
|
|
219
287
|
* Register a recovery listener (the OQ3 seam #5 webhooks + #8 health-cron
|
|
220
288
|
* consume). Returns an unsubscribe function. Two edges fire it:
|
|
@@ -257,6 +325,12 @@ var SubscriptionAccountHealth = class {
|
|
|
257
325
|
}
|
|
258
326
|
}
|
|
259
327
|
}
|
|
328
|
+
recordDiagnostic(event) {
|
|
329
|
+
this.diagnostics.push({ ...event });
|
|
330
|
+
if (this.diagnostics.length > ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT) {
|
|
331
|
+
this.diagnostics.splice(0, this.diagnostics.length - ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
260
334
|
/**
|
|
261
335
|
* Delete every EXPIRED timer field on the record in place; return whether any
|
|
262
336
|
* timer is STILL active (in the future). `blocked` is not a timer and is left
|
|
@@ -276,6 +350,10 @@ var SubscriptionAccountHealth = class {
|
|
|
276
350
|
if (now >= record.tempUnavailableUntil) delete record.tempUnavailableUntil;
|
|
277
351
|
else active = true;
|
|
278
352
|
}
|
|
353
|
+
if (record.quotaExhaustedUntil !== void 0) {
|
|
354
|
+
if (now >= record.quotaExhaustedUntil) delete record.quotaExhaustedUntil;
|
|
355
|
+
else active = true;
|
|
356
|
+
}
|
|
279
357
|
return active;
|
|
280
358
|
}
|
|
281
359
|
key(providerId, accountId) {
|
|
@@ -303,6 +381,8 @@ export {
|
|
|
303
381
|
OVERLOAD_ENABLED_DEFAULT,
|
|
304
382
|
AUTH_ERROR_TTL_MS,
|
|
305
383
|
SERVER_ERROR_TTL_MS,
|
|
384
|
+
MAX_QUOTA_COOLDOWN_MS,
|
|
385
|
+
ACCOUNT_HEALTH_DIAGNOSTIC_LIMIT,
|
|
306
386
|
resolveResetSeconds,
|
|
307
387
|
SubscriptionAccountHealth,
|
|
308
388
|
getSharedAccountHealth,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); 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;// src/pipeline/AccountRouteActivity.ts
|
|
2
|
+
var ACCOUNT_ROUTE_ACTIVITY_LIMIT = 300;
|
|
3
|
+
function boundedLimit(value) {
|
|
4
|
+
if (!Number.isFinite(value)) return 100;
|
|
5
|
+
return Math.max(1, Math.min(ACCOUNT_ROUTE_ACTIVITY_LIMIT, Math.trunc(value)));
|
|
6
|
+
}
|
|
7
|
+
var AccountRouteActivityStore = (_class = class {constructor() { _class.prototype.__init.call(this);_class.prototype.__init2.call(this); }
|
|
8
|
+
__init() {this.records = []}
|
|
9
|
+
__init2() {this.sequence = 0}
|
|
10
|
+
record(input) {
|
|
11
|
+
const ts = _nullishCoalesce(input.ts, () => ( Date.now()));
|
|
12
|
+
const sessionKey = _optionalChain([input, 'access', _ => _.sessionKey, 'optionalAccess', _2 => _2.trim, 'call', _3 => _3()]) || void 0;
|
|
13
|
+
const previous = sessionKey ? this.records.find((record2) => record2.providerId === input.providerId && record2.endpoint === input.endpoint && record2.sessionKey === sessionKey) : void 0;
|
|
14
|
+
const affinity = !sessionKey ? "untracked" : !previous ? "new" : previous.accountId === input.accountId ? "sticky" : "switched";
|
|
15
|
+
const record = {
|
|
16
|
+
...input,
|
|
17
|
+
id: `${ts.toString(36)}-${(this.sequence++).toString(36)}`,
|
|
18
|
+
ts,
|
|
19
|
+
sessionKey,
|
|
20
|
+
affinity,
|
|
21
|
+
...affinity === "switched" && previous ? { previousAccountId: previous.accountId } : {}
|
|
22
|
+
};
|
|
23
|
+
this.records.unshift(record);
|
|
24
|
+
if (this.records.length > ACCOUNT_ROUTE_ACTIVITY_LIMIT) {
|
|
25
|
+
this.records.length = ACCOUNT_ROUTE_ACTIVITY_LIMIT;
|
|
26
|
+
}
|
|
27
|
+
return { ...record };
|
|
28
|
+
}
|
|
29
|
+
list(query = {}) {
|
|
30
|
+
const providerId = _optionalChain([query, 'access', _4 => _4.providerId, 'optionalAccess', _5 => _5.trim, 'call', _6 => _6()]);
|
|
31
|
+
const accountId = _optionalChain([query, 'access', _7 => _7.accountId, 'optionalAccess', _8 => _8.trim, 'call', _9 => _9()]);
|
|
32
|
+
const sessionKey = _optionalChain([query, 'access', _10 => _10.sessionKey, 'optionalAccess', _11 => _11.trim, 'call', _12 => _12()]);
|
|
33
|
+
return this.records.filter((record) => !providerId || record.providerId === providerId).filter((record) => !accountId || record.accountId === accountId).filter((record) => !sessionKey || record.sessionKey === sessionKey).slice(0, boundedLimit(query.limit)).map((record) => ({ ...record }));
|
|
34
|
+
}
|
|
35
|
+
clear() {
|
|
36
|
+
this.records.length = 0;
|
|
37
|
+
this.sequence = 0;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Backfill a field on an already-recorded entry. Used to annotate a record
|
|
41
|
+
* AFTER the upstream body streams — e.g. a `200` whose SSE body carries a
|
|
42
|
+
* `response.failed` overload event is only detectable mid-relay, long after
|
|
43
|
+
* `record()` stamped the 200 status. `patch` is intentionally narrow: only
|
|
44
|
+
* post-hoc-observable fields belong here, never the routing-decision fields.
|
|
45
|
+
* No-op when the id has already aged out of the bounded ring.
|
|
46
|
+
*/
|
|
47
|
+
amend(id, patch) {
|
|
48
|
+
const record = this.records.find((r) => r.id === id);
|
|
49
|
+
if (record) Object.assign(record, patch);
|
|
50
|
+
}
|
|
51
|
+
}, _class);
|
|
52
|
+
var sharedAccountRouteActivity = new AccountRouteActivityStore();
|
|
53
|
+
function getSharedAccountRouteActivity() {
|
|
54
|
+
return sharedAccountRouteActivity;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
exports.ACCOUNT_ROUTE_ACTIVITY_LIMIT = ACCOUNT_ROUTE_ACTIVITY_LIMIT; exports.AccountRouteActivityStore = AccountRouteActivityStore; exports.getSharedAccountRouteActivity = getSharedAccountRouteActivity;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); 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;// src/transformer/transformers/OpenAITransformer.ts
|
|
2
|
+
var FOREIGN_MESSAGE_KEYS = ["cache_control", "thinking"];
|
|
3
|
+
var FOREIGN_BODY_KEYS = ["meta", "reasoning", "thinking"];
|
|
4
|
+
function normalizeContent(content) {
|
|
5
|
+
if (!Array.isArray(content)) return content;
|
|
6
|
+
const cleaned = content.map((block) => {
|
|
7
|
+
if (block && typeof block === "object" && "cache_control" in block) {
|
|
8
|
+
const { cache_control: _dropped, ...rest } = block;
|
|
9
|
+
return rest;
|
|
10
|
+
}
|
|
11
|
+
return block;
|
|
12
|
+
});
|
|
13
|
+
if (cleaned.length === 1) {
|
|
14
|
+
const only = cleaned[0];
|
|
15
|
+
if (_optionalChain([only, 'optionalAccess', _ => _.type]) === "text" && typeof only.text === "string") return only.text;
|
|
16
|
+
}
|
|
17
|
+
return cleaned;
|
|
18
|
+
}
|
|
19
|
+
var OpenAITransformer = (_class = class {constructor() { _class.prototype.__init.call(this);_class.prototype.__init2.call(this); }
|
|
20
|
+
static __initStatic() {this.TransformerName = "openai"}
|
|
21
|
+
__init() {this.name = "openai"}
|
|
22
|
+
__init2() {this.endPoint = "/v1/chat/completions"}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Unified → OpenAI Chat Completions.
|
|
26
|
+
*/
|
|
27
|
+
async transformRequestIn(request, _provider, _context) {
|
|
28
|
+
const source = request;
|
|
29
|
+
const out = {};
|
|
30
|
+
for (const key of Object.keys(source)) {
|
|
31
|
+
if (FOREIGN_BODY_KEYS.includes(key)) continue;
|
|
32
|
+
out[key] = source[key];
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(request.messages)) {
|
|
35
|
+
out.messages = request.messages.map((message) => {
|
|
36
|
+
const src = message;
|
|
37
|
+
const encoded = {};
|
|
38
|
+
for (const key of Object.keys(src)) {
|
|
39
|
+
if (FOREIGN_MESSAGE_KEYS.includes(key)) continue;
|
|
40
|
+
encoded[key] = src[key];
|
|
41
|
+
}
|
|
42
|
+
if (message.content !== void 0) {
|
|
43
|
+
encoded.content = normalizeContent(message.content);
|
|
44
|
+
}
|
|
45
|
+
const thinkingContent = _optionalChain([message, 'access', _2 => _2.thinking, 'optionalAccess', _3 => _3.content]);
|
|
46
|
+
if (typeof thinkingContent === "string" && thinkingContent.length > 0) {
|
|
47
|
+
encoded.reasoning_content = thinkingContent;
|
|
48
|
+
}
|
|
49
|
+
return encoded;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const effort = _optionalChain([request, 'access', _4 => _4.reasoning, 'optionalAccess', _5 => _5.effort]);
|
|
53
|
+
if (typeof effort === "string" && effort !== "none") {
|
|
54
|
+
out.reasoning_effort = effort;
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* OpenAI Chat Completions → Unified. Identity — see the module JSDoc for why
|
|
60
|
+
* `reasoning_content` is NOT rewritten here.
|
|
61
|
+
*/
|
|
62
|
+
async transformResponseOut(response, _context) {
|
|
63
|
+
return response;
|
|
64
|
+
}
|
|
65
|
+
}, _class.__initStatic(), _class);
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
exports.OpenAITransformer = OpenAITransformer;
|
|
@@ -78,7 +78,9 @@ var UsageRecorder = class {
|
|
|
78
78
|
costSavedByCacheUsd,
|
|
79
79
|
rawUsage: input.rawUsage != null ? safeStringify(input.rawUsage) : null,
|
|
80
80
|
runId: _nullishCoalesce(input.runId, () => ( null)),
|
|
81
|
-
eventId: _nullishCoalesce(input.eventId, () => ( null))
|
|
81
|
+
eventId: _nullishCoalesce(input.eventId, () => ( null)),
|
|
82
|
+
...input.cacheKeySource !== void 0 ? { cacheKeySource: input.cacheKeySource } : {},
|
|
83
|
+
...input.cacheKeyInjected !== void 0 ? { cacheKeyInjected: input.cacheKeyInjected } : {}
|
|
82
84
|
};
|
|
83
85
|
try {
|
|
84
86
|
return await this.store.insert(row);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
var
|
|
6
|
+
var _chunkKBM2W6UYcjs = require('./chunk-KBM2W6UY.cjs');
|
|
7
7
|
|
|
8
8
|
// src/transformer/transformers/GeminiCodeAssistTransformer.ts
|
|
9
9
|
var DEFAULT_CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
|
|
@@ -117,7 +117,7 @@ var GeminiCodeAssistTransformer = (_class = class {constructor() { _class.protot
|
|
|
117
117
|
* openaiResponsesIngress). `undefined` is the valid fresh free-tier value.
|
|
118
118
|
*/
|
|
119
119
|
async transformRequestIn(request, provider, _context) {
|
|
120
|
-
const inner =
|
|
120
|
+
const inner = _chunkKBM2W6UYcjs.buildRequestBody.call(void 0, request);
|
|
121
121
|
const envelope = {
|
|
122
122
|
model: request.model,
|
|
123
123
|
project: provider.geminiProject,
|
|
@@ -147,7 +147,7 @@ var GeminiCodeAssistTransformer = (_class = class {constructor() { _class.protot
|
|
|
147
147
|
if (inner && typeof inner === "object" && !("model" in inner) && "model" in r) {
|
|
148
148
|
inner.model = r.model;
|
|
149
149
|
}
|
|
150
|
-
return
|
|
150
|
+
return _chunkKBM2W6UYcjs.transformRequestOut.call(void 0, inner);
|
|
151
151
|
}
|
|
152
152
|
/**
|
|
153
153
|
* Code Assist response → OpenAI-compatible. PEEL the `.response` envelope from
|
|
@@ -155,7 +155,7 @@ var GeminiCodeAssistTransformer = (_class = class {constructor() { _class.protot
|
|
|
155
155
|
*/
|
|
156
156
|
async transformResponseOut(response, _context) {
|
|
157
157
|
const unwrapped = await unwrapCodeAssistResponse(response);
|
|
158
|
-
return
|
|
158
|
+
return _chunkKBM2W6UYcjs.transformResponseOut.call(void 0, unwrapped, this.name, this.logger);
|
|
159
159
|
}
|
|
160
160
|
/**
|
|
161
161
|
* OpenAI-compatible response → Code Assist (endpoint-encode parity). Re-wraps
|
|
@@ -164,7 +164,7 @@ var GeminiCodeAssistTransformer = (_class = class {constructor() { _class.protot
|
|
|
164
164
|
* `transformResponseOut`'s peel.
|
|
165
165
|
*/
|
|
166
166
|
async transformResponseIn(response, _context) {
|
|
167
|
-
const geminiResponse = await
|
|
167
|
+
const geminiResponse = await _chunkKBM2W6UYcjs.transformResponseIn.call(void 0, response, this.logger);
|
|
168
168
|
const contentType = _nullishCoalesce(geminiResponse.headers.get("Content-Type"), () => ( ""));
|
|
169
169
|
if (contentType.includes("text/event-stream")) {
|
|
170
170
|
return geminiResponse;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getSharedAccountAllowanceStore
|
|
3
|
+
} from "./chunk-MKF46GNZ.js";
|
|
4
|
+
|
|
5
|
+
// src/pipeline/AccountAllowanceScheduling.ts
|
|
6
|
+
var DEFAULT_POLICY = {
|
|
7
|
+
enabled: false,
|
|
8
|
+
demoteAtPercent: 80,
|
|
9
|
+
pauseAtPercent: 98,
|
|
10
|
+
priorityPenalty: 100
|
|
11
|
+
};
|
|
12
|
+
var HISTORY_LIMIT = 200;
|
|
13
|
+
var AccountAllowanceExhaustedError = class extends Error {
|
|
14
|
+
constructor(providerId, resumeAt) {
|
|
15
|
+
super(
|
|
16
|
+
resumeAt ? `All ${providerId} subscription accounts are paused by the allowance policy until ${resumeAt}` : `All ${providerId} subscription accounts are paused by the allowance policy`
|
|
17
|
+
);
|
|
18
|
+
this.providerId = providerId;
|
|
19
|
+
this.resumeAt = resumeAt;
|
|
20
|
+
this.name = "AccountAllowanceExhaustedError";
|
|
21
|
+
}
|
|
22
|
+
providerId;
|
|
23
|
+
resumeAt;
|
|
24
|
+
code = "account_allowance_exhausted";
|
|
25
|
+
status = 429;
|
|
26
|
+
};
|
|
27
|
+
function isAccountAllowanceExhaustedError(error) {
|
|
28
|
+
if (error instanceof AccountAllowanceExhaustedError) return true;
|
|
29
|
+
if (!error || typeof error !== "object") return false;
|
|
30
|
+
const value = error;
|
|
31
|
+
return value.code === "account_allowance_exhausted" && value.status === 429;
|
|
32
|
+
}
|
|
33
|
+
function freshWorstWindow(snapshot) {
|
|
34
|
+
return snapshot.windows.filter((window) => window.state === "fresh" && typeof window.usedPercent === "number").sort((left, right) => (right.usedPercent ?? -1) - (left.usedPercent ?? -1))[0];
|
|
35
|
+
}
|
|
36
|
+
var AccountAllowanceScheduling = class {
|
|
37
|
+
constructor(store = getSharedAccountAllowanceStore(), now = Date.now) {
|
|
38
|
+
this.store = store;
|
|
39
|
+
this.now = now;
|
|
40
|
+
}
|
|
41
|
+
store;
|
|
42
|
+
now;
|
|
43
|
+
config = { ...DEFAULT_POLICY };
|
|
44
|
+
history = [];
|
|
45
|
+
lastRecorded = /* @__PURE__ */ new Map();
|
|
46
|
+
configure(config) {
|
|
47
|
+
this.config = config ? { ...config } : { ...DEFAULT_POLICY };
|
|
48
|
+
}
|
|
49
|
+
getConfig() {
|
|
50
|
+
return { ...this.config };
|
|
51
|
+
}
|
|
52
|
+
evaluate(providerId, accountId, basePriority, now = this.now()) {
|
|
53
|
+
const decision = this.decide(providerId, accountId, basePriority, now);
|
|
54
|
+
this.recordApplied(decision);
|
|
55
|
+
return decision;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Project the current policy result without recording an applied decision.
|
|
59
|
+
* Admin/read-model callers use this so polling an account list never creates
|
|
60
|
+
* synthetic demotion/pause history entries.
|
|
61
|
+
*/
|
|
62
|
+
preview(providerId, accountId, basePriority, now = this.now()) {
|
|
63
|
+
return this.decide(providerId, accountId, basePriority, now);
|
|
64
|
+
}
|
|
65
|
+
decide(providerId, accountId, basePriority, now) {
|
|
66
|
+
const decidedAt = new Date(now).toISOString();
|
|
67
|
+
const base = {
|
|
68
|
+
providerId,
|
|
69
|
+
accountId,
|
|
70
|
+
basePriority,
|
|
71
|
+
effectivePriority: basePriority,
|
|
72
|
+
schedulable: true,
|
|
73
|
+
decidedAt
|
|
74
|
+
};
|
|
75
|
+
if (!this.config.enabled) return { ...base, action: "ignore", reason: "policy-disabled" };
|
|
76
|
+
if (providerId !== "claude" && providerId !== "codex") {
|
|
77
|
+
return { ...base, action: "ignore", reason: "provider-unsupported" };
|
|
78
|
+
}
|
|
79
|
+
const snapshot = this.store.get(providerId, accountId, now);
|
|
80
|
+
if (!snapshot) return { ...base, action: "ignore", reason: "snapshot-missing" };
|
|
81
|
+
const worst = freshWorstWindow(snapshot);
|
|
82
|
+
if (!worst || worst.usedPercent === null) {
|
|
83
|
+
return { ...base, action: "ignore", reason: "snapshot-not-fresh", observedAt: snapshot.observedAt };
|
|
84
|
+
}
|
|
85
|
+
const resumeAt = worst.resetsAt ?? snapshot.expiresAt;
|
|
86
|
+
const resumeMs = resumeAt ? Date.parse(resumeAt) : Number.NaN;
|
|
87
|
+
const canPauseUntilKnownDeadline = Number.isFinite(resumeMs) && resumeMs > now;
|
|
88
|
+
let decision;
|
|
89
|
+
if (worst.usedPercent >= this.config.pauseAtPercent && canPauseUntilKnownDeadline) {
|
|
90
|
+
decision = {
|
|
91
|
+
...base,
|
|
92
|
+
action: "pause",
|
|
93
|
+
reason: "pause-threshold",
|
|
94
|
+
schedulable: false,
|
|
95
|
+
usedPercent: worst.usedPercent,
|
|
96
|
+
observedAt: snapshot.observedAt,
|
|
97
|
+
resumeAt
|
|
98
|
+
};
|
|
99
|
+
} else if (worst.usedPercent >= this.config.demoteAtPercent) {
|
|
100
|
+
decision = {
|
|
101
|
+
...base,
|
|
102
|
+
action: "demote",
|
|
103
|
+
reason: "demote-threshold",
|
|
104
|
+
effectivePriority: basePriority + this.config.priorityPenalty,
|
|
105
|
+
usedPercent: worst.usedPercent,
|
|
106
|
+
observedAt: snapshot.observedAt,
|
|
107
|
+
resumeAt: canPauseUntilKnownDeadline ? resumeAt : void 0
|
|
108
|
+
};
|
|
109
|
+
} else {
|
|
110
|
+
decision = {
|
|
111
|
+
...base,
|
|
112
|
+
action: "normal",
|
|
113
|
+
reason: "below-threshold",
|
|
114
|
+
usedPercent: worst.usedPercent,
|
|
115
|
+
observedAt: snapshot.observedAt,
|
|
116
|
+
resumeAt: canPauseUntilKnownDeadline ? resumeAt : void 0
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return decision;
|
|
120
|
+
}
|
|
121
|
+
getHistory() {
|
|
122
|
+
return this.history.map((decision) => ({ ...decision }));
|
|
123
|
+
}
|
|
124
|
+
clearHistory() {
|
|
125
|
+
this.history.length = 0;
|
|
126
|
+
this.lastRecorded.clear();
|
|
127
|
+
}
|
|
128
|
+
recordApplied(decision) {
|
|
129
|
+
if (decision.action !== "demote" && decision.action !== "pause") return;
|
|
130
|
+
const key = `${decision.providerId}\0${decision.accountId}`;
|
|
131
|
+
const signature = `${decision.action}\0${decision.observedAt ?? ""}\0${decision.usedPercent ?? ""}`;
|
|
132
|
+
if (this.lastRecorded.get(key) === signature) return;
|
|
133
|
+
this.lastRecorded.set(key, signature);
|
|
134
|
+
this.history.push({ ...decision });
|
|
135
|
+
if (this.history.length > HISTORY_LIMIT) this.history.splice(0, this.history.length - HISTORY_LIMIT);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
var sharedScheduling = null;
|
|
139
|
+
function getSharedAccountAllowanceScheduling() {
|
|
140
|
+
if (!sharedScheduling) sharedScheduling = new AccountAllowanceScheduling();
|
|
141
|
+
return sharedScheduling;
|
|
142
|
+
}
|
|
143
|
+
function __resetSharedAccountAllowanceSchedulingForTests() {
|
|
144
|
+
sharedScheduling = null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export {
|
|
148
|
+
AccountAllowanceExhaustedError,
|
|
149
|
+
isAccountAllowanceExhaustedError,
|
|
150
|
+
AccountAllowanceScheduling,
|
|
151
|
+
getSharedAccountAllowanceScheduling,
|
|
152
|
+
__resetSharedAccountAllowanceSchedulingForTests
|
|
153
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); 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;// src/pipeline/ServerOverloadCounter.ts
|
|
2
|
+
var OVERLOAD_RECENT_LIMIT = 64;
|
|
3
|
+
var OVERLOAD_ENTRY_TTL_MS = 24 * 60 * 6e4;
|
|
4
|
+
function entryKey(providerId, accountId, endpoint) {
|
|
5
|
+
return `${providerId}\0${accountId}\0${endpoint}`;
|
|
6
|
+
}
|
|
7
|
+
var ServerOverloadCounterStore = (_class = class {constructor() { _class.prototype.__init.call(this); }
|
|
8
|
+
__init() {this.entries = /* @__PURE__ */ new Map()}
|
|
9
|
+
/**
|
|
10
|
+
* Record one overload event. Creates the entry on first sight, then bumps
|
|
11
|
+
* `count`, `lastTs`, and prepends the event to the bounded `recent` ring.
|
|
12
|
+
*/
|
|
13
|
+
recordOverload(input, now = Date.now()) {
|
|
14
|
+
const key = entryKey(input.providerId, input.accountId, input.endpoint);
|
|
15
|
+
const existing = this.entries.get(key);
|
|
16
|
+
if (existing) {
|
|
17
|
+
existing.count += 1;
|
|
18
|
+
existing.lastTs = now;
|
|
19
|
+
existing.recent.unshift(now);
|
|
20
|
+
if (existing.recent.length > OVERLOAD_RECENT_LIMIT) {
|
|
21
|
+
existing.recent.length = OVERLOAD_RECENT_LIMIT;
|
|
22
|
+
}
|
|
23
|
+
return { ...existing, recent: [...existing.recent] };
|
|
24
|
+
}
|
|
25
|
+
const entry = {
|
|
26
|
+
providerId: input.providerId,
|
|
27
|
+
accountId: input.accountId,
|
|
28
|
+
endpoint: input.endpoint,
|
|
29
|
+
count: 1,
|
|
30
|
+
firstTs: now,
|
|
31
|
+
lastTs: now,
|
|
32
|
+
recent: [now]
|
|
33
|
+
};
|
|
34
|
+
this.entries.set(key, entry);
|
|
35
|
+
return { ...entry, recent: [...entry.recent] };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Snapshot all (optionally filtered) entries, pruning stale ones first. Returns
|
|
39
|
+
* copies — callers (admin API → JSON) never see the live mutable entries.
|
|
40
|
+
*/
|
|
41
|
+
list(query = {}, now = Date.now()) {
|
|
42
|
+
const providerId = _optionalChain([query, 'access', _ => _.providerId, 'optionalAccess', _2 => _2.trim, 'call', _3 => _3()]);
|
|
43
|
+
const accountId = _optionalChain([query, 'access', _4 => _4.accountId, 'optionalAccess', _5 => _5.trim, 'call', _6 => _6()]);
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const [key, entry] of this.entries) {
|
|
46
|
+
if (now - entry.lastTs > OVERLOAD_ENTRY_TTL_MS) {
|
|
47
|
+
this.entries.delete(key);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (providerId && entry.providerId !== providerId) continue;
|
|
51
|
+
if (accountId && entry.accountId !== accountId) continue;
|
|
52
|
+
out.push({ ...entry, recent: [...entry.recent] });
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
clear() {
|
|
57
|
+
this.entries.clear();
|
|
58
|
+
}
|
|
59
|
+
}, _class);
|
|
60
|
+
var sharedStore = null;
|
|
61
|
+
function getSharedOverloadCounter() {
|
|
62
|
+
if (!sharedStore) sharedStore = new ServerOverloadCounterStore();
|
|
63
|
+
return sharedStore;
|
|
64
|
+
}
|
|
65
|
+
function setSharedOverloadCounter(instance) {
|
|
66
|
+
sharedStore = instance;
|
|
67
|
+
}
|
|
68
|
+
function __resetSharedOverloadCounterForTests() {
|
|
69
|
+
sharedStore = null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
exports.OVERLOAD_RECENT_LIMIT = OVERLOAD_RECENT_LIMIT; exports.OVERLOAD_ENTRY_TTL_MS = OVERLOAD_ENTRY_TTL_MS; exports.ServerOverloadCounterStore = ServerOverloadCounterStore; exports.getSharedOverloadCounter = getSharedOverloadCounter; exports.setSharedOverloadCounter = setSharedOverloadCounter; exports.__resetSharedOverloadCounterForTests = __resetSharedOverloadCounterForTests;
|