@omnicross/daemon 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -6,7 +6,7 @@ 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
8
  import { AccountAllowanceSnapshot, AllowanceWindow } from '@omnicross/contracts/account-allowance-types';
9
- import { ClaudeTokenConfig, CodexTokenConfig, GeminiTokenConfig, KimiTokenConfig, AccountTokensConfig, ProxyConfig, SubscriptionAccountSanitized, AccountClientIdentity, SubscriptionAccountEntry } from '@omnicross/contracts/account-tokens-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 —
@@ -666,7 +678,7 @@ type AtomicFileReplace = (targetPath: string, contents: string) => void;
666
678
  */
667
679
 
668
680
  /** Provider id → owned contract field names. */
669
- type DaemonProvider = 'claude' | 'codex' | 'gemini' | 'opencodego' | 'kimi';
681
+ type DaemonProvider = 'claude' | 'codex' | 'gemini' | 'opencodego' | 'kimi' | 'grok' | 'copilot';
670
682
  interface AccountMetadataPatch {
671
683
  label?: string;
672
684
  enabled?: boolean;
@@ -795,7 +807,7 @@ type ExternalCliReader = (provider: ExternalCliProvider) => ExternalCliCredentia
795
807
  * shapes), keyed by `SubscriptionProviderId` the daemon admin layer validates
796
808
  * the wire body to one of these before calling the writer.
797
809
  */
