@omnicross/core 0.1.1 → 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/ApiConverter.cjs +3 -778
- package/dist/ApiConverter.js +12 -751
- package/dist/{CompletionService-DtOF_War.d.cts → CompletionService-D3a47vdU.d.cts} +2 -1
- package/dist/{CompletionService-7fCmKAP3.d.ts → CompletionService-DdWaAxq0.d.ts} +2 -1
- package/dist/auth/GeminiCodeAssistProjectResolver.cjs +32 -60
- package/dist/auth/GeminiCodeAssistProjectResolver.js +11 -10
- package/dist/builtin-web-fetch-77JZUZGM.cjs +100 -0
- package/dist/builtin-web-fetch-IGLBX2XY.js +100 -0
- package/dist/chunk-2DCNB7DF.cjs +514 -0
- package/dist/chunk-2FMI2B6V.cjs +6196 -0
- package/dist/chunk-3OPYJG76.cjs +266 -0
- package/dist/chunk-3QOSDDNI.cjs +130 -0
- package/dist/chunk-46RLHI7D.js +0 -0
- package/dist/chunk-46ZIFWYG.cjs +277 -0
- package/dist/chunk-4DECIFU7.js +6196 -0
- package/dist/chunk-4IH4EL7M.cjs +9 -0
- package/dist/chunk-4VAVYUZS.cjs +991 -0
- package/dist/chunk-5ENKBSWO.js +311 -0
- package/dist/chunk-5ERIZW3D.js +44 -0
- package/dist/chunk-5HTVET6E.js +514 -0
- package/dist/chunk-5ZQBEOMD.js +764 -0
- package/dist/chunk-6RPZADX3.cjs +92 -0
- package/dist/chunk-7VU7V2E4.js +0 -0
- package/dist/chunk-7YN22L2H.cjs +87 -0
- package/dist/chunk-A2YMUCBT.cjs +105 -0
- package/dist/chunk-AEOZIDEB.cjs +32 -0
- package/dist/chunk-AQ5TNQB7.js +13 -0
- package/dist/chunk-AVYJ4YBN.js +58 -0
- package/dist/chunk-BO4RFWBR.js +89 -0
- package/dist/chunk-C72S6TTV.cjs +49 -0
- package/dist/chunk-C7PSERMH.js +87 -0
- package/dist/chunk-CDHS2QDC.js +179 -0
- package/dist/chunk-CWM2ZDU5.js +130 -0
- package/dist/chunk-CZHZGRU7.js +383 -0
- package/dist/chunk-DK4A7DLE.js +266 -0
- package/dist/chunk-DQ5VAYZ6.cjs +42 -0
- package/dist/chunk-DRWHTIIZ.js +13 -0
- package/dist/chunk-DWKQZ2SK.cjs +254 -0
- package/dist/chunk-EYZYXJTJ.cjs +1 -0
- package/dist/chunk-FFALUUZZ.cjs +235 -0
- package/dist/chunk-FMOBD2OQ.js +39 -0
- package/dist/chunk-FTQ7IQUC.js +991 -0
- package/dist/chunk-FZ46M34D.cjs +1 -0
- package/dist/chunk-G2UTNSGU.js +277 -0
- package/dist/chunk-GBHYAGX3.cjs +26 -0
- package/dist/chunk-GMMT7RVN.cjs +39 -0
- package/dist/chunk-H5JUT3KV.js +105 -0
- package/dist/chunk-HQP3K7PU.cjs +431 -0
- package/dist/chunk-HUZZZ3JL.js +32 -0
- package/dist/chunk-IAXWICZU.js +69 -0
- package/dist/chunk-JMJBSACD.cjs +147 -0
- package/dist/chunk-JO5NLTLY.js +46 -0
- package/dist/chunk-JWC6APFQ.js +23 -0
- package/dist/chunk-KUU2RNG6.cjs +46 -0
- package/dist/chunk-L74VDN4A.cjs +32 -0
- package/dist/chunk-MQNDWYPN.js +235 -0
- package/dist/chunk-MZNPGW5Q.js +26 -0
- package/dist/chunk-N5QFKSDO.js +56 -0
- package/dist/chunk-NYUNU75A.cjs +44 -0
- package/dist/chunk-NZYN7C3P.cjs +179 -0
- package/dist/chunk-Q27JY5RG.cjs +126 -0
- package/dist/chunk-QBZJ7P2T.cjs +13 -0
- package/dist/chunk-QWBWGODS.cjs +69 -0
- package/dist/chunk-QXD4MV2X.cjs +23 -0
- package/dist/chunk-R3HK6R4B.cjs +13 -0
- package/dist/chunk-RPII3E6Z.cjs +145 -0
- package/dist/chunk-RSJ3TWJQ.cjs +186 -0
- package/dist/chunk-S4WBV25P.js +913 -0
- package/dist/chunk-S6JGLF3G.cjs +89 -0
- package/dist/chunk-SUEGJ75D.cjs +764 -0
- package/dist/chunk-SVNDB62D.cjs +58 -0
- package/dist/chunk-SVXC36MU.cjs +383 -0
- package/dist/chunk-TKJMZBAH.js +32 -0
- package/dist/chunk-UBWEBKRC.js +42 -0
- package/dist/chunk-UNEFIWXI.cjs +32 -0
- package/dist/chunk-VQY5W4YW.cjs +311 -0
- package/dist/chunk-VVEHS2LI.js +431 -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-Y6OM7EV7.cjs +56 -0
- package/dist/chunk-Y7FO65VT.js +147 -0
- package/dist/chunk-YGXJGRFI.cjs +913 -0
- package/dist/chunk-YMFAJFQT.cjs +173 -0
- package/dist/chunk-Z7LUNECE.js +49 -0
- package/dist/chunk-Z7Y2NPHY.js +186 -0
- package/dist/chunk-ZJZAOKI7.js +254 -0
- package/dist/chunk-ZLX3KGXX.js +173 -0
- package/dist/chunk-ZY6KU6P3.js +126 -0
- package/dist/completion/ApiKeyPoolService.cjs +23 -48
- package/dist/completion/ApiKeyPoolService.d.cts +150 -2
- package/dist/completion/ApiKeyPoolService.d.ts +150 -2
- package/dist/completion/BuiltinToolExecutor.cjs +9 -324
- package/dist/completion/BuiltinToolExecutor.js +6 -290
- package/dist/completion/CompletionService.cjs +45 -3487
- package/dist/completion/CompletionService.d.cts +3 -2
- package/dist/completion/CompletionService.d.ts +3 -2
- package/dist/completion/CompletionService.js +41 -3457
- package/dist/completion/NativeSearchInjector.cjs +8 -192
- package/dist/completion/NativeSearchInjector.js +7 -162
- package/dist/completion/ProviderSearchInjector.cjs +12 -39
- package/dist/completion/native-search-types.cjs +13 -66
- package/dist/completion/native-search-types.js +7 -31
- package/dist/completion/openrouter-headers.cjs +12 -71
- package/dist/completion/openrouter-headers.d.cts +9 -12
- package/dist/completion/openrouter-headers.d.ts +9 -12
- package/dist/completion/openrouter-headers.js +7 -36
- package/dist/completion/openrouter-models.cjs +11 -67
- package/dist/completion/openrouter-models.js +5 -34
- package/dist/completion/types.cjs +1 -18
- package/dist/completion/url-builder.cjs +24 -138
- package/dist/completion/url-builder.js +12 -92
- package/dist/completion.cjs +62 -3989
- package/dist/completion.d.cts +3 -2
- package/dist/completion.d.ts +3 -2
- package/dist/completion.js +70 -3953
- package/dist/index.cjs +319 -10429
- package/dist/index.d.cts +49 -12
- package/dist/index.d.ts +49 -12
- package/dist/index.js +248 -10269
- package/dist/logger-4GvQNzhE.d.cts +22 -0
- package/dist/logger-4GvQNzhE.d.ts +22 -0
- package/dist/outbound-api/routeResolver.cjs +10 -217
- package/dist/outbound-api/routeResolver.d.cts +10 -3
- package/dist/outbound-api/routeResolver.d.ts +10 -3
- package/dist/outbound-api/routeResolver.js +9 -187
- package/dist/outbound-api/subscriptionRegistryPort.cjs +7 -37
- package/dist/outbound-api/subscriptionRegistryPort.d.cts +2 -1
- package/dist/outbound-api/subscriptionRegistryPort.d.ts +2 -1
- package/dist/outbound-api/subscriptionRegistryPort.js +4 -8
- package/dist/outbound-api/types.cjs +5 -17
- package/dist/outbound-api/types.d.cts +12 -144
- package/dist/outbound-api/types.d.ts +12 -144
- package/dist/outbound-api/types.js +6 -0
- package/dist/outbound-api.cjs +197 -5265
- package/dist/outbound-api.d.cts +787 -25
- package/dist/outbound-api.d.ts +787 -25
- package/dist/outbound-api.js +173 -5195
- package/dist/pipeline/AuthSource.cjs +1 -18
- package/dist/pipeline/AuthSource.d.cts +23 -1
- package/dist/pipeline/AuthSource.d.ts +23 -1
- package/dist/pipeline/LlmConfigProviderAuth.cjs +5 -166
- package/dist/pipeline/LlmConfigProviderAuth.d.cts +2 -1
- package/dist/pipeline/LlmConfigProviderAuth.d.ts +2 -1
- package/dist/pipeline/LlmConfigProviderAuth.js +5 -139
- 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 +6 -128
- package/dist/pipeline/SubscriptionAuthSource.d.cts +3 -2
- package/dist/pipeline/SubscriptionAuthSource.d.ts +3 -2
- package/dist/pipeline/SubscriptionAuthSource.js +5 -99
- package/dist/pipeline/SubscriptionAuthStrategy.cjs +1 -18
- 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/pipeline/executeProviderCall.cjs +5 -69
- package/dist/pipeline/executeProviderCall.js +3 -42
- package/dist/pipeline/resolveProviderChain.cjs +5 -46
- package/dist/pipeline/resolveProviderChain.js +3 -19
- package/dist/pipeline/resolveSubscriptionChain.cjs +5 -67
- package/dist/pipeline/resolveSubscriptionChain.js +3 -40
- package/dist/ports/gemini-code-assist-resolver.cjs +7 -37
- package/dist/ports/gemini-code-assist-resolver.js +4 -8
- package/dist/ports/pricing-store.cjs +1 -0
- package/dist/ports/pricing-store.d.cts +50 -0
- package/dist/ports/pricing-store.d.ts +50 -0
- package/dist/ports/pricing-store.js +0 -0
- package/dist/ports/provider-config-source.cjs +1 -18
- package/dist/ports/usage-event-store.cjs +1 -0
- package/dist/ports/usage-event-store.d.cts +54 -0
- package/dist/ports/usage-event-store.d.ts +54 -0
- package/dist/ports/usage-event-store.js +0 -0
- package/dist/ports/web-search-backend.cjs +1 -18
- package/dist/ports.cjs +1 -18
- package/dist/ports.d.cts +13 -2
- package/dist/ports.d.ts +13 -2
- package/dist/provider-proxy/ProviderProxy.cjs +47 -4643
- package/dist/provider-proxy/ProviderProxy.d.cts +2 -1
- package/dist/provider-proxy/ProviderProxy.d.ts +2 -1
- package/dist/provider-proxy/ProviderProxy.js +42 -4613
- 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 +42 -2941
- package/dist/provider-proxy/ingress/providerProxyShared.d.cts +13 -3
- package/dist/provider-proxy/ingress/providerProxyShared.d.ts +13 -3
- package/dist/provider-proxy/ingress/providerProxyShared.js +48 -2914
- package/dist/provider-proxy/matchText.cjs +13 -72
- package/dist/provider-proxy/matchText.d.cts +14 -1
- package/dist/provider-proxy/matchText.d.ts +14 -1
- package/dist/provider-proxy/matchText.js +8 -39
- package/dist/provider-proxy/types.cjs +1 -18
- package/dist/provider-proxy/types.d.cts +25 -1
- package/dist/provider-proxy/types.d.ts +25 -1
- package/dist/provider-proxy.cjs +42 -4655
- package/dist/provider-proxy.d.cts +2 -1
- package/dist/provider-proxy.d.ts +2 -1
- package/dist/provider-proxy.js +46 -4628
- 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/serializeError.cjs +5 -81
- package/dist/serializeError.js +3 -54
- package/dist/sse-parser.cjs +7 -455
- package/dist/sse-parser.js +4 -426
- package/dist/transformer/TransformerChainExecutor.cjs +4 -318
- package/dist/transformer/TransformerChainExecutor.js +4 -291
- package/dist/transformer/TransformerService.cjs +5 -289
- package/dist/transformer/TransformerService.js +3 -262
- package/dist/transformer/anthropicBetaInject.cjs +7 -50
- package/dist/transformer/anthropicBetaInject.js +4 -21
- package/dist/transformer/transformers/AnthropicTransformer.cjs +3 -1014
- package/dist/transformer/transformers/AnthropicTransformer.js +3 -987
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +4 -1106
- package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +7 -1079
- package/dist/transformer/transformers/GeminiTransformer.cjs +4 -1010
- package/dist/transformer/transformers/GeminiTransformer.js +4 -983
- package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +3 -535
- package/dist/transformer/transformers/OpenAIResponseTransformer.js +3 -510
- package/dist/transformer/transformers/OpenCodeGoTransformer.cjs +5 -72
- package/dist/transformer/transformers/OpenCodeGoTransformer.js +3 -45
- package/dist/transformer/transformers/ReasoningTransformer.cjs +3 -270
- package/dist/transformer/transformers/ReasoningTransformer.js +2 -249
- package/dist/transformer/transformers.cjs +10 -3177
- package/dist/transformer/transformers.js +23 -3158
- package/dist/transformer/types.cjs +1 -18
- package/dist/transformer.cjs +13 -3726
- package/dist/transformer.js +17 -3706
- package/dist/types-6OUY_xfA.d.ts +830 -0
- package/dist/types-aBFFXoBz.d.cts +830 -0
- package/dist/usage/pricing-engine.cjs +6 -0
- package/dist/usage/pricing-engine.d.cts +83 -0
- package/dist/usage/pricing-engine.d.ts +83 -0
- package/dist/usage/pricing-engine.js +6 -0
- package/dist/usage/usage-recorder.cjs +6 -0
- package/dist/usage/usage-recorder.d.cts +91 -0
- package/dist/usage/usage-recorder.d.ts +91 -0
- package/dist/usage/usage-recorder.js +6 -0
- package/dist/usage.cjs +11 -0
- package/dist/usage.d.cts +8 -0
- package/dist/usage.d.ts +8 -0
- package/dist/usage.js +11 -0
- package/package.json +4 -2
- package/dist/ApiKeyPoolService-BmMkau07.d.cts +0 -170
- package/dist/ApiKeyPoolService-BmMkau07.d.ts +0 -170
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
import { ProxyConfig } from '@omnicross/contracts/account-tokens-types';
|
|
2
|
+
import { AuditConfig } from '@omnicross/contracts/audit-types';
|
|
3
|
+
import { BillingConfig } from '@omnicross/contracts/billing-types';
|
|
4
|
+
import { HealthReport } from '@omnicross/contracts/health-logging-types';
|
|
5
|
+
import { VoucherRecord, VoucherGrant, VoucherInfo, VoucherConfig } from '@omnicross/contracts/voucher-types';
|
|
6
|
+
import { WebhookConfig } from '@omnicross/contracts/webhook-types';
|
|
7
|
+
import { L as Logger } from './logger-4GvQNzhE.cjs';
|
|
8
|
+
import { ProviderConfigSource } from './ports/provider-config-source.cjs';
|
|
9
|
+
import { P as ProviderProxy } from './ProviderProxy-CnMQYN59.cjs';
|
|
10
|
+
import { ProviderProxyDeps } from './provider-proxy/types.cjs';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* keyPolicy — PURE decision helpers for the outbound key-policy envelope
|
|
14
|
+
* (`outbound-key-policy`, design D2/D4). Expiry/activation resolution +
|
|
15
|
+
* cost-quota decision. No I/O, no clock ownership (the caller passes `now`), so
|
|
16
|
+
* these are trivially unit-testable and reused by both the auth path
|
|
17
|
+
* (`verifyKey`) and the wire path (`outboundApiRouter`).
|
|
18
|
+
*
|
|
19
|
+
* The #6 (`restrictedModels`) and #9 (voucher) children EXTEND the key-policy
|
|
20
|
+
* schema; they must not change the shapes frozen here.
|
|
21
|
+
*
|
|
22
|
+
* @module outbound-api/keyPolicy
|
|
23
|
+
*/
|
|
24
|
+
/** The expiry/activation subset of a key row `computeKeyExpiry` reads. */
|
|
25
|
+
interface KeyExpiryInput {
|
|
26
|
+
/** Fixed-mode absolute expiry (epoch ms). */
|
|
27
|
+
expiresAt?: number | null;
|
|
28
|
+
/** Expiry mode; absent ⇒ `'fixed'`. */
|
|
29
|
+
activationMode?: 'fixed' | 'activation';
|
|
30
|
+
/** Activation-mode lifetime in days (absent/null ⇒ never expires). */
|
|
31
|
+
activationDays?: number | null;
|
|
32
|
+
/** First-use activation stamp (epoch ms); null ⇒ not yet activated. */
|
|
33
|
+
activatedAt?: number | null;
|
|
34
|
+
}
|
|
35
|
+
/** Result of {@link computeKeyExpiry}. */
|
|
36
|
+
interface KeyExpiryResult {
|
|
37
|
+
expired: boolean;
|
|
38
|
+
/** The resolved absolute expiry (epoch ms), when the key has one. */
|
|
39
|
+
effectiveExpiresAt?: number;
|
|
40
|
+
/**
|
|
41
|
+
* True when this is an activation-mode key that has NOT been activated yet, so
|
|
42
|
+
* the caller SHOULD stamp the activation (best-effort) on a successful verify.
|
|
43
|
+
*/
|
|
44
|
+
needsActivation?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve whether a key is past its effective expiry (design D2).
|
|
48
|
+
* - `fixed`: expired when `expiresAt != null && now >= expiresAt`.
|
|
49
|
+
* - `activation`: while `activatedAt == null` the key is NOT expired (it
|
|
50
|
+
* activates on THIS use → `needsActivation`); once activated, expired when
|
|
51
|
+
* `now >= activatedAt + activationDays*DAY`. A null `activationDays` never
|
|
52
|
+
* expires.
|
|
53
|
+
* A key with no expiry configured never expires.
|
|
54
|
+
*/
|
|
55
|
+
declare function computeKeyExpiry(row: KeyExpiryInput, now: number): KeyExpiryResult;
|
|
56
|
+
/** Per-key USD cost limits carried on the verified key (absent scope ⇒ no cap). */
|
|
57
|
+
interface KeyCostLimits {
|
|
58
|
+
dailyUsd?: number;
|
|
59
|
+
totalUsd?: number;
|
|
60
|
+
weeklyUsd?: number;
|
|
61
|
+
}
|
|
62
|
+
/** In-memory per-key accumulated spend snapshot (see `keySpendTracker`). */
|
|
63
|
+
interface KeySpend {
|
|
64
|
+
dailyUsd: number;
|
|
65
|
+
/** Local-midnight boundary the `dailyUsd` window opened at (epoch ms). */
|
|
66
|
+
dailyWindowStart: number;
|
|
67
|
+
weeklyUsd: number;
|
|
68
|
+
/** Local week-start boundary the `weeklyUsd` window opened at (epoch ms). */
|
|
69
|
+
weeklyWindowStart: number;
|
|
70
|
+
totalUsd: number;
|
|
71
|
+
}
|
|
72
|
+
/** Decision returned by {@link checkKeyQuota}. */
|
|
73
|
+
type QuotaDecision = {
|
|
74
|
+
allowed: true;
|
|
75
|
+
} | {
|
|
76
|
+
allowed: false;
|
|
77
|
+
scope: 'daily' | 'weekly' | 'total';
|
|
78
|
+
limitUsd: number;
|
|
79
|
+
spentUsd: number;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Decide whether the key's accumulated spend already meets/exceeds any
|
|
83
|
+
* configured cost limit (design D4). `allowed:false` when a scope's PRIOR spend
|
|
84
|
+
* is `>= limit` (the triggering request's own cost is unknown pre-flight →
|
|
85
|
+
* bounded one-request overage, CRS-equivalent). An unconfigured scope is never
|
|
86
|
+
* gated; a `limits`-less key always passes.
|
|
87
|
+
*/
|
|
88
|
+
declare function checkKeyQuota(limits: KeyCostLimits | undefined, spend: KeySpend): QuotaDecision;
|
|
89
|
+
/**
|
|
90
|
+
* Per-key model restriction carried on the verified key (outbound-key-policy #6).
|
|
91
|
+
* Present ONLY when the key has `enableModelRestriction === true` — so the wire
|
|
92
|
+
* layer's presence check (`if (key.modelRestriction) …`) is the zero-regression
|
|
93
|
+
* gate.
|
|
94
|
+
*/
|
|
95
|
+
interface ModelRestriction {
|
|
96
|
+
mode: 'blacklist' | 'allowlist';
|
|
97
|
+
/** Bare modelIds the mode acts on. Empty allowlist denies every model. */
|
|
98
|
+
models: string[];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* keySpendTracker — a cheap in-memory per-key accumulated-spend cache
|
|
103
|
+
* (`outbound-key-policy`, design D3).
|
|
104
|
+
*
|
|
105
|
+
* The request-path cost check MUST NOT scan the usage log. This tracker keeps an
|
|
106
|
+
* O(1) per-`apiKeyId` aggregate:
|
|
107
|
+
* - LAZY SEED: on the first `getSpend` for a key (cache miss), it derives that
|
|
108
|
+
* key's total / today's / this-week's spend from the durable usage store via
|
|
109
|
+
* the injected {@link KeySpendSeeder} — ONE query per newly-seen key, NOT per
|
|
110
|
+
* request. Because it re-seeds from the store, `totalUsd` survives a restart
|
|
111
|
+
* (the jsonl IS the durable source; this is just a hot cache).
|
|
112
|
+
* - INCREMENT: `add(id, cost, now)` is wired to `UsageRecorder`'s per-request
|
|
113
|
+
* record hook, so every recorded request updates the aggregate with no scan.
|
|
114
|
+
* - WINDOW ROLL: `getSpend`/`add` roll the daily window at local midnight and
|
|
115
|
+
* the weekly window at the local week start before reading/incrementing.
|
|
116
|
+
*
|
|
117
|
+
* @module outbound-api/keySpendTracker
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Reader the wire layer depends on (structural). Kept minimal so a test double —
|
|
122
|
+
* or a future indexed impl — is trivial and the router needs no concrete class.
|
|
123
|
+
*/
|
|
124
|
+
interface KeySpendReader {
|
|
125
|
+
getSpend(apiKeyId: string, now: number): Promise<KeySpend>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Durable-store seed source (design D3). ONE scan of a single key's events
|
|
129
|
+
* returns all three spend windows (`totalUsd` + `dailyUsd` from `dayStartTs` +
|
|
130
|
+
* `weeklyUsd` from `weekStartTs`, all `ts < endTs`). One call per newly-seen key.
|
|
131
|
+
*/
|
|
132
|
+
interface KeySpendSeeder {
|
|
133
|
+
getSpendByKey(query: {
|
|
134
|
+
apiKeyId: string;
|
|
135
|
+
dayStartTs: number;
|
|
136
|
+
weekStartTs: number;
|
|
137
|
+
endTs: number;
|
|
138
|
+
}): Promise<{
|
|
139
|
+
totalUsd: number;
|
|
140
|
+
dailyUsd: number;
|
|
141
|
+
weeklyUsd: number;
|
|
142
|
+
}>;
|
|
143
|
+
}
|
|
144
|
+
/** Local-midnight boundary for `ts` (epoch ms). */
|
|
145
|
+
declare function startOfLocalDay(ts: number): number;
|
|
146
|
+
/**
|
|
147
|
+
* Local week-start boundary for `ts` (Sunday 00:00 local) — the daily boundary
|
|
148
|
+
* shifted back to the most recent Sunday.
|
|
149
|
+
*/
|
|
150
|
+
declare function startOfLocalWeek(ts: number): number;
|
|
151
|
+
declare class KeySpendTracker implements KeySpendReader {
|
|
152
|
+
private readonly seeder;
|
|
153
|
+
/** apiKeyId → live accumulated spend (seeded lazily). */
|
|
154
|
+
private readonly spend;
|
|
155
|
+
constructor(seeder: KeySpendSeeder);
|
|
156
|
+
/**
|
|
157
|
+
* Read the key's accumulated spend, seeding from the durable store on a cache
|
|
158
|
+
* miss and rolling the daily/weekly windows on a hit.
|
|
159
|
+
*/
|
|
160
|
+
getSpend(apiKeyId: string, now: number): Promise<KeySpend>;
|
|
161
|
+
/**
|
|
162
|
+
* Increment the aggregate for one recorded request. No-op when the key has not
|
|
163
|
+
* been seeded yet — a later `getSpend` will lazy-seed from the durable store,
|
|
164
|
+
* which already includes this record, so it is neither lost nor double-counted.
|
|
165
|
+
*/
|
|
166
|
+
add(apiKeyId: string, costUsd: number, now: number): void;
|
|
167
|
+
/** Drop all cached spend (tests / teardown). */
|
|
168
|
+
reset(): void;
|
|
169
|
+
/**
|
|
170
|
+
* Derive a fresh spend snapshot for `apiKeyId` from the durable store in a
|
|
171
|
+
* SINGLE scan (all three windows at once). `endTs` is EXCLUSIVE, so `now + 1`
|
|
172
|
+
* includes a record stamped at exactly `now`.
|
|
173
|
+
*/
|
|
174
|
+
private seed;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* voucher — the `VoucherDb` port + PURE code/grant helpers for the voucher
|
|
179
|
+
* redemption system (voucher-redemption #9, design D2/D3/D4/D5/D7).
|
|
180
|
+
*
|
|
181
|
+
* Layered on top of the #4 outbound key-policy: a voucher redemption only
|
|
182
|
+
* MUTATES a key's `totalCostLimitUsd` (credit) or `expiresAt` (renewal) through
|
|
183
|
+
* the already-built `outboundApiKeysSetPolicy` enforcement — no new key fields.
|
|
184
|
+
*
|
|
185
|
+
* This module is pure logic + a store port:
|
|
186
|
+
* - Code generation reuses the key-auth unbiased base62 generator (`CC_` prefix)
|
|
187
|
+
* and sha256 hashing — the plaintext is returned once and NEVER persisted.
|
|
188
|
+
* - `computeVoucherGrant` derives the ABSOLUTE grant target (with per-card caps)
|
|
189
|
+
* from the current key policy — recorded on the card so the apply step is
|
|
190
|
+
* idempotently re-appliable after an interrupted redeem (design D4).
|
|
191
|
+
* - `VoucherDb` mirrors `OutboundKeyDb` with an ATOMIC status compare-and-set
|
|
192
|
+
* (`redeemCas`/`revokeCas`) — the single-use guarantee.
|
|
193
|
+
*
|
|
194
|
+
* @module outbound-api/voucher
|
|
195
|
+
*/
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Generate a fresh high-entropy voucher code: `CC_<base62>`. Reuses the key-auth
|
|
199
|
+
* unbiased (rejection-sampled) base62 generator, so a code is a ~190-bit
|
|
200
|
+
* credential — guessing is infeasible (design D3). The plaintext is returned ONCE
|
|
201
|
+
* at generation and NEVER persisted or logged.
|
|
202
|
+
*/
|
|
203
|
+
declare function generateVoucherCode(): string;
|
|
204
|
+
/** Hash a presented/generated voucher code (sha256 hex) — the redeem lookup key. */
|
|
205
|
+
declare function hashVoucherCode(code: string): string;
|
|
206
|
+
/** The short display prefix stored alongside the hash (e.g. `CC_AB12`). */
|
|
207
|
+
declare function voucherCodePrefix(code: string): string;
|
|
208
|
+
/** A small id for a created voucher (independent of the code). */
|
|
209
|
+
declare function newVoucherId(): string;
|
|
210
|
+
/**
|
|
211
|
+
* Compute the ABSOLUTE grant target a redemption should apply to a key (design
|
|
212
|
+
* D5), clamped by the card's per-card anti-abuse caps. Pure — the caller records
|
|
213
|
+
* this on the card at the CAS flip AND applies the same absolute via
|
|
214
|
+
* `outboundApiKeysSetPolicy`, so an interrupted redeem is idempotently
|
|
215
|
+
* re-appliable (never double-spends).
|
|
216
|
+
*
|
|
217
|
+
* - `credit` → `min((current.totalCostLimitUsd ?? 0) + creditUsd, maxTotalCostLimitUsd ?? ∞)`.
|
|
218
|
+
* Raising the ceiling above accumulated spend lets an over-limit key resume
|
|
219
|
+
* (the #4 spend tracker keeps the spend; only the ceiling rises).
|
|
220
|
+
* - `renewal` → `min((current.expiresAt ?? now) + renewalDays·DAY, now + maxExpiryDays·DAY ?? ∞)`.
|
|
221
|
+
*/
|
|
222
|
+
declare function computeVoucherGrant(voucher: VoucherRecord, currentKey: Pick<OutboundKeyDbRow, 'totalCostLimitUsd' | 'expiresAt'> | null | undefined, now: number): VoucherGrant;
|
|
223
|
+
/**
|
|
224
|
+
* Project a stored voucher to the admin-safe DTO (design D2). Strips the
|
|
225
|
+
* `codeHash` — the admin sees the display prefix + status + value + caps only,
|
|
226
|
+
* NEVER the hash. This is the ONLY projection an admin GET returns.
|
|
227
|
+
*/
|
|
228
|
+
declare function toVoucherInfo(v: VoucherRecord): VoucherInfo;
|
|
229
|
+
/** Input to `VoucherDb.create` — the card fields minus the derived lifecycle. */
|
|
230
|
+
interface VoucherCreateInput {
|
|
231
|
+
id: string;
|
|
232
|
+
codeHash: string;
|
|
233
|
+
codePrefix: string;
|
|
234
|
+
type: VoucherRecord['type'];
|
|
235
|
+
creditUsd?: number;
|
|
236
|
+
renewalDays?: number;
|
|
237
|
+
maxTotalCostLimitUsd?: number;
|
|
238
|
+
maxExpiryDays?: number;
|
|
239
|
+
createdAt?: number;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The voucher store port (design D7), mirroring `OutboundKeyDb`. The daemon
|
|
243
|
+
* implements it over the SAME update-capable mechanism the key store uses (a JSON
|
|
244
|
+
* file with a synchronous read-check-write that is atomic within Node's single
|
|
245
|
+
* thread — NOT an append-only jsonl, which cannot flip a status in place).
|
|
246
|
+
*
|
|
247
|
+
* `redeemCas`/`revokeCas` are ATOMIC compare-and-sets: they transition the card
|
|
248
|
+
* out of `unredeemed` ONLY IF it is still `unredeemed`, returning whether THIS
|
|
249
|
+
* call won. A concurrent second redeem loses (returns false) — the single-use
|
|
250
|
+
* guarantee (design D4).
|
|
251
|
+
*/
|
|
252
|
+
interface VoucherDb {
|
|
253
|
+
/** Create + persist a card (status `unredeemed`). */
|
|
254
|
+
voucherCreate(input: VoucherCreateInput): Promise<VoucherRecord>;
|
|
255
|
+
/** Look up a card by its code hash (the redeem lookup). Null when absent. */
|
|
256
|
+
voucherGetByHash(codeHash: string): Promise<VoucherRecord | null>;
|
|
257
|
+
/**
|
|
258
|
+
* ATOMIC single-use flip `unredeemed → redeemed`, recording the redeeming key
|
|
259
|
+
* id + the AUDIT `granted*` snapshot and setting `grantApplied = false`. Returns
|
|
260
|
+
* true when THIS call won the CAS (the card was still `unredeemed`), false when
|
|
261
|
+
* it lost (already redeemed/revoked) — the single-use guard (design D4). The
|
|
262
|
+
* grant is NOT applied here; the caller applies a RELATIVE increment then calls
|
|
263
|
+
* {@link voucherMarkGrantApplied}.
|
|
264
|
+
*/
|
|
265
|
+
voucherRedeemCas(id: string, keyId: string, granted: VoucherGrant, now: number): Promise<boolean>;
|
|
266
|
+
/**
|
|
267
|
+
* Mark a redeemed card's grant as APPLIED (`grantApplied = true`), AFTER the
|
|
268
|
+
* key policy write succeeded (MJ1 fix). The guard against a replay double-add:
|
|
269
|
+
* a `redeemed` card with `grantApplied === true` is never re-applied. Returns
|
|
270
|
+
* false when the card is missing/not redeemed.
|
|
271
|
+
*/
|
|
272
|
+
voucherMarkGrantApplied(id: string): Promise<boolean>;
|
|
273
|
+
/**
|
|
274
|
+
* Revert a flip whose grant apply FAILED (M3): `redeemed → unredeemed`, clearing
|
|
275
|
+
* the redeem bookkeeping, ONLY IF the card is `redeemed`, was redeemed by
|
|
276
|
+
* `keyId`, and `grantApplied !== true` (never revert an applied grant). Returns
|
|
277
|
+
* true when reverted, false otherwise. Lets the holder retry a card whose apply
|
|
278
|
+
* hit a transient key error instead of silently losing it.
|
|
279
|
+
*/
|
|
280
|
+
voucherRevertRedeem(id: string, keyId: string): Promise<boolean>;
|
|
281
|
+
/**
|
|
282
|
+
* ATOMIC flip `unredeemed → revoked`. Returns true when THIS call won (the card
|
|
283
|
+
* was still `unredeemed`), false otherwise — a REDEEMED card can NEVER be
|
|
284
|
+
* revoked (design D6).
|
|
285
|
+
*/
|
|
286
|
+
voucherRevokeCas(id: string, now: number): Promise<boolean>;
|
|
287
|
+
/** List all cards (admin; the caller projects to `VoucherInfo` — no hash out). */
|
|
288
|
+
voucherList(): Promise<VoucherRecord[]>;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Shared contract types for the outbound API server (`outbound-api-server`).
|
|
293
|
+
*
|
|
294
|
+
* The outbound server is a SEPARATE long-lived HTTP listener (distinct from the
|
|
295
|
+
* resident loopback `ProviderProxy`) that authenticates external callers via
|
|
296
|
+
* named API keys and routes their requests through the EXISTING `provider-proxy`
|
|
297
|
+
* ingress parsers + transformer — there is exactly one conversion stack.
|
|
298
|
+
*
|
|
299
|
+
* These types are kept in their own module so the server, router, auth, rate
|
|
300
|
+
* limiter, role detection, and route resolver can share shapes without circular
|
|
301
|
+
* imports.
|
|
302
|
+
*
|
|
303
|
+
* @module outbound-api/types
|
|
304
|
+
*/
|
|
305
|
+
|
|
306
|
+
/** The four endpoints, 1:1 with the four wire-format ingress parsers. */
|
|
307
|
+
type OutboundEndpoint = 'chat' | 'responses' | 'messages' | 'gemini';
|
|
308
|
+
/** A `"providerId,modelId"` model reference (mirrors `RouterConfig` vocab). */
|
|
309
|
+
type ModelRef = string;
|
|
310
|
+
/** The detected request role; precedence background > default. */
|
|
311
|
+
type RequestRole = 'background' | 'default';
|
|
312
|
+
/**
|
|
313
|
+
* Dispatch mode for the list-mapped `chat` endpoint (openai-chat-bridge #11).
|
|
314
|
+
* - `'list'` (default): the client requests one of the endpoint's configured
|
|
315
|
+
* `models` refs by modelId — the exact current behavior (zero regression).
|
|
316
|
+
* - `'prefix'`: the requested model's NAME PREFIX (`claude-*` / `gpt-*` /
|
|
317
|
+
* `gemini-*`) selects a configured target from {@link ModelPrefixTargets},
|
|
318
|
+
* on a single `/v1/chat/completions` — a routing convenience over the same
|
|
319
|
+
* conversion machinery. An unmatched prefix is a clear per-request error.
|
|
320
|
+
*/
|
|
321
|
+
type ChatDispatchMode = 'list' | 'prefix';
|
|
322
|
+
/**
|
|
323
|
+
* Prefix → target `"providerId,modelId"` ref map for the `chat` endpoint's
|
|
324
|
+
* `dispatchMode: 'prefix'` (openai-chat-bridge #11). Each target may be a
|
|
325
|
+
* subscription (e.g. `claude,claude-sonnet-4-5`) or a BYO ref — the same ref
|
|
326
|
+
* vocabulary the list mode uses. Absent prefixes are simply unroutable in prefix
|
|
327
|
+
* mode. The vocabulary is the three core prefixes (claude / gpt / gemini);
|
|
328
|
+
* additional prefixes (deepseek, …) are an additive-later extension.
|
|
329
|
+
*/
|
|
330
|
+
interface ModelPrefixTargets {
|
|
331
|
+
claude?: ModelRef;
|
|
332
|
+
gpt?: ModelRef;
|
|
333
|
+
gemini?: ModelRef;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* SSOT of the canonical model KINDS per kind-mapped endpoint.
|
|
337
|
+
*
|
|
338
|
+
* The `messages` (Claude Code) and `responses` (Codex) endpoints route by model
|
|
339
|
+
* KIND rather than role: the user configures one upstream ref per kind, and an
|
|
340
|
+
* incoming versioned client id (`claude-opus-4-8-2026xxxx`) is classified to its
|
|
341
|
+
* kind (`opus`) so CLI upgrades need no reconfig. `chat`/`gemini` are NOT in this
|
|
342
|
+
* map — they keep the role-based (`default`/`background`) config.
|
|
343
|
+
*/
|
|
344
|
+
declare const ENDPOINT_MODEL_KINDS: {
|
|
345
|
+
readonly messages: readonly ["fable", "opus", "sonnet", "haiku"];
|
|
346
|
+
readonly responses: readonly ["codex", "mini"];
|
|
347
|
+
};
|
|
348
|
+
/** The endpoints that route by model kind (`messages` | `responses`). */
|
|
349
|
+
type KindMappedEndpoint = keyof typeof ENDPOINT_MODEL_KINDS;
|
|
350
|
+
/** Claude Code (`messages`) kinds. */
|
|
351
|
+
type MessagesModelKind = (typeof ENDPOINT_MODEL_KINDS)['messages'][number];
|
|
352
|
+
/** Codex (`responses`) kinds. */
|
|
353
|
+
type ResponsesModelKind = (typeof ENDPOINT_MODEL_KINDS)['responses'][number];
|
|
354
|
+
/** Any canonical model kind across the kind-mapped endpoints. */
|
|
355
|
+
type ModelKind = MessagesModelKind | ResponsesModelKind;
|
|
356
|
+
/**
|
|
357
|
+
* Per-endpoint routing config. Persisted independently of the global
|
|
358
|
+
* "默认模型" settings tab; survives restart.
|
|
359
|
+
*
|
|
360
|
+
* The shape is HETEROGENEOUS by endpoint class:
|
|
361
|
+
* - kind-mapped (`messages`/`responses`): `modelMap` (kind → ref) is authoritative;
|
|
362
|
+
* everything else is unused.
|
|
363
|
+
* - list-mapped (`chat`): `models` (a list of refs) is authoritative — the
|
|
364
|
+
* client requests one of the listed modelIds directly (`GET /v1/models`
|
|
365
|
+
* serves the list); no default/background roles.
|
|
366
|
+
* - role-based (`gemini`): `defaultModel`/`backgroundModel` (+ optional
|
|
367
|
+
* `backgroundModelIds`) are authoritative.
|
|
368
|
+
*/
|
|
369
|
+
interface EndpointRoutingConfig {
|
|
370
|
+
/** Which of the four endpoints this block configures. */
|
|
371
|
+
endpoint: OutboundEndpoint;
|
|
372
|
+
/**
|
|
373
|
+
* Kind-mapped endpoints (`messages`/`responses`): model KIND → `"providerId,modelId"`.
|
|
374
|
+
* Keys are the endpoint's declared {@link ENDPOINT_MODEL_KINDS}.
|
|
375
|
+
*/
|
|
376
|
+
modelMap?: Record<string, ModelRef>;
|
|
377
|
+
/**
|
|
378
|
+
* List-mapped endpoint (`chat`): the `"providerId,modelId"` refs this endpoint
|
|
379
|
+
* serves. The ref's modelId is the model name the client requests and the name
|
|
380
|
+
* `GET /v1/models` advertises; a request whose `model` is not in the list is
|
|
381
|
+
* rejected per-request (404-style). Empty ⇒ endpoint unused.
|
|
382
|
+
*/
|
|
383
|
+
models?: ModelRef[];
|
|
384
|
+
/**
|
|
385
|
+
* List-mapped endpoint (`chat`) dispatch mode (openai-chat-bridge #11). Absent
|
|
386
|
+
* ⇒ `'list'` (default) — routing is byte-identical to before this change. Only
|
|
387
|
+
* `'prefix'` changes behavior (route by model-name prefix via `prefixTargets`).
|
|
388
|
+
*/
|
|
389
|
+
dispatchMode?: ChatDispatchMode;
|
|
390
|
+
/**
|
|
391
|
+
* List-mapped endpoint (`chat`) prefix → target map, consumed ONLY when
|
|
392
|
+
* `dispatchMode === 'prefix'` (openai-chat-bridge #11). Ignored in list mode.
|
|
393
|
+
*/
|
|
394
|
+
prefixTargets?: ModelPrefixTargets;
|
|
395
|
+
/** Role-based endpoint (`gemini`): model for normal requests. */
|
|
396
|
+
defaultModel?: ModelRef;
|
|
397
|
+
/** Role-based endpoint (`gemini`): model for background/probe requests. */
|
|
398
|
+
backgroundModel?: ModelRef;
|
|
399
|
+
/** Gates subscription-vs-BYO provider selection. Default FALSE. */
|
|
400
|
+
useSubscription: boolean;
|
|
401
|
+
/**
|
|
402
|
+
* OPTIONAL "background model id" override list (role-based `gemini` only).
|
|
403
|
+
* When set, an incoming requested model id appearing in this list is
|
|
404
|
+
* classified as the BACKGROUND role; otherwise the registry small/haiku-class
|
|
405
|
+
* signal is the baseline. Empty/unset → registry signal only. Compared against
|
|
406
|
+
* the requested model id (bare or `providerId,modelId`).
|
|
407
|
+
*/
|
|
408
|
+
backgroundModelIds?: string[];
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* User-message serial-queue segment (per-upstream-account 防风控). Frozen
|
|
412
|
+
* defaults + valid ranges (SSOT + clamp in `apiServerConfig`):
|
|
413
|
+
* - `enabled` default **false** (opt-in).
|
|
414
|
+
* - `delayMs` default **200**, valid `0..10000` — min gap between one
|
|
415
|
+
* account's serialized requests.
|
|
416
|
+
* - `waitTimeoutMs` default **60000**, valid `1000..300000` — a waiter past
|
|
417
|
+
* this rejects (wire → 503).
|
|
418
|
+
*/
|
|
419
|
+
interface UserMessageQueueConfig {
|
|
420
|
+
enabled: boolean;
|
|
421
|
+
delayMs: number;
|
|
422
|
+
waitTimeoutMs: number;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Per-key concurrency-queue segment (排队而非硬拒). Frozen defaults + valid
|
|
426
|
+
* ranges (SSOT + clamp in `apiServerConfig`):
|
|
427
|
+
* - `maxQueueSizeFactor` default **2**, valid `1..10` — per-key max queued =
|
|
428
|
+
* `max(limit*factor, minQueueSize)`.
|
|
429
|
+
* - `minQueueSize` default **4**, valid `1..100` — floor of the above.
|
|
430
|
+
* - `waitTimeoutMs` default **60000**, valid `1000..300000` — a waiter
|
|
431
|
+
* past this rejects (wire → 429).
|
|
432
|
+
*/
|
|
433
|
+
interface ConcurrencyQueueConfig {
|
|
434
|
+
maxQueueSizeFactor: number;
|
|
435
|
+
minQueueSize: number;
|
|
436
|
+
waitTimeoutMs: number;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Subscription account-health segment (subscription-account-health, LEAD OQ1).
|
|
440
|
+
* Persisted + strictly validated like the queue segments; applied to the shared
|
|
441
|
+
* `SubscriptionAccountHealth` tracker at daemon boot.
|
|
442
|
+
* - `overloadCooldownEnabled` default **true** — a 529 places the account in
|
|
443
|
+
* overload cooldown (ON by default; CRS ships it off).
|
|
444
|
+
* - `overloadCooldownMs` default **600000** (10 min), valid `60000..3600000`.
|
|
445
|
+
*/
|
|
446
|
+
interface AccountHealthConfig {
|
|
447
|
+
overloadCooldownEnabled: boolean;
|
|
448
|
+
overloadCooldownMs: number;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Scheduled account-probe segment (subscription-account-probe, #8 health-cron).
|
|
452
|
+
* The active complement to #2's passive health machine: a cheap, staggered,
|
|
453
|
+
* default-OFF background probe that discovers a dead/expired account BEFORE real
|
|
454
|
+
* traffic does. Persisted + range-clamped like `accountHealth`; consumed by the
|
|
455
|
+
* daemon `AccountHealthProbeScheduler` (an `unref()`ed interval — omnicross has
|
|
456
|
+
* no cron dep). ALL knobs are load-safety valves:
|
|
457
|
+
* - `enabled` default **false** (zero regression — no probes, no /health
|
|
458
|
+
* boolean, byte-identical when off).
|
|
459
|
+
* - `intervalMs` default **900000** (15 min), valid `60000..86400000`.
|
|
460
|
+
* - `onlyMultiAccount` default **true** — probe only providers with ≥2 accounts
|
|
461
|
+
* (where #2 health-gating actually matters; a single-account
|
|
462
|
+
* provider is never probed — #2 keeps it schedulable anyway).
|
|
463
|
+
* - `timeoutMs` default **5000** per upstream probe, valid `1000..60000`.
|
|
464
|
+
* - `historySize` default **10** rolling per-account records, valid `1..200`.
|
|
465
|
+
* - `staggerMs` default **500** gap between consecutive probes, valid
|
|
466
|
+
* `0..60000` (so N accounts never fire N simultaneous GETs).
|
|
467
|
+
*/
|
|
468
|
+
interface AccountProbeConfig {
|
|
469
|
+
enabled: boolean;
|
|
470
|
+
intervalMs: number;
|
|
471
|
+
onlyMultiAccount: boolean;
|
|
472
|
+
timeoutMs: number;
|
|
473
|
+
historySize: number;
|
|
474
|
+
staggerMs: number;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Client-fingerprint segment (subscription-client-fingerprint #7). Opt-in,
|
|
478
|
+
* default OFF: when disabled the claude-subscription outbound headers are
|
|
479
|
+
* byte-identical to before this change (content-type + the OAuth bearer only).
|
|
480
|
+
* When enabled, real client fingerprint headers are captured per account, frozen,
|
|
481
|
+
* and replayed on outbound so relayed traffic presents a stable, REAL per-account
|
|
482
|
+
* client identity. `ua` is an OPTIONAL operator UA baseline applied ONLY when an
|
|
483
|
+
* account has no captured identity yet (never a fabricated stainless value).
|
|
484
|
+
* Persisted + read at daemon boot (like `accountHealth`; a change takes effect on
|
|
485
|
+
* restart). Carries NO secret.
|
|
486
|
+
*/
|
|
487
|
+
interface FingerprintConfig {
|
|
488
|
+
/** Master switch; default false ⇒ no capture/replay ⇒ byte-identical outbound. */
|
|
489
|
+
enabled: boolean;
|
|
490
|
+
/** Operator UA baseline for un-captured accounts (piece 4). Absent ⇒ none. */
|
|
491
|
+
ua?: string;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Layered upstream-proxy segment (upstream-proxy). Global + per-provider proxy
|
|
495
|
+
* config for outbound EGRESS to upstreams (NOT the inbound listener). Resolved
|
|
496
|
+
* with precedence account > provider > global > env; the per-account layer lives
|
|
497
|
+
* on the account entry (`SubscriptionAccountEntry.proxy`), not here. Absent ⇒ no
|
|
498
|
+
* proxy at these layers (direct fetch). `byProvider` keys are subscription
|
|
499
|
+
* provider ids (`claude`/`codex`/`gemini`/`opencodego`) or `'byo'`. A `ProxyConfig`
|
|
500
|
+
* `password` is a SECRET — encrypted at rest + masked in the admin GET.
|
|
501
|
+
*/
|
|
502
|
+
interface OutboundProxyConfig {
|
|
503
|
+
/** Applies to every upstream unless a more specific layer overrides. */
|
|
504
|
+
global?: ProxyConfig;
|
|
505
|
+
/** Per-provider override, keyed by subscription provider id or `'byo'`. */
|
|
506
|
+
byProvider?: Record<string, ProxyConfig>;
|
|
507
|
+
}
|
|
508
|
+
/** The persisted server config. */
|
|
509
|
+
interface OutboundApiServerConfig {
|
|
510
|
+
/** When false the listener never binds (off by default). */
|
|
511
|
+
enabled: boolean;
|
|
512
|
+
/** When true the listener binds `0.0.0.0` (LAN) instead of `127.0.0.1`. */
|
|
513
|
+
networkBinding: boolean;
|
|
514
|
+
/** Per-endpoint routing config, one per endpoint (4 entries). */
|
|
515
|
+
endpoints: EndpointRoutingConfig[];
|
|
516
|
+
/** Persisted port (fixed default; falls back to ephemeral on EADDRINUSE). */
|
|
517
|
+
port?: number;
|
|
518
|
+
/**
|
|
519
|
+
* User-message serial queue segment. Optional in the persisted shape;
|
|
520
|
+
* `normalizeServerConfig` always fills it with the frozen defaults, so a
|
|
521
|
+
* normalized config carries it.
|
|
522
|
+
*/
|
|
523
|
+
userMessageQueue?: UserMessageQueueConfig;
|
|
524
|
+
/**
|
|
525
|
+
* Per-key concurrency queue segment. Optional in the persisted shape;
|
|
526
|
+
* `normalizeServerConfig` always fills it with the frozen defaults.
|
|
527
|
+
*/
|
|
528
|
+
concurrencyQueue?: ConcurrencyQueueConfig;
|
|
529
|
+
/**
|
|
530
|
+
* Subscription account-health segment. Optional in the persisted shape;
|
|
531
|
+
* `normalizeServerConfig` always fills it with the frozen defaults. Applied to
|
|
532
|
+
* the shared health tracker at daemon boot (change takes effect on restart).
|
|
533
|
+
*/
|
|
534
|
+
accountHealth?: AccountHealthConfig;
|
|
535
|
+
/**
|
|
536
|
+
* Scheduled account-probe segment (subscription-account-probe #8). Optional in
|
|
537
|
+
* the persisted shape; `normalizeServerConfig` always fills it with the frozen
|
|
538
|
+
* defaults (enabled:false). Read by the daemon `AccountHealthProbeScheduler` at
|
|
539
|
+
* boot; a change takes effect on restart. Default-off ⇒ no scheduler runs.
|
|
540
|
+
*/
|
|
541
|
+
accountProbe?: AccountProbeConfig;
|
|
542
|
+
/**
|
|
543
|
+
* Layered upstream-proxy segment (upstream-proxy). Optional; when absent no
|
|
544
|
+
* global/provider proxy applies (direct egress). `normalizeServerConfig` drops
|
|
545
|
+
* malformed entries but does NOT synthesize a default (a missing proxy segment
|
|
546
|
+
* stays absent — zero-config = direct fetch).
|
|
547
|
+
*/
|
|
548
|
+
proxy?: OutboundProxyConfig;
|
|
549
|
+
/**
|
|
550
|
+
* Webhook notification segment (webhook-notifications). Optional; when absent
|
|
551
|
+
* (or `enabled:false`) the emit sink is never wired ⇒ `emitWebhookEvent` is a
|
|
552
|
+
* no-op ⇒ byte-identical zero regression. `normalizeServerConfig` drops
|
|
553
|
+
* malformed destinations but does NOT synthesize a default (a missing webhook
|
|
554
|
+
* segment stays absent). A destination `secret` is encrypted at rest + masked
|
|
555
|
+
* in admin views.
|
|
556
|
+
*/
|
|
557
|
+
webhook?: WebhookConfig;
|
|
558
|
+
/**
|
|
559
|
+
* Request-audit segment (request-audit-log). Optional; when absent (or
|
|
560
|
+
* `enabled:false`) no capture config or sink is wired ⇒ the capture hook does
|
|
561
|
+
* nothing ⇒ byte-identical zero regression. `normalizeServerConfig` always
|
|
562
|
+
* fills it with the frozen defaults (enabled:false). Read by the daemon at boot
|
|
563
|
+
* + admin PUT to (un)register the core sink + capture config; a change takes
|
|
564
|
+
* effect immediately (hot-reloaded like `webhook`). Carries NO secret.
|
|
565
|
+
*/
|
|
566
|
+
audit?: AuditConfig;
|
|
567
|
+
/**
|
|
568
|
+
* Billing-event-stream segment (billing-event-stream). Optional; when absent
|
|
569
|
+
* (or `enabled:false`) no sink is wired ⇒ `publishBillingEvent` is a no-op ⇒ no
|
|
570
|
+
* ledger append, no POST, byte-identical zero regression.
|
|
571
|
+
* `normalizeServerConfig` always fills it with the frozen defaults
|
|
572
|
+
* (enabled:false). Read by the daemon at boot + admin PUT to (un)register the
|
|
573
|
+
* core sink + capture gate; a change takes effect immediately (hot-reloaded like
|
|
574
|
+
* `webhook`/`audit`). Carries ONE secret (`secret`, the HMAC key — encrypted at
|
|
575
|
+
* rest + masked in admin).
|
|
576
|
+
*/
|
|
577
|
+
billing?: BillingConfig;
|
|
578
|
+
/**
|
|
579
|
+
* Client-fingerprint segment (subscription-client-fingerprint #7). Optional in
|
|
580
|
+
* the persisted shape; `normalizeServerConfig` always fills it with the frozen
|
|
581
|
+
* defaults (enabled:false). Read at daemon boot to `configure` + seed + wire
|
|
582
|
+
* persistence on the shared identity store; a change takes effect on restart.
|
|
583
|
+
* Default-off ⇒ no capture/replay ⇒ byte-identical outbound headers.
|
|
584
|
+
*/
|
|
585
|
+
fingerprint?: FingerprintConfig;
|
|
586
|
+
/**
|
|
587
|
+
* Voucher (redemption-card) segment (voucher-redemption #9). Optional in the
|
|
588
|
+
* persisted shape; `normalizeServerConfig` always fills it with the frozen
|
|
589
|
+
* defaults (enabled:false). When disabled (the default) the `POST /redeem`
|
|
590
|
+
* endpoint rejects + no admin generate is offered ⇒ no key is ever mutated ⇒
|
|
591
|
+
* byte-identical zero regression, purely additive on the #4 key-policy.
|
|
592
|
+
*/
|
|
593
|
+
voucher?: VoucherConfig;
|
|
594
|
+
}
|
|
595
|
+
/** A live status snapshot the Settings tab renders. */
|
|
596
|
+
interface OutboundApiServerStatus {
|
|
597
|
+
running: boolean;
|
|
598
|
+
/** Actual bound port (0 when not running). */
|
|
599
|
+
port: number;
|
|
600
|
+
/** Loopback base URL (always present when running). */
|
|
601
|
+
loopbackUrl: string | null;
|
|
602
|
+
/** LAN base URL (present only when network binding is on). */
|
|
603
|
+
lanUrl: string | null;
|
|
604
|
+
/** The four format endpoint URLs (loopback). */
|
|
605
|
+
formats: OutboundFormatUrls | null;
|
|
606
|
+
/** The four format endpoint URLs over the LAN (only when network binding on). */
|
|
607
|
+
lanFormats: OutboundFormatUrls | null;
|
|
608
|
+
}
|
|
609
|
+
/** The four format endpoint URLs for one base. */
|
|
610
|
+
interface OutboundFormatUrls {
|
|
611
|
+
chat: string;
|
|
612
|
+
responses: string;
|
|
613
|
+
messages: string;
|
|
614
|
+
gemini: string;
|
|
615
|
+
}
|
|
616
|
+
/** A stored key DTO (never carries the plaintext secret). */
|
|
617
|
+
interface OutboundApiKeyInfo {
|
|
618
|
+
id: string;
|
|
619
|
+
name: string;
|
|
620
|
+
keyPrefix: string;
|
|
621
|
+
enabled: boolean;
|
|
622
|
+
createdAt: number;
|
|
623
|
+
lastUsedAt: number | null;
|
|
624
|
+
revoked: boolean;
|
|
625
|
+
/**
|
|
626
|
+
* Per-key concurrency ceiling for the outbound concurrency gate. Absent or
|
|
627
|
+
* `0` = unlimited (the gate is bypassed entirely for this key).
|
|
628
|
+
*/
|
|
629
|
+
maxConcurrency?: number;
|
|
630
|
+
/** Fixed-mode absolute expiry (epoch ms). */
|
|
631
|
+
expiresAt?: number | null;
|
|
632
|
+
/** Expiry mode; absent ⇒ `'fixed'`. */
|
|
633
|
+
activationMode?: OutboundKeyActivationMode;
|
|
634
|
+
/** Activation-mode lifetime in days. */
|
|
635
|
+
activationDays?: number | null;
|
|
636
|
+
/** First-use activation stamp (epoch ms); written once, surfaced read-only. */
|
|
637
|
+
activatedAt?: number | null;
|
|
638
|
+
/** Daily USD cost cap. */
|
|
639
|
+
dailyCostLimitUsd?: number | null;
|
|
640
|
+
/** Lifetime USD cost cap. */
|
|
641
|
+
totalCostLimitUsd?: number | null;
|
|
642
|
+
/** Weekly USD cost cap. */
|
|
643
|
+
weeklyCostLimitUsd?: number | null;
|
|
644
|
+
/** Per-key rate-limit max requests per window (absent ⇒ 60; `0` ⇒ unlimited). */
|
|
645
|
+
rateLimitMaxRequests?: number | null;
|
|
646
|
+
/** Per-key rate-limit window (ms; absent ⇒ 60_000). */
|
|
647
|
+
rateLimitWindowMs?: number | null;
|
|
648
|
+
/** Master switch: when false/unset the model list is inert (no restriction). */
|
|
649
|
+
enableModelRestriction?: boolean;
|
|
650
|
+
/** Restriction mode; absent ⇒ `'blacklist'` (CRS-parity). */
|
|
651
|
+
restrictionMode?: OutboundKeyModelRestrictionMode;
|
|
652
|
+
/** The model-id list the mode acts on (bare modelIds; empty allowlist denies all). */
|
|
653
|
+
restrictedModels?: string[];
|
|
654
|
+
/**
|
|
655
|
+
* The key's OWN accumulated spend (outbound-key-policy), surfaced by the admin
|
|
656
|
+
* so an operator sees spend-vs-limit. Present only when the host wired a spend
|
|
657
|
+
* reader. Leak-safe: each key carries only ITS own numbers — the same data the
|
|
658
|
+
* key's holder already sees in a 402 body.
|
|
659
|
+
*/
|
|
660
|
+
spend?: {
|
|
661
|
+
dailyUsd: number;
|
|
662
|
+
weeklyUsd: number;
|
|
663
|
+
totalUsd: number;
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
/** Expiry mode for an outbound key: fixed absolute vs first-use activation. */
|
|
667
|
+
type OutboundKeyActivationMode = 'fixed' | 'activation';
|
|
668
|
+
/**
|
|
669
|
+
* Per-key model-restriction mode (outbound-key-policy #6). `blacklist` denies the
|
|
670
|
+
* listed models; `allowlist` permits ONLY the listed models (an empty allowlist
|
|
671
|
+
* denies everything — a deliberate "disabled by model" state).
|
|
672
|
+
*/
|
|
673
|
+
type OutboundKeyModelRestrictionMode = 'blacklist' | 'allowlist';
|
|
674
|
+
/**
|
|
675
|
+
* The settable key-policy envelope (`outboundApiKeysSetPolicy`). Every field is
|
|
676
|
+
* three-way: a value SETS, explicit `null` CLEARS, and OMISSION keeps the stored
|
|
677
|
+
* value (mirrors the `maxConcurrency` write contract). `activatedAt` is NOT here
|
|
678
|
+
* — it is written once by `outboundApiKeysMarkActivated`, never operator-set.
|
|
679
|
+
* The #6/#9 children extend this shape; do not drift the frozen fields.
|
|
680
|
+
*/
|
|
681
|
+
interface OutboundKeyPolicy {
|
|
682
|
+
expiresAt?: number | null;
|
|
683
|
+
activationMode?: OutboundKeyActivationMode | null;
|
|
684
|
+
activationDays?: number | null;
|
|
685
|
+
dailyCostLimitUsd?: number | null;
|
|
686
|
+
totalCostLimitUsd?: number | null;
|
|
687
|
+
weeklyCostLimitUsd?: number | null;
|
|
688
|
+
rateLimitMaxRequests?: number | null;
|
|
689
|
+
rateLimitWindowMs?: number | null;
|
|
690
|
+
enableModelRestriction?: boolean | null;
|
|
691
|
+
restrictionMode?: OutboundKeyModelRestrictionMode | null;
|
|
692
|
+
restrictedModels?: string[] | null;
|
|
693
|
+
}
|
|
694
|
+
/** The one-time create result; `plaintextOnce` is shown exactly once. */
|
|
695
|
+
interface OutboundApiKeyCreated {
|
|
696
|
+
id: string;
|
|
697
|
+
name: string;
|
|
698
|
+
keyPrefix: string;
|
|
699
|
+
createdAt: number;
|
|
700
|
+
plaintextOnce: string;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Structural subset of the DB the outbound server needs — the named-key CRUD
|
|
704
|
+
* surface. Kept structural so the server module does not depend on the full
|
|
705
|
+
* `DbRpc`.
|
|
706
|
+
*/
|
|
707
|
+
interface OutboundKeyDbRow {
|
|
708
|
+
id: string;
|
|
709
|
+
name: string;
|
|
710
|
+
keyHash: string;
|
|
711
|
+
keyPrefix: string;
|
|
712
|
+
enabled: boolean;
|
|
713
|
+
createdAt: number;
|
|
714
|
+
lastUsedAt: number | null;
|
|
715
|
+
revokedAt: number | null;
|
|
716
|
+
/**
|
|
717
|
+
* Per-key concurrency ceiling. Absent/`0` = unlimited = the concurrency gate
|
|
718
|
+
* is bypassed for this key. Persisted by the daemon (`omnicross-uqc-daemon`).
|
|
719
|
+
*/
|
|
720
|
+
maxConcurrency?: number;
|
|
721
|
+
/** Fixed-mode absolute expiry (epoch ms). */
|
|
722
|
+
expiresAt?: number | null;
|
|
723
|
+
/** Expiry mode; absent ⇒ `'fixed'`. */
|
|
724
|
+
activationMode?: OutboundKeyActivationMode;
|
|
725
|
+
/** Activation-mode lifetime in days. */
|
|
726
|
+
activationDays?: number | null;
|
|
727
|
+
/** First-use activation stamp (epoch ms); written ONCE on first successful use. */
|
|
728
|
+
activatedAt?: number | null;
|
|
729
|
+
/** Daily USD cost cap. */
|
|
730
|
+
dailyCostLimitUsd?: number | null;
|
|
731
|
+
/** Lifetime USD cost cap. */
|
|
732
|
+
totalCostLimitUsd?: number | null;
|
|
733
|
+
/** Weekly USD cost cap. */
|
|
734
|
+
weeklyCostLimitUsd?: number | null;
|
|
735
|
+
/** Per-key rate-limit max requests per window (absent ⇒ 60; `0` ⇒ unlimited). */
|
|
736
|
+
rateLimitMaxRequests?: number | null;
|
|
737
|
+
/** Per-key rate-limit window (ms; absent ⇒ 60_000). */
|
|
738
|
+
rateLimitWindowMs?: number | null;
|
|
739
|
+
/** Master switch; false/unset ⇒ no model check (zero-regression gate). */
|
|
740
|
+
enableModelRestriction?: boolean;
|
|
741
|
+
/** Restriction mode; absent ⇒ `'blacklist'`. */
|
|
742
|
+
restrictionMode?: OutboundKeyModelRestrictionMode;
|
|
743
|
+
/** The model-id list the mode acts on (bare modelIds). */
|
|
744
|
+
restrictedModels?: string[];
|
|
745
|
+
}
|
|
746
|
+
interface OutboundKeyDb {
|
|
747
|
+
outboundApiKeysList(): Promise<OutboundKeyDbRow[]>;
|
|
748
|
+
outboundApiKeysGetByHash(hash: string): Promise<OutboundKeyDbRow | null>;
|
|
749
|
+
outboundApiKeysCreate(input: {
|
|
750
|
+
id: string;
|
|
751
|
+
name: string;
|
|
752
|
+
keyHash: string;
|
|
753
|
+
keyPrefix: string;
|
|
754
|
+
createdAt?: number;
|
|
755
|
+
}): Promise<OutboundKeyDbRow>;
|
|
756
|
+
outboundApiKeysRevoke(id: string): Promise<boolean>;
|
|
757
|
+
outboundApiKeysTouchLastUsed(id: string): Promise<boolean>;
|
|
758
|
+
outboundApiKeysSetEnabled(id: string, enabled: boolean): Promise<boolean>;
|
|
759
|
+
/**
|
|
760
|
+
* Set (or clear) a key's per-key concurrency ceiling. `null` clears the field
|
|
761
|
+
* → unlimited (gate bypassed). Mirrors `outboundApiKeysSetEnabled`; returns
|
|
762
|
+
* `false` when the key is missing/revoked. Implemented by the daemon
|
|
763
|
+
* (`omnicross-uqc-daemon`); test fakes implement it inline.
|
|
764
|
+
*/
|
|
765
|
+
outboundApiKeysSetMaxConcurrency(id: string, maxConcurrency: number | null): Promise<boolean>;
|
|
766
|
+
/**
|
|
767
|
+
* Set (or clear) a key's policy envelope (expiry / activation window / cost
|
|
768
|
+
* limits / per-key rate) in one write (outbound-key-policy). Each field is
|
|
769
|
+
* three-way — a value sets, explicit `null` clears, omission keeps — mirroring
|
|
770
|
+
* `outboundApiKeysSetMaxConcurrency`. Returns `false` when the key is
|
|
771
|
+
* missing/revoked. Does NOT touch `activatedAt`.
|
|
772
|
+
*/
|
|
773
|
+
outboundApiKeysSetPolicy(id: string, policy: OutboundKeyPolicy): Promise<boolean>;
|
|
774
|
+
/**
|
|
775
|
+
* Stamp the one-time first-use activation (`activatedAt`) for an
|
|
776
|
+
* activation-mode key. Best-effort like `outboundApiKeysTouchLastUsed`; a
|
|
777
|
+
* no-op returning `false` when the key is missing/revoked or already
|
|
778
|
+
* activated (the original stamp is never overwritten — activation is
|
|
779
|
+
* idempotent).
|
|
780
|
+
*/
|
|
781
|
+
outboundApiKeysMarkActivated(id: string, activatedAt: number): Promise<boolean>;
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* App-session-scoped dependencies the outbound server needs. The resident
|
|
785
|
+
* `ProviderProxy` is shared so the new listener mints routes on the SAME route
|
|
786
|
+
* map and reuses the SAME ingress parsers + transformer.
|
|
787
|
+
*/
|
|
788
|
+
interface OutboundApiDeps {
|
|
789
|
+
readonly db: OutboundKeyDb;
|
|
790
|
+
/**
|
|
791
|
+
* OPTIONAL voucher store (voucher-redemption #9). When wired (by the daemon
|
|
792
|
+
* bootstrap), the key-authenticated `POST /redeem` endpoint can redeem cards
|
|
793
|
+
* against the presenting key (gated on `voucher.enabled`). Absent ⇒ `/redeem`
|
|
794
|
+
* is inert (byte-identical zero-regression for embedders/tests that do not wire
|
|
795
|
+
* it, and whenever the feature is disabled).
|
|
796
|
+
*/
|
|
797
|
+
readonly voucherDb?: VoucherDb;
|
|
798
|
+
readonly llmConfig: ProviderConfigSource;
|
|
799
|
+
/** The resident proxy — shared for route minting + ingress dispatch. */
|
|
800
|
+
readonly providerProxy: ProviderProxy;
|
|
801
|
+
/** The proxy's app-session deps (reused verbatim for `routeRequest`). */
|
|
802
|
+
readonly proxyDeps: ProviderProxyDeps;
|
|
803
|
+
/**
|
|
804
|
+
* OPTIONAL unauthenticated `/health` provider (daemon-health-endpoint, D1
|
|
805
|
+
* secondary mount). When wired (by the daemon bootstrap — core NEVER imports
|
|
806
|
+
* daemon), the server serves `GET|HEAD /health` (+ `/healthz`) BEFORE its
|
|
807
|
+
* per-request key-auth, so an orchestrator can probe the TRAFFIC port. Absent
|
|
808
|
+
* ⇒ `/health` is not mounted and falls through to normal auth (byte-identical
|
|
809
|
+
* zero-regression for embedders that do not wire it).
|
|
810
|
+
*/
|
|
811
|
+
readonly healthReportProvider?: () => HealthReport;
|
|
812
|
+
/**
|
|
813
|
+
* OPTIONAL per-key spend reader (outbound-key-policy). When wired (by the
|
|
814
|
+
* daemon bootstrap), a key carrying a cost limit is checked against its
|
|
815
|
+
* accumulated spend before dispatch (→ 402). Absent ⇒ NO cost-quota check runs
|
|
816
|
+
* (byte-identical zero-regression for embedders/tests that do not wire it, and
|
|
817
|
+
* for every policy-less key regardless).
|
|
818
|
+
*/
|
|
819
|
+
readonly keySpendTracker?: KeySpendReader;
|
|
820
|
+
/**
|
|
821
|
+
* OPTIONAL injected logger (configurable-logging). When wired (by the daemon
|
|
822
|
+
* bootstrap), the server's OWN lifecycle lines (listen/stop/error) + the relay
|
|
823
|
+
* dispatch-error route through it — so they honor the configured level / format
|
|
824
|
+
* / file sink. Absent ⇒ the legacy `console.*` fallback (byte-identical for
|
|
825
|
+
* embedders/tests that do not wire it).
|
|
826
|
+
*/
|
|
827
|
+
readonly logger?: Logger;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
export { type AccountProbeConfig as A, computeVoucherGrant as B, type ChatDispatchMode as C, generateVoucherCode as D, ENDPOINT_MODEL_KINDS as E, type FingerprintConfig as F, hashVoucherCode as G, newVoucherId as H, startOfLocalDay as I, startOfLocalWeek as J, type KeyCostLimits as K, toVoucherInfo as L, type MessagesModelKind as M, voucherCodePrefix as N, type OutboundKeyDb as O, type ModelRef as P, type QuotaDecision as Q, type RequestRole as R, type ModelRestriction as S, type AccountHealthConfig as T, type UserMessageQueueConfig as U, type VoucherCreateInput as V, type OutboundKeyModelRestrictionMode as W, type ConcurrencyQueueConfig as a, type EndpointRoutingConfig as b, type KeyExpiryInput as c, type KeyExpiryResult as d, type KeySpend as e, type KeySpendReader as f, type KeySpendSeeder as g, KeySpendTracker as h, type KindMappedEndpoint as i, type ModelKind as j, type ModelPrefixTargets as k, type OutboundApiDeps as l, type OutboundApiKeyCreated as m, type OutboundApiKeyInfo as n, type OutboundApiServerConfig as o, type OutboundApiServerStatus as p, type OutboundEndpoint as q, type OutboundFormatUrls as r, type OutboundKeyActivationMode as s, type OutboundKeyDbRow as t, type OutboundKeyPolicy as u, type OutboundProxyConfig as v, type ResponsesModelKind as w, type VoucherDb as x, checkKeyQuota as y, computeKeyExpiry as z };
|