@omnicross/daemon 0.3.0 → 0.4.0
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/cli.cjs +2787 -359
- package/dist/cli.js +2743 -297
- package/dist/index.cjs +2717 -429
- package/dist/index.d.cts +486 -28
- package/dist/index.d.ts +486 -28
- package/dist/index.js +2583 -284
- package/package.json +6 -6
package/dist/index.d.cts
CHANGED
|
@@ -5,8 +5,8 @@ import { AllowanceSchedulingConfig, AccountProbeConfig, OutboundKeyDb as Outboun
|
|
|
5
5
|
import { RouteLeaseManager, ProviderProxy } from '@omnicross/core/provider-proxy';
|
|
6
6
|
import { UsageRecorder, PricingEngine } from '@omnicross/core/usage';
|
|
7
7
|
import { SubscriptionCredentialStore, FetchLike, CodexImageCapabilityEvidenceSource, CodexImageCapabilityEvidenceRequest, CodexImageCapabilityEvidence, CodexImageCapabilityObservation, ImageExecutionScheduler, ImageExecutionAccountKey, ImageExecutionSchedulerRequest, ImageExecutionSchedulerGrant, SubscriptionAccountService, SubscriptionProviderRegistry } from '@omnicross/subscriptions';
|
|
8
|
-
import { AccountAllowanceSnapshot } from '@omnicross/contracts/account-allowance-types';
|
|
9
|
-
import { ClaudeTokenConfig, CodexTokenConfig, GeminiTokenConfig, AccountTokensConfig, ProxyConfig, SubscriptionAccountSanitized, AccountClientIdentity, SubscriptionAccountEntry } from '@omnicross/contracts/account-tokens-types';
|
|
8
|
+
import { AccountAllowanceSnapshot, AllowanceWindow } from '@omnicross/contracts/account-allowance-types';
|
|
9
|
+
import { ClaudeTokenConfig, CodexTokenConfig, GeminiTokenConfig, KimiTokenConfig, GrokTokenConfig, CopilotTokenConfig, AccountTokensConfig, ProxyConfig, SubscriptionAccountSanitized, AccountClientIdentity, SubscriptionAccountEntry } from '@omnicross/contracts/account-tokens-types';
|
|
10
10
|
import * as _omnicross_contracts_subscription_types from '@omnicross/contracts/subscription-types';
|
|
11
11
|
import { OpenCodeGoTokenConfig, SubscriptionProviderId } from '@omnicross/contracts/subscription-types';
|
|
12
12
|
import { AccountAllowanceStore } from '@omnicross/core/pipeline/AccountAllowanceStore';
|
|
@@ -536,6 +536,18 @@ interface DaemonProviderConfig {
|
|
|
536
536
|
* round-trips verbatim on GET. Management-UI only.
|
|
537
537
|
*/
|
|
538
538
|
modelsEndpoint?: string;
|
|
539
|
+
/**
|
|
540
|
+
* OPTIONAL static extra request headers merged into every BYO request for
|
|
541
|
+
* this row — identity/attribution contracts some gateways hard-gate on
|
|
542
|
+
* (e.g. the Cline client-identity set). Additive + back-compat: absent
|
|
543
|
+
* reads as undefined. NON-SECRET — round-trips verbatim on GET; values may
|
|
544
|
+
* carry `{{platform}}` (resolved to `process.platform` at request time).
|
|
545
|
+
* ENFORCED: the outbound header funnel (`getProviderHeaders`) +
|
|
546
|
+
* discover-models + test-model merge them; auth/content header NAMES are
|
|
547
|
+
* dropped by the load guard (`validateExtraHeaders`) so credentials stay
|
|
548
|
+
* key-field-only.
|
|
549
|
+
*/
|
|
550
|
+
extraHeaders?: Record<string, string>;
|
|
539
551
|
/**
|
|
540
552
|
* OPTIONAL provider transformer config (app-parity child 5). Additive +
|
|
541
553
|
* back-compat: absent reads as the prior default (undefined). NON-SECRET —
|
|
@@ -626,6 +638,32 @@ declare function loadConfig(path: string): DaemonConfig;
|
|
|
626
638
|
* `enc:` untouched) before serializing. */
|
|
627
639
|
declare function saveConfig(path: string, cfg: DaemonConfig): void;
|
|
628
640
|
|
|
641
|
+
/**
|
|
642
|
+
* atomicFile — the daemon's shared ATOMIC file-replace for credential-bearing
|
|
643
|
+
* state files (`keys.json`, `tokens.json`, …).
|
|
644
|
+
*
|
|
645
|
+
* A bare `writeFileSync(target, …)` is truncate-then-write: a crash, ENOSPC,
|
|
646
|
+
* or power loss mid-write leaves the target HALF-WRITTEN and the prior content
|
|
647
|
+
* is gone. For `tokens.json` that is account data — the 2026-09-06 incident
|
|
648
|
+
* lost every stored OAuth account exactly this way (a truncated file that the
|
|
649
|
+
* reader then silently treated as "no accounts"). Same-directory temp-write +
|
|
650
|
+
* fsync + rename means a FAILED write only ever discards the temp file; the
|
|
651
|
+
* prior target survives byte-equal.
|
|
652
|
+
*
|
|
653
|
+
* The temp name embeds pid + random bytes (never collides with a live temp of
|
|
654
|
+
* another process) and lives in the target's own directory so the final
|
|
655
|
+
* `renameSync` never crosses a volume (POSIX and win32 both make the replace
|
|
656
|
+
* atomic there; Node maps rename-over-existing to MoveFileEx with
|
|
657
|
+
* REPLACE_EXISTING on Windows).
|
|
658
|
+
*
|
|
659
|
+
* Extracted verbatim from `JsonOutboundKeyDb` (its original + only user) so
|
|
660
|
+
* every file-backed port shares ONE implementation instead of re-deriving it.
|
|
661
|
+
*
|
|
662
|
+
* @module @omnicross/daemon/ports/atomicFile
|
|
663
|
+
*/
|
|
664
|
+
/** Injectable seam (tests pass a throwing/stubbed replace to prove the prior file survives). */
|
|
665
|
+
type AtomicFileReplace = (targetPath: string, contents: string) => void;
|
|
666
|
+
|
|
629
667
|
/**
|
|
630
668
|
* account-multi — daemon-side pure helpers for the subscription multi-account
|
|
631
669
|
* layout.
|
|
@@ -640,7 +678,7 @@ declare function saveConfig(path: string, cfg: DaemonConfig): void;
|
|
|
640
678
|
*/
|
|
641
679
|
|
|
642
680
|
/** Provider id → owned contract field names. */
|
|
643
|
-
type DaemonProvider = 'claude' | 'codex' | 'gemini' | 'opencodego';
|
|
681
|
+
type DaemonProvider = 'claude' | 'codex' | 'gemini' | 'opencodego' | 'kimi' | 'grok' | 'copilot';
|
|
644
682
|
interface AccountMetadataPatch {
|
|
645
683
|
label?: string;
|
|
646
684
|
enabled?: boolean;
|
|
@@ -710,9 +748,14 @@ type ExternalCliReader = (provider: ExternalCliProvider) => ExternalCliCredentia
|
|
|
710
748
|
* over a sibling `tokens.json` holding an `AccountTokensConfig`-shaped object
|
|
711
749
|
* (`{ claude?, codex?, gemini?, opencodego?, updatedAt }`). Modeled on
|
|
712
750
|
* `JsonOutboundKeyDb`: the constructor takes the path; reads are
|
|
713
|
-
* `existsSync` `readFileSync` `JSON.parse
|
|
714
|
-
*
|
|
715
|
-
*
|
|
751
|
+
* `existsSync` `readFileSync` `JSON.parse`. A MISSING file returns a minimal
|
|
752
|
+
* `{ updatedAt }` config (first boot; the strategies already guard
|
|
753
|
+
* `?.accessToken`, so a partial/empty config never crashes dispatch). A file
|
|
754
|
+
* that EXISTS but will not parse as a JSON object is first QUARANTINED to a
|
|
755
|
+
* sibling `tokens.json.corrupt-<stamp>` backup (see `quarantineCorrupt`) and
|
|
756
|
+
* only then treated as empty — a corrupt file is DATA, not "no accounts"
|
|
757
|
+
* (2026-09-06 incident: a truncated write read as empty, then the re-login
|
|
758
|
+
* persist overwrote the only copy of every stored account).
|
|
716
759
|
*
|
|
717
760
|
* The PORT surface is read-only by design: the codex / gemini strategies pull
|
|
718
761
|
* their access token via `getFullConfig().<provider>.accessToken`; only claude /
|
|
@@ -736,6 +779,12 @@ type ExternalCliReader = (provider: ExternalCliProvider) => ExternalCliCredentia
|
|
|
736
779
|
* at-rest with NO extra work (the store API guarantees it). The "re-read on every
|
|
737
780
|
* call, no cache" semantics are unchanged.
|
|
738
781
|
*
|
|
782
|
+
* DURABILITY: `persist` writes through the shared same-directory temp + fsync +
|
|
783
|
+
* rename (`atomicReplaceUtf8`) — a crashed/failed/interrupted write discards
|
|
784
|
+
* only the temp file and the prior `tokens.json` survives byte-equal. This is
|
|
785
|
+
* the other half of the 2026-09-06 fix: the store previously used a bare
|
|
786
|
+
* truncate-then-write `writeFileSync`, which could leave a half-written file.
|
|
787
|
+
*
|
|
739
788
|
* REAL TOKEN REFRESH (oauth design D4): `refresh{Claude,Codex,Gemini}Token` mint
|
|
740
789
|
* a new access token via the shared host-clean OAuth refresh functions
|
|
741
790
|
* (`@omnicross/subscriptions/oauth`, injected `FetchLike` default global
|
|
@@ -758,13 +807,15 @@ type ExternalCliReader = (provider: ExternalCliProvider) => ExternalCliCredentia
|
|
|
758
807
|
* shapes), keyed by `SubscriptionProviderId` the daemon admin layer validates
|
|
759
808
|
* the wire body to one of these before calling the writer.
|
|
760
809
|
*/
|
|
761
|
-
type SubscriptionTokenBlock = ClaudeTokenConfig | CodexTokenConfig | GeminiTokenConfig | OpenCodeGoTokenConfig;
|
|
810
|
+
type SubscriptionTokenBlock = ClaudeTokenConfig | CodexTokenConfig | GeminiTokenConfig | OpenCodeGoTokenConfig | KimiTokenConfig | GrokTokenConfig | CopilotTokenConfig;
|
|
762
811
|
declare class JsonSubscriptionCredentialStore implements SubscriptionCredentialStore {
|
|
763
812
|
private readonly tokensPath;
|
|
764
813
|
private readonly box;
|
|
765
814
|
private readonly fetchImpl;
|
|
766
815
|
/** Injectable, strictly read-only external CLI native-store reader. */
|
|
767
816
|
private readonly externalCliReader;
|
|
817
|
+
/** Injectable atomic-replace seam (tests prove a failed write keeps the prior file). */
|
|
818
|
+
private readonly atomicReplace;
|
|
768
819
|
/**
|
|
769
820
|
* @param tokensPath on-disk `tokens.json` location.
|
|
770
821
|
* @param box at-rest `SecretBox` (encrypt-on-write / decrypt-on-read).
|
|
@@ -779,7 +830,9 @@ declare class JsonSubscriptionCredentialStore implements SubscriptionCredentialS
|
|
|
779
830
|
*/
|
|
780
831
|
constructor(tokensPath: string, box: SecretBox, fetchImpl?: FetchLike | undefined,
|
|
781
832
|
/** Injectable, strictly read-only external CLI native-store reader. */
|
|
782
|
-
externalCliReader?: ExternalCliReader
|
|
833
|
+
externalCliReader?: ExternalCliReader,
|
|
834
|
+
/** Injectable atomic-replace seam (tests prove a failed write keeps the prior file). */
|
|
835
|
+
atomicReplace?: AtomicFileReplace);
|
|
783
836
|
/**
|
|
784
837
|
* The proxy-aware `FetchLike` for one refresh round-trip (upstream-proxy M1). A
|
|
785
838
|
* TEST-injected `fetchImpl` is returned verbatim; otherwise the refresh routes
|
|
@@ -858,13 +911,36 @@ declare class JsonSubscriptionCredentialStore implements SubscriptionCredentialS
|
|
|
858
911
|
* destroy the ability to refresh again). HONEST `false` when no refresh_token.
|
|
859
912
|
*/
|
|
860
913
|
refreshGeminiToken(): Promise<boolean>;
|
|
914
|
+
/**
|
|
915
|
+
* Refresh the Kimi Code (Moonshot) OAuth access token (device-flow grant).
|
|
916
|
+
* Kimi ROTATES the refresh token, so the response's pair is written back
|
|
917
|
+
* whole; the account's stable `deviceId` (fingerprint header input) is
|
|
918
|
+
* preserved. The refresh call carries the CLI fingerprint headers. HONEST
|
|
919
|
+
* `false` when no refresh_token.
|
|
920
|
+
*/
|
|
921
|
+
refreshKimiToken(): Promise<boolean>;
|
|
922
|
+
/**
|
|
923
|
+
* Refresh the Grok (xAI SuperGrok) OAuth access token. The token endpoint is
|
|
924
|
+
* resolved through OIDC discovery on every refresh (process-cached 1h by the
|
|
925
|
+
* flow module) so a rotated endpoint document is picked up without a daemon
|
|
926
|
+
* restart. HONEST `false` when no refresh_token.
|
|
927
|
+
*/
|
|
928
|
+
refreshGrokToken(): Promise<boolean>;
|
|
929
|
+
/**
|
|
930
|
+
* "Refresh" a GitHub Copilot token — there is nothing to refresh (ghu_
|
|
931
|
+
* tokens are long-lived with no exchange endpoint). A call here means the
|
|
932
|
+
* strategy saw a 401 (the token was revoked); mark the account `expired`
|
|
933
|
+
* with a re-authenticate message and return `false` (the proxy then declines
|
|
934
|
+
* the retry instead of looping on a dead token).
|
|
935
|
+
*/
|
|
936
|
+
refreshCopilotToken(): Promise<boolean>;
|
|
861
937
|
/**
|
|
862
938
|
* Refresh a SPECIFIC managed account by id (background scheduler sweep and
|
|
863
939
|
* account-pool resolution). It uses only that account's stored refresh
|
|
864
940
|
* token. Coalesced per `provider:id`; on failure flags ONLY that account
|
|
865
941
|
* `expired`.
|
|
866
942
|
*/
|
|
867
|
-
refreshAccountById(provider: 'claude' | 'codex' | 'gemini', id: string): Promise<boolean>;
|
|
943
|
+
refreshAccountById(provider: 'claude' | 'codex' | 'gemini' | 'kimi' | 'grok' | 'copilot', id: string): Promise<boolean>;
|
|
868
944
|
/**
|
|
869
945
|
* Resolve a SPECIFIC account's access token by id (design D6). Mirrors each
|
|
870
946
|
* provider's ACTIVE-getter policy, keyed by id: claude returns the stored token
|
|
@@ -1030,22 +1106,49 @@ declare class JsonSubscriptionCredentialStore implements SubscriptionCredentialS
|
|
|
1030
1106
|
/** Write the merged config to disk as pretty JSON (mkdir parent if needed).
|
|
1031
1107
|
* Encrypt-on-write: the token-material fields are encrypted (legacy plaintext
|
|
1032
1108
|
* `enc:v1:`; already-`enc:`/`$ENV` untouched) before serializing, so any
|
|
1033
|
-
* write incl. child 4's future refresh writes lands encrypted.
|
|
1109
|
+
* write incl. child 4's future refresh writes lands encrypted.
|
|
1110
|
+
* ATOMIC: temp + fsync + rename (`atomicReplaceUtf8`) — a failed or
|
|
1111
|
+
* interrupted write discards only the temp file; the prior `tokens.json`
|
|
1112
|
+
* survives byte-equal (bare `writeFileSync` truncate-writes lost every
|
|
1113
|
+
* account on a mid-write failure, 2026-09-06). */
|
|
1034
1114
|
private persist;
|
|
1035
1115
|
/**
|
|
1036
|
-
* Read + parse `tokens.json`,
|
|
1037
|
-
*
|
|
1038
|
-
*
|
|
1116
|
+
* Read + parse `tokens.json`, then DECRYPT the token-material fields so every
|
|
1117
|
+
* getter returns plaintext (the subscription bearer path is byte-identical).
|
|
1118
|
+
*
|
|
1119
|
+
* A MISSING file is a legitimate first-boot state → minimal `{ updatedAt: '' }`.
|
|
1120
|
+
* A file that EXISTS but cannot be parsed as a JSON object is CORRUPT →
|
|
1121
|
+
* `quarantineCorrupt` moves it aside (once) before the empty config is
|
|
1122
|
+
* returned, so the unreadable accounts survive for manual recovery.
|
|
1039
1123
|
*
|
|
1040
|
-
* The
|
|
1041
|
-
*
|
|
1042
|
-
*
|
|
1043
|
-
*
|
|
1044
|
-
*
|
|
1045
|
-
*
|
|
1046
|
-
* `config.ts loadConfig`, which decrypts outside its parse try.
|
|
1124
|
+
* The DECRYPT runs OUTSIDE any try, so a wrong/missing master key or a
|
|
1125
|
+
* tampered `enc:` envelope FAILS FAST with the box's clear, secret-free
|
|
1126
|
+
* error (secrets spec "/ UX": SHALL fail-fast, SHALL NOT a swallowed
|
|
1127
|
+
* decrypt would report "no tokens" and silently send the WRONG bearer
|
|
1128
|
+
* upstream 401). Mirrors `config.ts loadConfig`, which decrypts outside
|
|
1129
|
+
* its parse try.
|
|
1047
1130
|
*/
|
|
1048
1131
|
private readConfig;
|
|
1132
|
+
/** One-shot latch: a corrupt file is quarantined (or found unmovable) at
|
|
1133
|
+
* most once per process, so the hot read path never re-attempts or re-logs. */
|
|
1134
|
+
private corruptQuarantined;
|
|
1135
|
+
/**
|
|
1136
|
+
* Quarantine a present-but-corrupt `tokens.json`, then treat it as empty.
|
|
1137
|
+
*
|
|
1138
|
+
* Renames the file to a sibling `tokens.json.corrupt-<stamp>` backup and
|
|
1139
|
+
* logs loudly (the daemon's stderr log; secret-free — reason + paths only).
|
|
1140
|
+
* The daemon KEEPS SERVING (API-key routing is unaffected; subscription
|
|
1141
|
+
* routing reports no credential, same as an absent file) while the corrupt
|
|
1142
|
+
* bytes survive for manual recovery — and, critically, the NEXT persist
|
|
1143
|
+
* (e.g. the user re-logging in) can no longer overwrite the only copy of
|
|
1144
|
+
* the old accounts, which is exactly how the 2026-09-06 incident turned a
|
|
1145
|
+
* recoverable truncated file into permanent account loss.
|
|
1146
|
+
*
|
|
1147
|
+
* Best-effort: if the rename fails (file locked, permissions), the corrupt
|
|
1148
|
+
* file is left in place and every later read still tolerates it as empty;
|
|
1149
|
+
* the latch still trips so the attempt + log happen exactly once.
|
|
1150
|
+
*/
|
|
1151
|
+
private quarantineCorrupt;
|
|
1049
1152
|
}
|
|
1050
1153
|
|
|
1051
1154
|
/**
|
|
@@ -1349,9 +1452,239 @@ declare class ClaudeAllowanceCollector {
|
|
|
1349
1452
|
private unsupportedSnapshot;
|
|
1350
1453
|
}
|
|
1351
1454
|
|
|
1455
|
+
/**
|
|
1456
|
+
* Codex (ChatGPT) OAuth usage collector.
|
|
1457
|
+
*
|
|
1458
|
+
* Actively polls `GET https://chatgpt.com/backend-api/wham/usage` per codex
|
|
1459
|
+
* account (Bearer + `ChatGPT-Account-Id` decoded from the OAuth id_token),
|
|
1460
|
+
* mirroring the Claude collector's cache/coalescing contract: 5-minute cache,
|
|
1461
|
+
* per-account in-flight merging, one 401→refresh→retry. The passive
|
|
1462
|
+
* `x-codex-*` response-header tap (`upstreamFetch`) remains the complement —
|
|
1463
|
+
* it keeps windows fresh mid-flight; this collector makes the quota visible
|
|
1464
|
+
* with ZERO traffic (previously codex reported not-observed until a real
|
|
1465
|
+
* model response, and its refresh button had to spend a probe request).
|
|
1466
|
+
*
|
|
1467
|
+
* `reset_at` (absolute epoch seconds) is preferred over `reset_after_seconds`
|
|
1468
|
+
* so deadlines do not accumulate observation-clock skew. Tokens and raw
|
|
1469
|
+
* upstream payloads never leave this module.
|
|
1470
|
+
*/
|
|
1471
|
+
|
|
1472
|
+
interface CodexAllowanceCredentialReader {
|
|
1473
|
+
getAccessTokenForAccount(providerId: 'codex', accountId: string): Promise<string | null>;
|
|
1474
|
+
refreshAccountToken(providerId: 'codex', accountId: string): Promise<boolean>;
|
|
1475
|
+
}
|
|
1476
|
+
type CodexAllowanceFetch = (url: string, init: RequestInit, accountId: string) => Promise<Response>;
|
|
1477
|
+
interface CodexAllowanceCollectOptions {
|
|
1478
|
+
force?: boolean;
|
|
1479
|
+
/** Treat an otherwise valid cache entry as due when it expires within this window. */
|
|
1480
|
+
refreshAheadMs?: number;
|
|
1481
|
+
}
|
|
1482
|
+
declare class CodexAllowanceCollector {
|
|
1483
|
+
private readonly credentials;
|
|
1484
|
+
private readonly store;
|
|
1485
|
+
private readonly fetchImpl;
|
|
1486
|
+
private readonly now;
|
|
1487
|
+
private readonly inFlight;
|
|
1488
|
+
constructor(credentials: CodexAllowanceCredentialReader, store?: AccountAllowanceStore, fetchImpl?: CodexAllowanceFetch, now?: () => number);
|
|
1489
|
+
collectMany(accounts: readonly SubscriptionAccountEntry<CodexTokenConfig>[], options?: CodexAllowanceCollectOptions): Promise<AccountAllowanceSnapshot[]>;
|
|
1490
|
+
collect(account: SubscriptionAccountEntry<CodexTokenConfig>, options?: CodexAllowanceCollectOptions): Promise<AccountAllowanceSnapshot>;
|
|
1491
|
+
/**
|
|
1492
|
+
* A response-header snapshot stays a valid cache hit only while fresh; an
|
|
1493
|
+
* active oauth-usage snapshot is honored on the same 5-minute cadence as
|
|
1494
|
+
* Claude's (the poll is cheap and quota is the scheduling input).
|
|
1495
|
+
*/
|
|
1496
|
+
private isCacheValid;
|
|
1497
|
+
private fetchAccount;
|
|
1498
|
+
private request;
|
|
1499
|
+
private failureSnapshot;
|
|
1500
|
+
private unsupportedSnapshot;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Kimi Code OAuth usage collector.
|
|
1505
|
+
*
|
|
1506
|
+
* Polls `GET https://api.kimi.com/coding/v1/usages` per kimi account (Bearer +
|
|
1507
|
+
* the CLI fingerprint headers), mirroring the Claude/Codex collectors' cache
|
|
1508
|
+
* contract: 5-minute cache, per-account in-flight merging, one 401→refresh→
|
|
1509
|
+
* retry. The payload's `usage` aggregate is a weekly row; `limits[]` carries
|
|
1510
|
+
* the per-window rows — the 300-minute burst window normalizes to `five-hour`
|
|
1511
|
+
* and whole-day spans to `seven-day` (the same canonical ids the Claude view
|
|
1512
|
+
* and the UI's window labels use).
|
|
1513
|
+
*/
|
|
1514
|
+
|
|
1515
|
+
interface KimiAllowanceCredentialReader {
|
|
1516
|
+
getAccessTokenForAccount(providerId: 'kimi', accountId: string): Promise<string | null>;
|
|
1517
|
+
refreshAccountToken(providerId: 'kimi', accountId: string): Promise<boolean>;
|
|
1518
|
+
}
|
|
1519
|
+
type KimiAllowanceFetch = (url: string, init: RequestInit, accountId: string) => Promise<Response>;
|
|
1520
|
+
interface KimiAllowanceCollectOptions {
|
|
1521
|
+
force?: boolean;
|
|
1522
|
+
refreshAheadMs?: number;
|
|
1523
|
+
}
|
|
1524
|
+
declare class KimiAllowanceCollector {
|
|
1525
|
+
private readonly credentials;
|
|
1526
|
+
private readonly store;
|
|
1527
|
+
private readonly fetchImpl;
|
|
1528
|
+
private readonly now;
|
|
1529
|
+
private readonly inFlight;
|
|
1530
|
+
constructor(credentials: KimiAllowanceCredentialReader, store?: AccountAllowanceStore, fetchImpl?: KimiAllowanceFetch, now?: () => number);
|
|
1531
|
+
collectMany(accounts: readonly SubscriptionAccountEntry<KimiTokenConfig>[], options?: KimiAllowanceCollectOptions): Promise<AccountAllowanceSnapshot[]>;
|
|
1532
|
+
collect(account: SubscriptionAccountEntry<KimiTokenConfig>, options?: KimiAllowanceCollectOptions): Promise<AccountAllowanceSnapshot>;
|
|
1533
|
+
private isCacheValid;
|
|
1534
|
+
private fetchAccount;
|
|
1535
|
+
private request;
|
|
1536
|
+
private failureSnapshot;
|
|
1537
|
+
private unsupportedSnapshot;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
/**
|
|
1541
|
+
* Grok (xAI SuperGrok) OAuth usage collector.
|
|
1542
|
+
*
|
|
1543
|
+
* Polls the Grok CLI billing proxy (`cli-chat-proxy.grok.com/v1/billing` —
|
|
1544
|
+
* NOT `*.x.ai`, and it REJECTS paid API keys: OAuth bearer only) per grok
|
|
1545
|
+
* account, mirroring the Claude/Codex/Kimi collectors' cache contract:
|
|
1546
|
+
* 5-minute cache, per-account in-flight merging, one 401→refresh→retry.
|
|
1547
|
+
*
|
|
1548
|
+
* Dual shape (mirrors the audit source's semantics):
|
|
1549
|
+
* - WEEKLY credits (`?format=credits`): `config.creditUsagePercent` +
|
|
1550
|
+
* `config.currentPeriod{start,end,type}` (+ per-product rows and an
|
|
1551
|
+
* on-demand cap, both optional). A missing `creditUsagePercent` on an
|
|
1552
|
+
* ACTIVE weekly period reads as 0 (a fresh period has no usage row).
|
|
1553
|
+
* - UNIFIED monthly (default URL): `config.{billingPeriodStart,
|
|
1554
|
+
* billingPeriodEnd, monthlyLimit{val}, used{val}}` — accounts flagged
|
|
1555
|
+
* `isUnifiedBillingUser` omit the weekly percentage and meter a monthly
|
|
1556
|
+
* included quota instead.
|
|
1557
|
+
*
|
|
1558
|
+
* Probe policy: always probe weekly first; probe monthly when weekly is
|
|
1559
|
+
* missing OR the account is flagged unified. An INFERRED weekly percentage
|
|
1560
|
+
* (field absent) on a unified account is only kept when the monthly probe
|
|
1561
|
+
* positively confirms there is no monthly quota — otherwise the monthly
|
|
1562
|
+
* window wins (an inferred 0% weekly on a unified account is a lie), and a
|
|
1563
|
+
* failed monthly probe falls through to a failure snapshot so the store
|
|
1564
|
+
* retains the last good one.
|
|
1565
|
+
*
|
|
1566
|
+
* Every request carries `X-XAI-Token-Auth: xai-grok-cli` — the same product
|
|
1567
|
+
* gate the official CLI uses on this host.
|
|
1568
|
+
*/
|
|
1569
|
+
|
|
1570
|
+
interface GrokAllowanceCredentialReader {
|
|
1571
|
+
getAccessTokenForAccount(providerId: 'grok', accountId: string): Promise<string | null>;
|
|
1572
|
+
refreshAccountToken(providerId: 'grok', accountId: string): Promise<boolean>;
|
|
1573
|
+
}
|
|
1574
|
+
type GrokAllowanceFetch = (url: string, init: RequestInit, accountId: string) => Promise<Response>;
|
|
1575
|
+
interface GrokAllowanceCollectOptions {
|
|
1576
|
+
force?: boolean;
|
|
1577
|
+
refreshAheadMs?: number;
|
|
1578
|
+
}
|
|
1579
|
+
declare class GrokAllowanceCollector {
|
|
1580
|
+
private readonly credentials;
|
|
1581
|
+
private readonly store;
|
|
1582
|
+
private readonly fetchImpl;
|
|
1583
|
+
private readonly now;
|
|
1584
|
+
private readonly inFlight;
|
|
1585
|
+
constructor(credentials: GrokAllowanceCredentialReader, store?: AccountAllowanceStore, fetchImpl?: GrokAllowanceFetch, now?: () => number);
|
|
1586
|
+
collectMany(accounts: readonly SubscriptionAccountEntry<GrokTokenConfig>[], options?: GrokAllowanceCollectOptions): Promise<AccountAllowanceSnapshot[]>;
|
|
1587
|
+
collect(account: SubscriptionAccountEntry<GrokTokenConfig>, options?: GrokAllowanceCollectOptions): Promise<AccountAllowanceSnapshot>;
|
|
1588
|
+
private isCacheValid;
|
|
1589
|
+
private fetchAccount;
|
|
1590
|
+
private failureSnapshot;
|
|
1591
|
+
private unsupportedSnapshot;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* CopilotAllowanceCollector — GitHub Copilot quota via the internal user API.
|
|
1596
|
+
*
|
|
1597
|
+
* Polls `GET api.github.com/copilot_internal/user` per copilot account
|
|
1598
|
+
* (Bearer ghu_ + the mirrored Copilot CLI user-agent), reading
|
|
1599
|
+
* `quota_snapshots`:
|
|
1600
|
+
* - `premium_interactions` — the plan's premium-request monthly window
|
|
1601
|
+
* (`{entitlement, remaining, percent_remaining, unlimited}` + the
|
|
1602
|
+
* account-level `quota_reset_date`). Reported as a single `thirty-day`
|
|
1603
|
+
* window; an `unlimited` entitlement reports 0% (never blocks).
|
|
1604
|
+
* - `chat` — the legacy chat-completions quota, reported only when it is
|
|
1605
|
+
* NOT unlimited (newer plans fold it into premium).
|
|
1606
|
+
*
|
|
1607
|
+
* Mirrors the Claude/Codex/Kimi/Grok collectors' cache contract: 5-minute
|
|
1608
|
+
* cache, per-account in-flight merging, one 401→refresh→retry (for copilot a
|
|
1609
|
+
* "refresh" marks the account expired — ghu_ tokens cannot be refreshed — so
|
|
1610
|
+
* the retry path is exercised only by transient upstream 401 flaps and never
|
|
1611
|
+
* loops). The GitHub API base honors the account's `enterpriseUrl` (GHE).
|
|
1612
|
+
*/
|
|
1613
|
+
|
|
1614
|
+
interface CopilotAllowanceCredentialReader {
|
|
1615
|
+
getAccessTokenForAccount(providerId: 'copilot', accountId: string): Promise<string | null>;
|
|
1616
|
+
refreshAccountToken(providerId: 'copilot', accountId: string): Promise<boolean>;
|
|
1617
|
+
}
|
|
1618
|
+
type CopilotAllowanceFetch = (url: string, init: RequestInit, accountId: string) => Promise<Response>;
|
|
1619
|
+
interface CopilotAllowanceCollectOptions {
|
|
1620
|
+
force?: boolean;
|
|
1621
|
+
refreshAheadMs?: number;
|
|
1622
|
+
}
|
|
1623
|
+
declare class CopilotAllowanceCollector {
|
|
1624
|
+
private readonly credentials;
|
|
1625
|
+
private readonly store;
|
|
1626
|
+
private readonly fetchImpl;
|
|
1627
|
+
private readonly now;
|
|
1628
|
+
private readonly inFlight;
|
|
1629
|
+
constructor(credentials: CopilotAllowanceCredentialReader, store?: AccountAllowanceStore, fetchImpl?: CopilotAllowanceFetch, now?: () => number);
|
|
1630
|
+
collectMany(accounts: readonly SubscriptionAccountEntry<CopilotTokenConfig>[], options?: CopilotAllowanceCollectOptions): Promise<AccountAllowanceSnapshot[]>;
|
|
1631
|
+
collect(account: SubscriptionAccountEntry<CopilotTokenConfig>, options?: CopilotAllowanceCollectOptions): Promise<AccountAllowanceSnapshot>;
|
|
1632
|
+
private isCacheValid;
|
|
1633
|
+
private fetchAccount;
|
|
1634
|
+
private request;
|
|
1635
|
+
private failureSnapshot;
|
|
1636
|
+
private unsupportedSnapshot;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
/**
|
|
1640
|
+
* OpenCodeGo usage collector.
|
|
1641
|
+
*
|
|
1642
|
+
* Polls `GET {go-base}/v1/usage` per OpenCodeGo account with the account's
|
|
1643
|
+
* static bearer key, mirroring the other collectors' cache contract (5-minute
|
|
1644
|
+
* cache, per-account in-flight merging). The payload reports three percent
|
|
1645
|
+
* windows; only rolling(≈5h) + weekly are surfaced — the MONTHLY window is
|
|
1646
|
+
* deliberately dropped: the console's "Use balance" fallback keeps a
|
|
1647
|
+
* monthly-exhausted key SERVING, and the scheduling policy pauses on the worst
|
|
1648
|
+
* reported window, so reporting monthly would strand usable keys (oh-my-pi
|
|
1649
|
+
* reached the same conclusion for its ranking scopes).
|
|
1650
|
+
*
|
|
1651
|
+
* `status: "rate-limited"` is authoritative over the percent (→ 100%).
|
|
1652
|
+
*/
|
|
1653
|
+
|
|
1654
|
+
interface OpenCodeGoAllowanceCredentialReader {
|
|
1655
|
+
getAccessTokenForAccount(providerId: 'opencodego', accountId: string): Promise<string | null>;
|
|
1656
|
+
}
|
|
1657
|
+
type OpenCodeGoAllowanceFetch = (url: string, init: RequestInit, accountId: string) => Promise<Response>;
|
|
1658
|
+
declare class OpenCodeGoAllowanceCollector {
|
|
1659
|
+
private readonly credentials;
|
|
1660
|
+
private readonly store;
|
|
1661
|
+
private readonly fetchImpl;
|
|
1662
|
+
private readonly now;
|
|
1663
|
+
private readonly inFlight;
|
|
1664
|
+
constructor(credentials: OpenCodeGoAllowanceCredentialReader, store?: AccountAllowanceStore, fetchImpl?: OpenCodeGoAllowanceFetch, now?: () => number);
|
|
1665
|
+
collectMany(accounts: readonly SubscriptionAccountEntry<OpenCodeGoTokenConfig>[], options?: {
|
|
1666
|
+
force?: boolean;
|
|
1667
|
+
refreshAheadMs?: number;
|
|
1668
|
+
}): Promise<AccountAllowanceSnapshot[]>;
|
|
1669
|
+
collect(account: SubscriptionAccountEntry<OpenCodeGoTokenConfig>, options?: {
|
|
1670
|
+
force?: boolean;
|
|
1671
|
+
refreshAheadMs?: number;
|
|
1672
|
+
}): Promise<AccountAllowanceSnapshot>;
|
|
1673
|
+
private fetchAccount;
|
|
1674
|
+
private failureSnapshot;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1352
1677
|
/** Secret-free account allowance query/refresh facade used by the admin API. */
|
|
1353
1678
|
|
|
1354
|
-
|
|
1679
|
+
/**
|
|
1680
|
+
* The credential surface the collectors share. Declared explicitly (not via
|
|
1681
|
+
* interface extension) because the collectors narrow
|
|
1682
|
+
* `getAccessTokenForAccount`/`refreshAccountToken` to different provider
|
|
1683
|
+
* literals — extending them all would make the overloads conflict.
|
|
1684
|
+
*/
|
|
1685
|
+
interface AccountAllowanceCredentialReader {
|
|
1686
|
+
getAccessTokenForAccount(providerId: 'claude' | 'codex' | 'kimi' | 'opencodego' | 'grok' | 'copilot', accountId: string): Promise<string | null>;
|
|
1687
|
+
refreshAccountToken(providerId: 'claude' | 'codex' | 'kimi' | 'grok' | 'copilot', accountId: string): Promise<boolean>;
|
|
1355
1688
|
getFullConfig(): Promise<AccountTokensConfig>;
|
|
1356
1689
|
}
|
|
1357
1690
|
interface AccountAllowanceFilter {
|
|
@@ -1367,18 +1700,41 @@ declare class AccountAllowanceService {
|
|
|
1367
1700
|
private readonly store;
|
|
1368
1701
|
private readonly now;
|
|
1369
1702
|
readonly claudeCollector: ClaudeAllowanceCollector;
|
|
1370
|
-
|
|
1703
|
+
readonly codexCollector: CodexAllowanceCollector;
|
|
1704
|
+
readonly kimiCollector: KimiAllowanceCollector;
|
|
1705
|
+
readonly grokCollector: GrokAllowanceCollector;
|
|
1706
|
+
readonly copilotCollector: CopilotAllowanceCollector;
|
|
1707
|
+
readonly opencodegoCollector: OpenCodeGoAllowanceCollector;
|
|
1708
|
+
constructor(credentials: AccountAllowanceCredentialReader, store?: AccountAllowanceStore, collector?: ClaudeAllowanceCollector, codexCollector?: CodexAllowanceCollector, kimiCollector?: KimiAllowanceCollector, opencodegoCollector?: OpenCodeGoAllowanceCollector, grokCollector?: GrokAllowanceCollector, copilotCollector?: CopilotAllowanceCollector, now?: () => number);
|
|
1371
1709
|
/**
|
|
1372
|
-
* Read all/filtered snapshots. Claude's five-minute
|
|
1373
|
-
*
|
|
1710
|
+
* Read all/filtered snapshots. Claude's and Codex's five-minute caches are
|
|
1711
|
+
* refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
|
|
1712
|
+
* passive `x-codex-*` header tap still feeds mid-flight updates).
|
|
1374
1713
|
*/
|
|
1375
1714
|
list(filter?: AccountAllowanceFilter): Promise<AccountAllowanceSnapshot[]>;
|
|
1715
|
+
private knownAccounts;
|
|
1376
1716
|
/** Force-refresh Claude usage for one account or every stored Claude account. */
|
|
1377
1717
|
refreshClaude(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
1378
1718
|
/**
|
|
1379
|
-
*
|
|
1380
|
-
*
|
|
1381
|
-
*
|
|
1719
|
+
* Force-refresh Codex usage (`/backend-api/wham/usage`) for one account or
|
|
1720
|
+
* every stored Codex account. Replaces the old probe-request workaround —
|
|
1721
|
+
* no quota is spent reading the usage endpoint.
|
|
1722
|
+
*/
|
|
1723
|
+
refreshCodex(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
1724
|
+
/** Force-refresh OpenCodeGo usage (`{go}/v1/usage`) for one/all accounts. */
|
|
1725
|
+
refreshOpenCodeGo(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
1726
|
+
/** Force-refresh Kimi usage (`/coding/v1/usages`) for one/all accounts. */
|
|
1727
|
+
refreshKimi(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
1728
|
+
/** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
|
|
1729
|
+
refreshCopilot(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
1730
|
+
/** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
|
|
1731
|
+
refreshGrok(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
1732
|
+
/**
|
|
1733
|
+
* Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
|
|
1734
|
+
* collectors preserve their cache + per-account in-flight coalescing; a tick
|
|
1735
|
+
* normally performs no network I/O. (Codex joined the warm path when it
|
|
1736
|
+
* gained an active `/wham/usage` collector — the passive `x-codex-*` header
|
|
1737
|
+
* tap alone could not keep the policy fed while idle.)
|
|
1382
1738
|
*/
|
|
1383
1739
|
maintainClaudeCache(refreshAheadMs: number): Promise<void>;
|
|
1384
1740
|
/** Remove a cache row as soon as an account is deleted by the admin path. */
|
|
@@ -1743,6 +2099,68 @@ type AuditCompactor = () => {
|
|
|
1743
2099
|
/** The read surface the AdminServer consumes (bootstrap binds it to the ledger dir). */
|
|
1744
2100
|
type BillingStatusReader = () => BillingDeliveryStatus;
|
|
1745
2101
|
|
|
2102
|
+
/**
|
|
2103
|
+
* ProviderKeyQuota — BYO provider-row key quota parsing (pure functions).
|
|
2104
|
+
*
|
|
2105
|
+
* Subscription accounts have the daemon's allowance collectors; BYO rows (API
|
|
2106
|
+
* keys pasted from a provider console) had NO quota surface at all. Several CN
|
|
2107
|
+
* coding-plan providers expose a same-key usage endpoint:
|
|
2108
|
+
*
|
|
2109
|
+
* - Z.AI / Zhipu bigmodel (GLM Coding Plan):
|
|
2110
|
+
* GET {origin}/api/monitor/usage/quota/limit
|
|
2111
|
+
* Raw `Authorization: <key>` (NO Bearer prefix). Envelope
|
|
2112
|
+
* `{success, data: {limits[], level}}`; each limit carries
|
|
2113
|
+
* `{type, usage(limit), currentValue(used), percentage, remaining,
|
|
2114
|
+
* nextResetTime, unit(3=h/4=d/5=mo/6=w), number, usageDetails[]}`.
|
|
2115
|
+
* A coding-plan key reports 5h + weekly credit windows; a PAYG key's shape
|
|
2116
|
+
* is unknown → defensive parse, unavailable on surprise.
|
|
2117
|
+
* - MiniMax Token Plan:
|
|
2118
|
+
* GET {origin}/v1/token_plan/remains
|
|
2119
|
+
* `Authorization: Bearer <key>`. HTTP is ALWAYS 200 — `base_resp
|
|
2120
|
+
* .status_code === 0` is the real success gate. `model_remains[]` buckets
|
|
2121
|
+
* each carry a rolling interval + weekly window as REMAINING percent
|
|
2122
|
+
* (0-100); the `"general"` bucket is the plan-wide shared quota.
|
|
2123
|
+
* - Cline Pass:
|
|
2124
|
+
* GET {origin}/api/v1/users/me/plan/usage-limits
|
|
2125
|
+
* `Authorization: Bearer <key>` PLUS the Cline client-identity header set
|
|
2126
|
+
* (the row's `extraHeaders` — the gateway 403s without the full mirror).
|
|
2127
|
+
* `limits[]` rows carry `{type: five_hour|weekly|monthly, percentUsed,
|
|
2128
|
+
* resetsAt}` — pure percentage windows, no absolute meters.
|
|
2129
|
+
*
|
|
2130
|
+
* Everything here is pure; the fetch/cache lifecycle lives in
|
|
2131
|
+
* `ProviderKeyQuotaService`. Windows reuse the subscription `AllowanceWindow`
|
|
2132
|
+
* DTO so the UI renders one shape.
|
|
2133
|
+
*/
|
|
2134
|
+
|
|
2135
|
+
/** Which quota adapter applies to a provider row (by resolved endpoint). */
|
|
2136
|
+
type ProviderKeyQuotaAdapter = 'zai' | 'minimax-token-plan' | 'umans' | 'synthetic' | 'cline-pass';
|
|
2137
|
+
|
|
2138
|
+
/**
|
|
2139
|
+
* ProviderKeyQuotaService — read-through quota cache for BYO provider-row keys.
|
|
2140
|
+
*
|
|
2141
|
+
* The UI polls `GET /admin/api/providers/:id/keys` every few seconds for pool
|
|
2142
|
+
* health; this service backs the optional `quota` field on that DTO. A read is
|
|
2143
|
+
* cache-first (5-minute TTL) with per-key in-flight coalescing, so the UI's poll
|
|
2144
|
+
* cadence never translates into upstream request cadence. `force` (the refresh
|
|
2145
|
+
* button) bypasses the cache. Failures degrade to a stale marker — quota
|
|
2146
|
+
* telemetry must never break the keys view.
|
|
2147
|
+
*
|
|
2148
|
+
* The key plaintext is resolved from the live row (same synthesis as the pool
|
|
2149
|
+
* loader: explicit `apiKeys[]` else the single-key fallback) and decrypted via
|
|
2150
|
+
* the injected box; it is used ONLY for the upstream Authorization header and
|
|
2151
|
+
* never appears in any returned DTO.
|
|
2152
|
+
*/
|
|
2153
|
+
|
|
2154
|
+
/** Secret-free quota view for one pool key. */
|
|
2155
|
+
interface ProviderKeyQuota {
|
|
2156
|
+
adapter: ProviderKeyQuotaAdapter;
|
|
2157
|
+
observedAt: string;
|
|
2158
|
+
expiresAt: string;
|
|
2159
|
+
windows: AllowanceWindow[];
|
|
2160
|
+
/** Stable display-safe diagnostic code on a failed probe. */
|
|
2161
|
+
errorCode?: string;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
1746
2164
|
type PreparedImageRuntimeGeneration = {
|
|
1747
2165
|
readonly id: string;
|
|
1748
2166
|
readonly enabled: true;
|
|
@@ -2511,6 +2929,16 @@ interface AccountAllowanceAdminReader {
|
|
|
2511
2929
|
accountId?: string;
|
|
2512
2930
|
}): Promise<AccountAllowanceSnapshot[]>;
|
|
2513
2931
|
refreshClaude(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
2932
|
+
/** Optional: Codex active `/wham/usage` refresh (absent on older daemons). */
|
|
2933
|
+
refreshCodex?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
2934
|
+
/** Optional: Kimi `/coding/v1/usages` refresh (absent on older daemons). */
|
|
2935
|
+
refreshKimi?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
2936
|
+
/** Optional: OpenCodeGo `/v1/usage` refresh (absent on older daemons). */
|
|
2937
|
+
refreshOpenCodeGo?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
2938
|
+
/** Optional: Grok CLI-billing refresh (absent on older daemons). */
|
|
2939
|
+
refreshGrok?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
2940
|
+
/** Optional: Copilot user-quota refresh (absent on older daemons). */
|
|
2941
|
+
refreshCopilot?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
2514
2942
|
removeAccountSnapshot?(providerId: SubscriptionProviderId, accountId: string): void;
|
|
2515
2943
|
removeProviderSnapshots?(providerId: SubscriptionProviderId): void;
|
|
2516
2944
|
getSchedulingStatus?(): AccountAllowanceSchedulingStatus;
|
|
@@ -2535,6 +2963,16 @@ interface PoolKeyHealth {
|
|
|
2535
2963
|
interface PoolHealthReader {
|
|
2536
2964
|
getKeyHealth(providerId: string): Promise<Record<string, PoolKeyHealth>>;
|
|
2537
2965
|
}
|
|
2966
|
+
/**
|
|
2967
|
+
* The BYO provider-key quota surface the keys view needs — structurally
|
|
2968
|
+
* satisfied by `ProviderKeyQuotaService`. Read-only; the DTO is secret-free by
|
|
2969
|
+
* construction (normalized windows + diagnostic codes only).
|
|
2970
|
+
*/
|
|
2971
|
+
interface ProviderKeyQuotaReader {
|
|
2972
|
+
quotaFor(row: DaemonProviderConfig, keyId: string, options?: {
|
|
2973
|
+
force?: boolean;
|
|
2974
|
+
}): Promise<ProviderKeyQuota | null>;
|
|
2975
|
+
}
|
|
2538
2976
|
interface AdminImagesStatusReader {
|
|
2539
2977
|
inspectCapability(apiKeyId: string): Promise<ImageRuntimeCapabilityInspection>;
|
|
2540
2978
|
status(): ImageRuntimeManagerStatus;
|
|
@@ -2612,6 +3050,12 @@ interface AdminApiDeps {
|
|
|
2612
3050
|
readonly apiKeyPool: PoolHealthReader;
|
|
2613
3051
|
/** In-memory auto-disable store (design D5) — read-only for the health view. */
|
|
2614
3052
|
readonly autoDisableStore: AutoDisableStore;
|
|
3053
|
+
/**
|
|
3054
|
+
* OPTIONAL BYO provider-key quota service — same-key usage/quota probes for
|
|
3055
|
+
* provider rows with a known adapter (Z.AI coding plan, MiniMax Token Plan).
|
|
3056
|
+
* Absent ⇒ the keys view carries no `quota` field (light embedders).
|
|
3057
|
+
*/
|
|
3058
|
+
readonly providerKeyQuota?: ProviderKeyQuotaReader;
|
|
2615
3059
|
/**
|
|
2616
3060
|
* Pending interactive-OAuth sessions (app-parity child 4, design D1) — the
|
|
2617
3061
|
* in-memory `{ codeVerifier, state }` map keyed by a minted `sessionId`,
|
|
@@ -2639,6 +3083,21 @@ interface AdminApiDeps {
|
|
|
2639
3083
|
* flight (port 1455 is one resource). Wired in `bootstrap.ts`.
|
|
2640
3084
|
*/
|
|
2641
3085
|
readonly codexSessions: CodexOAuthSessionStore;
|
|
3086
|
+
/**
|
|
3087
|
+
* Kimi interactive-OAuth flow store (device code). Same token-free polled
|
|
3088
|
+
* shape as codex; one sign-in at a time. Wired in `bootstrap.ts`.
|
|
3089
|
+
*/
|
|
3090
|
+
readonly kimiSessions: CodexOAuthSessionStore;
|
|
3091
|
+
/**
|
|
3092
|
+
* Grok interactive-OAuth flow store (device code). Same token-free polled
|
|
3093
|
+
* shape as codex; one sign-in at a time. Wired in `bootstrap.ts`.
|
|
3094
|
+
*/
|
|
3095
|
+
readonly grokSessions: CodexOAuthSessionStore;
|
|
3096
|
+
/**
|
|
3097
|
+
* Copilot interactive-OAuth flow store (device code). Same token-free
|
|
3098
|
+
* polled shape; one sign-in at a time. Wired in `bootstrap.ts`.
|
|
3099
|
+
*/
|
|
3100
|
+
readonly copilotSessions: CodexOAuthSessionStore;
|
|
2642
3101
|
/**
|
|
2643
3102
|
* Codex loopback listener (app-parity-2 child 5) — defaults to `awaitLoopbackCode`
|
|
2644
3103
|
* (binds 127.0.0.1:1455) in `bootstrap.ts`; tests inject a mock so no real port
|
|
@@ -3467,7 +3926,6 @@ declare function createImageRuntimeGeneration(options: ImageRuntimeGenerationFac
|
|
|
3467
3926
|
* @module @omnicross/daemon/ports/JsonOutboundKeyDb
|
|
3468
3927
|
*/
|
|
3469
3928
|
|
|
3470
|
-
type AtomicFileReplace = (targetPath: string, contents: string) => void;
|
|
3471
3929
|
declare class JsonOutboundKeyDb implements OutboundKeyDb {
|
|
3472
3930
|
private readonly keysPath;
|
|
3473
3931
|
private readonly secretBox?;
|