798
- type SubscriptionTokenBlock = ClaudeTokenConfig | CodexTokenConfig | GeminiTokenConfig | OpenCodeGoTokenConfig | KimiTokenConfig;
810
+ type SubscriptionTokenBlock = ClaudeTokenConfig | CodexTokenConfig | GeminiTokenConfig | OpenCodeGoTokenConfig | KimiTokenConfig | GrokTokenConfig | CopilotTokenConfig;
799
811
  declare class JsonSubscriptionCredentialStore implements SubscriptionCredentialStore {
800
812
  private readonly tokensPath;
801
813
  private readonly box;
@@ -907,13 +919,28 @@ declare class JsonSubscriptionCredentialStore implements SubscriptionCredentialS
907
919
  * `false` when no refresh_token.
908
920
  */
909
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>;
910
937
  /**
911
938
  * Refresh a SPECIFIC managed account by id (background scheduler sweep and
912
939
  * account-pool resolution). It uses only that account's stored refresh
913
940
  * token. Coalesced per `provider:id`; on failure flags ONLY that account
914
941
  * `expired`.
915
942
  */
916
- refreshAccountById(provider: 'claude' | 'codex' | 'gemini' | 'kimi', id: string): Promise<boolean>;
943
+ refreshAccountById(provider: 'claude' | 'codex' | 'gemini' | 'kimi' | 'grok' | 'copilot', id: string): Promise<boolean>;
917
944
  /**
918
945
  * Resolve a SPECIFIC account's access token by id (design D6). Mirrors each
919
946
  * provider's ACTIVE-getter policy, keyed by id: claude returns the stored token
@@ -1510,6 +1537,155 @@ declare class KimiAllowanceCollector {
1510
1537
  private unsupportedSnapshot;
1511
1538
  }
1512
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
+ * GeminiAllowanceCollector — gemini subscription quota via the Cloud Code
1641
+ * Assist internal user-quota RPC.
1642
+ *
1643
+ * Probes `POST ${cloudcode-pa}/v1internal:retrieveUserQuota` per gemini
1644
+ * account (Bearer access token + the GeminiCLI masquerade pair the inference
1645
+ * path also carries), parsing `buckets[]` into one per-model window:
1646
+ * `remainingFraction` (0–1) → used percent, `resetTime` → resetsAt. The
1647
+ * Code Assist project id comes from the SHARED `GeminiCodeAssistProjectResolver`
1648
+ * (the same instance the dispatch seam uses, so the handshake runs at most
1649
+ * once per token across inference + quota); a handshake hard failure degrades
1650
+ * to a project-less probe (the valid free-tier envelope), mirroring the
1651
+ * reference implementation's tolerant behavior.
1652
+ *
1653
+ * Mirrors the sibling collectors' cache contract: 5-minute cache, per-account
1654
+ * in-flight merging, one 401/403→refresh→retry. Display-only — the scheduling
1655
+ * whitelist deliberately excludes gemini (per-model fraction buckets do not
1656
+ * fit the worst-window pause/demote rule).
1657
+ *
1658
+ * @module @omnicross/daemon/allowance/GeminiAllowanceCollector
1659
+ */
1660
+
1661
+ interface GeminiAllowanceCredentialReader {
1662
+ getAccessTokenForAccount(providerId: 'gemini', accountId: string): Promise<string | null>;
1663
+ refreshAccountToken(providerId: 'gemini', accountId: string): Promise<boolean>;
1664
+ }
1665
+ type GeminiAllowanceFetch = (url: string, init: RequestInit, accountId: string) => Promise<Response>;
1666
+ interface GeminiAllowanceCollectOptions {
1667
+ force?: boolean;
1668
+ refreshAheadMs?: number;
1669
+ }
1670
+ declare class GeminiAllowanceCollector {
1671
+ private readonly credentials;
1672
+ private readonly store;
1673
+ private readonly fetchImpl;
1674
+ private readonly now;
1675
+ private readonly projectResolver;
1676
+ private readonly inFlight;
1677
+ constructor(credentials: GeminiAllowanceCredentialReader, store?: AccountAllowanceStore, fetchImpl?: GeminiAllowanceFetch, now?: () => number, projectResolver?: {
1678
+ resolveProject(accessToken: string): Promise<string | undefined>;
1679
+ });
1680
+ collectMany(accounts: readonly SubscriptionAccountEntry<GeminiTokenConfig>[], options?: GeminiAllowanceCollectOptions): Promise<AccountAllowanceSnapshot[]>;
1681
+ collect(account: SubscriptionAccountEntry<GeminiTokenConfig>, options?: GeminiAllowanceCollectOptions): Promise<AccountAllowanceSnapshot>;
1682
+ private isCacheValid;
1683
+ private fetchAccount;
1684
+ private request;
1685
+ private failureSnapshot;
1686
+ private unsupportedSnapshot;
1687
+ }
1688
+
1513
1689
  /**
1514
1690
  * OpenCodeGo usage collector.
1515
1691
  *
@@ -1557,8 +1733,8 @@ declare class OpenCodeGoAllowanceCollector {
1557
1733
  * literals — extending them all would make the overloads conflict.
1558
1734
  */
1559
1735
  interface AccountAllowanceCredentialReader {
1560
- getAccessTokenForAccount(providerId: 'claude' | 'codex' | 'kimi' | 'opencodego', accountId: string): Promise<string | null>;
1561
- refreshAccountToken(providerId: 'claude' | 'codex' | 'kimi', accountId: string): Promise<boolean>;
1736
+ getAccessTokenForAccount(providerId: 'claude' | 'codex' | 'kimi' | 'opencodego' | 'grok' | 'copilot' | 'gemini', accountId: string): Promise<string | null>;
1737
+ refreshAccountToken(providerId: 'claude' | 'codex' | 'kimi' | 'grok' | 'copilot' | 'gemini', accountId: string): Promise<boolean>;
1562
1738
  getFullConfig(): Promise<AccountTokensConfig>;
1563
1739
  }
1564
1740
  interface AccountAllowanceFilter {
@@ -1576,8 +1752,11 @@ declare class AccountAllowanceService {
1576
1752
  readonly claudeCollector: ClaudeAllowanceCollector;
1577
1753
  readonly codexCollector: CodexAllowanceCollector;
1578
1754
  readonly kimiCollector: KimiAllowanceCollector;
1755
+ readonly grokCollector: GrokAllowanceCollector;
1756
+ readonly copilotCollector: CopilotAllowanceCollector;
1579
1757
  readonly opencodegoCollector: OpenCodeGoAllowanceCollector;
1580
- constructor(credentials: AccountAllowanceCredentialReader, store?: AccountAllowanceStore, collector?: ClaudeAllowanceCollector, codexCollector?: CodexAllowanceCollector, kimiCollector?: KimiAllowanceCollector, opencodegoCollector?: OpenCodeGoAllowanceCollector, now?: () => number);
1758
+ readonly geminiCollector: GeminiAllowanceCollector;
1759
+ constructor(credentials: AccountAllowanceCredentialReader, store?: AccountAllowanceStore, collector?: ClaudeAllowanceCollector, codexCollector?: CodexAllowanceCollector, kimiCollector?: KimiAllowanceCollector, opencodegoCollector?: OpenCodeGoAllowanceCollector, grokCollector?: GrokAllowanceCollector, copilotCollector?: CopilotAllowanceCollector, geminiCollector?: GeminiAllowanceCollector, now?: () => number);
1581
1760
  /**
1582
1761
  * Read all/filtered snapshots. Claude's and Codex's five-minute caches are
1583
1762
  * refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
@@ -1597,6 +1776,12 @@ declare class AccountAllowanceService {
1597
1776
  refreshOpenCodeGo(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
1598
1777
  /** Force-refresh Kimi usage (`/coding/v1/usages`) for one/all accounts. */
1599
1778
  refreshKimi(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
1779
+ /** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
1780
+ refreshCopilot(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
1781
+ /** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
1782
+ refreshGrok(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
1783
+ /** Force-refresh Gemini usage (Code Assist retrieveUserQuota) for one/all accounts. */
1784
+ refreshGemini(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
1600
1785
  /**
1601
1786
  * Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
1602
1787
  * collectors preserve their cache + per-account in-flight coalescing; a tick
@@ -1988,6 +2173,12 @@ type BillingStatusReader = () => BillingDeliveryStatus;
1988
2173
  * .status_code === 0` is the real success gate. `model_remains[]` buckets
1989
2174
  * each carry a rolling interval + weekly window as REMAINING percent
1990
2175
  * (0-100); the `"general"` bucket is the plan-wide shared quota.
2176
+ * - Cline Pass:
2177
+ * GET {origin}/api/v1/users/me/plan/usage-limits
2178
+ * `Authorization: Bearer <key>` PLUS the Cline client-identity header set
2179
+ * (the row's `extraHeaders` — the gateway 403s without the full mirror).
2180
+ * `limits[]` rows carry `{type: five_hour|weekly|monthly, percentUsed,
2181
+ * resetsAt}` — pure percentage windows, no absolute meters.
1991
2182
  *
1992
2183
  * Everything here is pure; the fetch/cache lifecycle lives in
1993
2184
  * `ProviderKeyQuotaService`. Windows reuse the subscription `AllowanceWindow`
@@ -1995,7 +2186,7 @@ type BillingStatusReader = () => BillingDeliveryStatus;
1995
2186
  */
1996
2187
 
1997
2188
  /** Which quota adapter applies to a provider row (by resolved endpoint). */
1998
- type ProviderKeyQuotaAdapter = 'zai' | 'minimax-token-plan' | 'umans' | 'synthetic';
2189
+ type ProviderKeyQuotaAdapter = 'zai' | 'minimax-token-plan' | 'umans' | 'synthetic' | 'cline-pass';
1999
2190
 
2000
2191
  /**
2001
2192
  * ProviderKeyQuotaService — read-through quota cache for BYO provider-row keys.
@@ -2797,6 +2988,12 @@ interface AccountAllowanceAdminReader {
2797
2988
  refreshKimi?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
2798
2989
  /** Optional: OpenCodeGo `/v1/usage` refresh (absent on older daemons). */
2799
2990
  refreshOpenCodeGo?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
2991
+ /** Optional: Grok CLI-billing refresh (absent on older daemons). */
2992
+ refreshGrok?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
2993
+ /** Optional: Copilot user-quota refresh (absent on older daemons). */
2994
+ refreshCopilot?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
2995
+ /** Optional: Gemini Code-Assist quota refresh (absent on older daemons). */
2996
+ refreshGemini?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
2800
2997
  removeAccountSnapshot?(providerId: SubscriptionProviderId, accountId: string): void;
2801
2998
  removeProviderSnapshots?(providerId: SubscriptionProviderId): void;
2802
2999
  getSchedulingStatus?(): AccountAllowanceSchedulingStatus;
@@ -2946,6 +3143,16 @@ interface AdminApiDeps {
2946
3143
  * shape as codex; one sign-in at a time. Wired in `bootstrap.ts`.
2947
3144
  */
2948
3145
  readonly kimiSessions: CodexOAuthSessionStore;
3146
+ /**
3147
+ * Grok interactive-OAuth flow store (device code). Same token-free polled
3148
+ * shape as codex; one sign-in at a time. Wired in `bootstrap.ts`.
3149
+ */
3150
+ readonly grokSessions: CodexOAuthSessionStore;
3151
+ /**
3152
+ * Copilot interactive-OAuth flow store (device code). Same token-free
3153
+ * polled shape; one sign-in at a time. Wired in `bootstrap.ts`.
3154
+ */
3155
+ readonly copilotSessions: CodexOAuthSessionStore;
2949
3156
  /**
2950
3157
  * Codex loopback listener (app-parity-2 child 5) — defaults to `awaitLoopbackCode`
2951
3158
  * (binds 127.0.0.1:1455) in `bootstrap.ts`; tests inject a mock so no real port
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ 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
8
  import { AccountAllowanceSnapshot, AllowanceWindow } from '@omnicross/contracts/account-allowance-types';
9
- import { ClaudeTokenConfig, CodexTokenConfig, GeminiTokenConfig, KimiTokenConfig, AccountTokensConfig, ProxyConfig, SubscriptionAccountSanitized, AccountClientIdentity, SubscriptionAccountEntry } from '@omnicross/contracts/account-tokens-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 —
@@ -666,7 +678,7 @@ type AtomicFileReplace = (targetPath: string, contents: string) => void;
666
678
  */
667
679
 
668
680
  /** Provider id → owned contract field names. */
669
- type DaemonProvider = 'claude' | 'codex' | 'gemini' | 'opencodego' | 'kimi';
681
+ type DaemonProvider = 'claude' | 'codex' | 'gemini' | 'opencodego' | 'kimi' | 'grok' | 'copilot';
670
682
  interface AccountMetadataPatch {
671
683
  label?: string;
672
684
  enabled?: boolean;
@@ -795,7 +807,7 @@ type ExternalCliReader = (provider: ExternalCliProvider) => ExternalCliCredentia
795
807
  * shapes), keyed by `SubscriptionProviderId` the daemon admin layer validates
796
808
  * the wire body to one of these before calling the writer.
797
809
  */
798
- type SubscriptionTokenBlock = ClaudeTokenConfig | CodexTokenConfig | GeminiTokenConfig | OpenCodeGoTokenConfig | KimiTokenConfig;
810
+ type SubscriptionTokenBlock = ClaudeTokenConfig | CodexTokenConfig | GeminiTokenConfig | OpenCodeGoTokenConfig | KimiTokenConfig | GrokTokenConfig | CopilotTokenConfig;
799
811
  declare class JsonSubscriptionCredentialStore implements SubscriptionCredentialStore {
800
812
  private readonly tokensPath;
801
813
  private readonly box;
@@ -907,13 +919,28 @@ declare class JsonSubscriptionCredentialStore implements SubscriptionCredentialS
907
919
  * `false` when no refresh_token.
908
920
  */
909
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>;
910
937
  /**
911
938
  * Refresh a SPECIFIC managed account by id (background scheduler sweep and
912
939
  * account-pool resolution). It uses only that account's stored refresh
913
940
  * token. Coalesced per `provider:id`; on failure flags ONLY that account
914
941
  * `expired`.
915
942
  */
916
- refreshAccountById(provider: 'claude' | 'codex' | 'gemini' | 'kimi', id: string): Promise<boolean>;
943
+ refreshAccountById(provider: 'claude' | 'codex' | 'gemini' | 'kimi' | 'grok' | 'copilot', id: string): Promise<boolean>;
917
944
  /**
918
945
  * Resolve a SPECIFIC account's access token by id (design D6). Mirrors each
919
946
  * provider's ACTIVE-getter policy, keyed by id: claude returns the stored token
@@ -1510,6 +1537,155 @@ declare class KimiAllowanceCollector {
1510
1537
  private unsupportedSnapshot;
1511
1538
  }
1512
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
+ * GeminiAllowanceCollector — gemini subscription quota via the Cloud Code
1641
+ * Assist internal user-quota RPC.
1642
+ *
1643
+ * Probes `POST ${cloudcode-pa}/v1internal:retrieveUserQuota` per gemini
1644
+ * account (Bearer access token + the GeminiCLI masquerade pair the inference
1645
+ * path also carries), parsing `buckets[]` into one per-model window:
1646
+ * `remainingFraction` (0–1) → used percent, `resetTime` → resetsAt. The
1647
+ * Code Assist project id comes from the SHARED `GeminiCodeAssistProjectResolver`
1648
+ * (the same instance the dispatch seam uses, so the handshake runs at most
1649
+ * once per token across inference + quota); a handshake hard failure degrades
1650
+ * to a project-less probe (the valid free-tier envelope), mirroring the
1651
+ * reference implementation's tolerant behavior.
1652
+ *
1653
+ * Mirrors the sibling collectors' cache contract: 5-minute cache, per-account
1654
+ * in-flight merging, one 401/403→refresh→retry. Display-only — the scheduling
1655
+ * whitelist deliberately excludes gemini (per-model fraction buckets do not
1656
+ * fit the worst-window pause/demote rule).
1657
+ *
1658
+ * @module @omnicross/daemon/allowance/GeminiAllowanceCollector
1659
+ */
1660
+
1661
+ interface GeminiAllowanceCredentialReader {
1662
+ getAccessTokenForAccount(providerId: 'gemini', accountId: string): Promise<string | null>;
1663
+ refreshAccountToken(providerId: 'gemini', accountId: string): Promise<boolean>;
1664
+ }
1665
+ type GeminiAllowanceFetch = (url: string, init: RequestInit, accountId: string) => Promise<Response>;
1666
+ interface GeminiAllowanceCollectOptions {
1667
+ force?: boolean;
1668
+ refreshAheadMs?: number;
1669
+ }
1670
+ declare class GeminiAllowanceCollector {
1671
+ private readonly credentials;
1672
+ private readonly store;
1673
+ private readonly fetchImpl;
1674
+ private readonly now;
1675
+ private readonly projectResolver;
1676
+ private readonly inFlight;
1677
+ constructor(credentials: GeminiAllowanceCredentialReader, store?: AccountAllowanceStore, fetchImpl?: GeminiAllowanceFetch, now?: () => number, projectResolver?: {
1678
+ resolveProject(accessToken: string): Promise<string | undefined>;
1679
+ });
1680
+ collectMany(accounts: readonly SubscriptionAccountEntry<GeminiTokenConfig>[], options?: GeminiAllowanceCollectOptions): Promise<AccountAllowanceSnapshot[]>;
1681
+ collect(account: SubscriptionAccountEntry<GeminiTokenConfig>, options?: GeminiAllowanceCollectOptions): Promise<AccountAllowanceSnapshot>;
1682
+ private isCacheValid;
1683
+ private fetchAccount;
1684
+ private request;
1685
+ private failureSnapshot;
1686
+ private unsupportedSnapshot;
1687
+ }
1688
+
1513
1689
  /**
1514
1690
  * OpenCodeGo usage collector.
1515
1691
  *
@@ -1557,8 +1733,8 @@ declare class OpenCodeGoAllowanceCollector {
1557
1733
  * literals — extending them all would make the overloads conflict.
1558
1734
  */
1559
1735
  interface AccountAllowanceCredentialReader {
1560
- getAccessTokenForAccount(providerId: 'claude' | 'codex' | 'kimi' | 'opencodego', accountId: string): Promise<string | null>;
1561
- refreshAccountToken(providerId: 'claude' | 'codex' | 'kimi', accountId: string): Promise<boolean>;
1736
+ getAccessTokenForAccount(providerId: 'claude' | 'codex' | 'kimi' | 'opencodego' | 'grok' | 'copilot' | 'gemini', accountId: string): Promise<string | null>;
1737
+ refreshAccountToken(providerId: 'claude' | 'codex' | 'kimi' | 'grok' | 'copilot' | 'gemini', accountId: string): Promise<boolean>;
1562
1738
  getFullConfig(): Promise<AccountTokensConfig>;
1563
1739
  }
1564
1740
  interface AccountAllowanceFilter {
@@ -1576,8 +1752,11 @@ declare class AccountAllowanceService {
1576
1752
  readonly claudeCollector: ClaudeAllowanceCollector;
1577
1753
  readonly codexCollector: CodexAllowanceCollector;
1578
1754
  readonly kimiCollector: KimiAllowanceCollector;
1755
+ readonly grokCollector: GrokAllowanceCollector;
1756
+ readonly copilotCollector: CopilotAllowanceCollector;
1579
1757
  readonly opencodegoCollector: OpenCodeGoAllowanceCollector;
1580
- constructor(credentials: AccountAllowanceCredentialReader, store?: AccountAllowanceStore, collector?: ClaudeAllowanceCollector, codexCollector?: CodexAllowanceCollector, kimiCollector?: KimiAllowanceCollector, opencodegoCollector?: OpenCodeGoAllowanceCollector, now?: () => number);
1758
+ readonly geminiCollector: GeminiAllowanceCollector;
1759
+ constructor(credentials: AccountAllowanceCredentialReader, store?: AccountAllowanceStore, collector?: ClaudeAllowanceCollector, codexCollector?: CodexAllowanceCollector, kimiCollector?: KimiAllowanceCollector, opencodegoCollector?: OpenCodeGoAllowanceCollector, grokCollector?: GrokAllowanceCollector, copilotCollector?: CopilotAllowanceCollector, geminiCollector?: GeminiAllowanceCollector, now?: () => number);
1581
1760
  /**
1582
1761
  * Read all/filtered snapshots. Claude's and Codex's five-minute caches are
1583
1762
  * refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
@@ -1597,6 +1776,12 @@ declare class AccountAllowanceService {
1597
1776
  refreshOpenCodeGo(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
1598
1777
  /** Force-refresh Kimi usage (`/coding/v1/usages`) for one/all accounts. */
1599
1778
  refreshKimi(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
1779
+ /** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
1780
+ refreshCopilot(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
1781
+ /** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
1782
+ refreshGrok(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
1783
+ /** Force-refresh Gemini usage (Code Assist retrieveUserQuota) for one/all accounts. */
1784
+ refreshGemini(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
1600
1785
  /**
1601
1786
  * Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
1602
1787
  * collectors preserve their cache + per-account in-flight coalescing; a tick
@@ -1988,6 +2173,12 @@ type BillingStatusReader = () => BillingDeliveryStatus;
1988
2173
  * .status_code === 0` is the real success gate. `model_remains[]` buckets
1989
2174
  * each carry a rolling interval + weekly window as REMAINING percent
1990
2175
  * (0-100); the `"general"` bucket is the plan-wide shared quota.
2176
+ * - Cline Pass:
2177
+ * GET {origin}/api/v1/users/me/plan/usage-limits
2178
+ * `Authorization: Bearer <key>` PLUS the Cline client-identity header set
2179
+ * (the row's `extraHeaders` — the gateway 403s without the full mirror).
2180
+ * `limits[]` rows carry `{type: five_hour|weekly|monthly, percentUsed,
2181
+ * resetsAt}` — pure percentage windows, no absolute meters.
1991
2182
  *
1992
2183
  * Everything here is pure; the fetch/cache lifecycle lives in
1993
2184
  * `ProviderKeyQuotaService`. Windows reuse the subscription `AllowanceWindow`
@@ -1995,7 +2186,7 @@ type BillingStatusReader = () => BillingDeliveryStatus;
1995
2186
  */
1996
2187
 
1997
2188
  /** Which quota adapter applies to a provider row (by resolved endpoint). */
1998
- type ProviderKeyQuotaAdapter = 'zai' | 'minimax-token-plan' | 'umans' | 'synthetic';
2189
+ type ProviderKeyQuotaAdapter = 'zai' | 'minimax-token-plan' | 'umans' | 'synthetic' | 'cline-pass';
1999
2190
 
2000
2191
  /**
2001
2192
  * ProviderKeyQuotaService — read-through quota cache for BYO provider-row keys.
@@ -2797,6 +2988,12 @@ interface AccountAllowanceAdminReader {
2797
2988
  refreshKimi?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
2798
2989
  /** Optional: OpenCodeGo `/v1/usage` refresh (absent on older daemons). */
2799
2990
  refreshOpenCodeGo?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
2991
+ /** Optional: Grok CLI-billing refresh (absent on older daemons). */
2992
+ refreshGrok?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
2993
+ /** Optional: Copilot user-quota refresh (absent on older daemons). */
2994
+ refreshCopilot?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
2995
+ /** Optional: Gemini Code-Assist quota refresh (absent on older daemons). */
2996
+ refreshGemini?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
2800
2997
  removeAccountSnapshot?(providerId: SubscriptionProviderId, accountId: string): void;
2801
2998
  removeProviderSnapshots?(providerId: SubscriptionProviderId): void;
2802
2999
  getSchedulingStatus?(): AccountAllowanceSchedulingStatus;
@@ -2946,6 +3143,16 @@ interface AdminApiDeps {
2946
3143
  * shape as codex; one sign-in at a time. Wired in `bootstrap.ts`.
2947
3144
  */
2948
3145
  readonly kimiSessions: CodexOAuthSessionStore;
3146
+ /**
3147
+ * Grok interactive-OAuth flow store (device code). Same token-free polled
3148
+ * shape as codex; one sign-in at a time. Wired in `bootstrap.ts`.
3149
+ */
3150
+ readonly grokSessions: CodexOAuthSessionStore;
3151
+ /**
3152
+ * Copilot interactive-OAuth flow store (device code). Same token-free
3153
+ * polled shape; one sign-in at a time. Wired in `bootstrap.ts`.
3154
+ */
3155
+ readonly copilotSessions: CodexOAuthSessionStore;
2949
3156
  /**
2950
3157
  * Codex loopback listener (app-parity-2 child 5) — defaults to `awaitLoopbackCode`
2951
3158
  * (binds 127.0.0.1:1455) in `bootstrap.ts`; tests inject a mock so no real port